wufan commited on
Commit
e2f8fd2
1 Parent(s): d5d93ad
Files changed (47) hide show
  1. Dockerfile +22 -0
  2. LICENSE.md +202 -0
  3. README.md +103 -10
  4. app.py +351 -0
  5. assets/demo/cases.png +0 -0
  6. assets/demo/demo.png +0 -0
  7. assets/example/input_example.json +17 -0
  8. assets/example/table_example.json +10 -0
  9. evaluation.py +290 -0
  10. modules/latex2bbox_color.py +215 -0
  11. modules/latex_processor.py +536 -0
  12. modules/latex_render_percentage.py +116 -0
  13. modules/tokenize_latex/preprocess_formula.js +387 -0
  14. modules/tokenize_latex/preprocess_tabular.js +395 -0
  15. modules/tokenize_latex/third_party/README.md +1 -0
  16. modules/tokenize_latex/third_party/katex/LICENSE.txt +27 -0
  17. modules/tokenize_latex/third_party/katex/README.md +68 -0
  18. modules/tokenize_latex/third_party/katex/cli.js +32 -0
  19. modules/tokenize_latex/third_party/katex/katex.js +74 -0
  20. modules/tokenize_latex/third_party/katex/package.json +108 -0
  21. modules/tokenize_latex/third_party/katex/src/Lexer.js +162 -0
  22. modules/tokenize_latex/third_party/katex/src/Options.js +189 -0
  23. modules/tokenize_latex/third_party/katex/src/ParseError.js +40 -0
  24. modules/tokenize_latex/third_party/katex/src/Parser.js +798 -0
  25. modules/tokenize_latex/third_party/katex/src/Settings.js +28 -0
  26. modules/tokenize_latex/third_party/katex/src/Style.js +126 -0
  27. modules/tokenize_latex/third_party/katex/src/buildCommon.js +450 -0
  28. modules/tokenize_latex/third_party/katex/src/buildHTML.js +1402 -0
  29. modules/tokenize_latex/third_party/katex/src/buildMathML.js +533 -0
  30. modules/tokenize_latex/third_party/katex/src/buildTree.js +40 -0
  31. modules/tokenize_latex/third_party/katex/src/delimiter.js +542 -0
  32. modules/tokenize_latex/third_party/katex/src/domTree.js +269 -0
  33. modules/tokenize_latex/third_party/katex/src/environments.js +295 -0
  34. modules/tokenize_latex/third_party/katex/src/fontMetrics.js +147 -0
  35. modules/tokenize_latex/third_party/katex/src/fontMetricsData.js +1752 -0
  36. modules/tokenize_latex/third_party/katex/src/functions.js +585 -0
  37. modules/tokenize_latex/third_party/katex/src/mathMLTree.js +102 -0
  38. modules/tokenize_latex/third_party/katex/src/parseData.js +13 -0
  39. modules/tokenize_latex/third_party/katex/src/parseTree.js +17 -0
  40. modules/tokenize_latex/third_party/katex/src/symbols.js +687 -0
  41. modules/tokenize_latex/third_party/katex/src/utils.js +106 -0
  42. modules/tokenize_latex/third_party/match-at/README.md +1 -0
  43. modules/tokenize_latex/third_party/match-at/lib/matchAt.js +42 -0
  44. modules/tokenize_latex/third_party/match-at/package.json +54 -0
  45. modules/tokenize_latex/tokenize_latex.py +98 -0
  46. modules/visual_matcher.py +191 -0
  47. requriements.txt +5 -0
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.8
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN useradd -m -u 1000 user
8
+
9
+ # Switch to the "user" user
10
+ USER user
11
+
12
+ # Set home to the user's home directory
13
+ ENV HOME=/home/user \
14
+ PATH=/home/user/.local/bin:$PATH
15
+
16
+ # Set the working directory to the user's home directory
17
+ WORKDIR $HOME/app
18
+
19
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
20
+ COPY --chown=user . $HOME/app
21
+
22
+ CMD ["python", "app.py"]
LICENSE.md ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
README.md CHANGED
@@ -1,10 +1,103 @@
1
- ---
2
- title: CDM Demo
3
- emoji: 🔥
4
- colorFrom: indigo
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Character Detection Matching (CDM)
2
+
3
+ ## Demo
4
+
5
+ CDM: A Reliable Metric for Fair and Accurate Formula Recognition
6
+
7
+ ![demo](assets/demo/demo.png)
8
+
9
+ Compair with BLEU and ExpRate:
10
+
11
+ ![demo](assets/demo/cases.png)
12
+
13
+ ## Installation Guide
14
+
15
+ Nodejs, imagemagic, pdflatex are requried packages when render pdf files and convert them to images, here are installation guides.
16
+
17
+ ### install nodejs
18
+
19
+ download the package from [offical website](https://registry.npmmirror.com/binary.html?path=node/latest-v16.x/), and then run these commands.
20
+ ```
21
+ tar -xvf node-v16.13.1-linux-x64.tar.gz
22
+
23
+ mv node-v16.13.1-linux-x64/* /usr/local/nodejs/
24
+
25
+ ln -s /usr/local/nodejs/bin/node /usr/local/bin
26
+
27
+ ln -s /usr/local/nodejs/bin/npm /usr/local/bin
28
+
29
+ node -v
30
+ ```
31
+
32
+ ### install imagemagic
33
+
34
+ the version of imagemagic installed by `apt-gt` usually be 6.x, so we also install it from source code.
35
+ ```
36
+ git clone https://github.com/ImageMagick/ImageMagick.git ImageMagick-7.1.1
37
+
38
+ cd ImageMagick-7.1.1
39
+
40
+ ./configure
41
+
42
+ make
43
+
44
+ sudo make install
45
+
46
+ sudo ldconfig /usr/local/lib
47
+
48
+ convert --version
49
+ ```
50
+
51
+ ### install latexpdf
52
+
53
+ ```
54
+ apt-get update
55
+
56
+ sudo apt-get install texlive-full
57
+ ```
58
+
59
+ ### install python requriements
60
+
61
+ ```
62
+ conda create -n CDM python=3.10 # optional
63
+
64
+ git clone xxx
65
+
66
+ cd xxx
67
+
68
+ pip install -r requirements.txt
69
+ ```
70
+
71
+
72
+ ## Usage
73
+
74
+ Should the installation go well, you may now enjoy the evaluation pipeline.
75
+
76
+ ### 1. batch evaluation
77
+
78
+ ```
79
+ python evaluation.py -i {path_to_your_input_json}
80
+ ```
81
+
82
+ the format of input json like this:
83
+ ```
84
+ [
85
+ {
86
+ "img_id": "case_1", # optional key
87
+ "gt": "y = 2z + 3x",
88
+ "pred": "y = 2x + 3z"
89
+ },
90
+ {
91
+ "img_id": "case_2",
92
+ "gt": "y = x^2 + 1",
93
+ "pred": "y = x^2 + 1"
94
+ },
95
+ ...
96
+ ]
97
+ ```
98
+
99
+ ### 2. run a gradio demo
100
+
101
+ ```
102
+ python app.py
103
+ ```
app.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ import re
4
+ import json
5
+ import time
6
+ import shutil
7
+ import numpy as np
8
+ import gradio as gr
9
+ from datetime import datetime
10
+ from multiprocessing import Pool
11
+ from multiprocessing.dummy import Pool as ThreadPool
12
+ from PIL import Image, ImageDraw
13
+ from skimage.measure import ransac
14
+ import matplotlib.pyplot as plt
15
+
16
+ from modules.latex2bbox_color import latex2bbox_color
17
+ from modules.tokenize_latex.tokenize_latex import tokenize_latex
18
+ from modules.visual_matcher import HungarianMatcher, SimpleAffineTransform
19
+
20
+
21
+ DATA_ROOT = "output"
22
+
23
+ def gen_color_list(num=10, gap=15):
24
+ num += 1
25
+ single_num = 255 // gap + 1
26
+ max_num = single_num ** 3
27
+ num = min(num, max_num)
28
+ color_list = []
29
+ for idx in range(num):
30
+ R = idx // single_num**2
31
+ GB = idx % single_num**2
32
+ G = GB // single_num
33
+ B = GB % single_num
34
+
35
+ color_list.append((R*gap, G*gap, B*gap))
36
+ return color_list[1:]
37
+
38
+ def process_latex(groundtruths, predictions, user_id="test"):
39
+ data_root = DATA_ROOT
40
+ temp_dir = os.path.join(data_root, "temp_dir")
41
+
42
+ data_root = os.path.join(data_root, user_id)
43
+ output_dir_info = {}
44
+ input_args = []
45
+ for subset, latex_list in zip(['gt', 'pred'], [groundtruths, predictions]):
46
+ sub_temp_dir = os.path.join(temp_dir, f"{user_id}_{subset}")
47
+ os.makedirs(sub_temp_dir, exist_ok=True)
48
+
49
+ output_path = os.path.join(data_root, subset)
50
+ output_dir_info[output_path] = []
51
+
52
+ os.makedirs(os.path.join(output_path, 'bbox'), exist_ok=True)
53
+ os.makedirs(os.path.join(output_path, 'vis'), exist_ok=True)
54
+
55
+ total_color_list = gen_color_list(num=5800)
56
+
57
+ for idx, latex in enumerate(latex_list):
58
+ basename = f"sample_{idx}"
59
+ input_arg = latex, basename, output_path, sub_temp_dir, total_color_list
60
+ a = time.time()
61
+ latex2bbox_color(input_arg)
62
+ b = time.time()
63
+
64
+ for subset in ['gt', 'pred']:
65
+ shutil.rmtree(os.path.join(temp_dir, f"{user_id}_{subset}"))
66
+
67
+
68
+ def update_inliers(ori_inliers, sub_inliers):
69
+ inliers = np.copy(ori_inliers)
70
+ sub_idx = -1
71
+ for idx in range(len(ori_inliers)):
72
+ if ori_inliers[idx] == False:
73
+ sub_idx += 1
74
+ if sub_inliers[sub_idx] == True:
75
+ inliers[idx] = True
76
+ return inliers
77
+
78
+ def reshape_inliers(ori_inliers, sub_inliers):
79
+ inliers = np.copy(ori_inliers)
80
+ sub_idx = -1
81
+ for idx in range(len(ori_inliers)):
82
+ if ori_inliers[idx] == False:
83
+ sub_idx += 1
84
+ if sub_inliers[sub_idx] == True:
85
+ inliers[idx] = True
86
+ else:
87
+ inliers[idx] = False
88
+ return inliers
89
+
90
+
91
+ def evaluation(user_id="test"):
92
+ data_root = DATA_ROOT
93
+ data_root = os.path.join(data_root, user_id)
94
+ gt_box_dir = os.path.join(data_root, "gt")
95
+ pred_box_dir = os.path.join(data_root, "pred")
96
+ match_vis_dir = os.path.join(data_root, "vis_match")
97
+ os.makedirs(match_vis_dir, exist_ok=True)
98
+
99
+ max_iter = 3
100
+ min_samples = 3
101
+ residual_threshold = 25
102
+ max_trials = 50
103
+
104
+ metrics_per_img = {}
105
+ gt_basename_list = [item.split(".")[0] for item in os.listdir(os.path.join(gt_box_dir, 'bbox'))]
106
+ for basename in gt_basename_list:
107
+ gt_valid, pred_valid = True, True
108
+ if not os.path.exists(os.path.join(gt_box_dir, 'bbox', basename+".jsonl")):
109
+ gt_valid = False
110
+ else:
111
+ with open(os.path.join(gt_box_dir, 'bbox', basename+".jsonl"), 'r') as f:
112
+ box_gt = []
113
+ for line in f:
114
+ info = json.loads(line)
115
+ if info['bbox']:
116
+ box_gt.append(info)
117
+ if not box_gt:
118
+ gt_valid = False
119
+ if not gt_valid:
120
+ continue
121
+
122
+ if not os.path.exists(os.path.join(pred_box_dir, 'bbox', basename+".jsonl")):
123
+ pred_valid = False
124
+ else:
125
+ with open(os.path.join(pred_box_dir, 'bbox', basename+".jsonl"), 'r') as f:
126
+ box_pred = []
127
+ for line in f:
128
+ info = json.loads(line)
129
+ if info['bbox']:
130
+ box_pred.append(info)
131
+ if not box_pred:
132
+ pred_valid = False
133
+ if not pred_valid:
134
+ metrics_per_img[basename] = {
135
+ "recall": 0,
136
+ "precision": 0,
137
+ "F1_score": 0,
138
+ }
139
+ continue
140
+ gt_img_path = os.path.join(gt_box_dir, 'vis', basename+"_base.png")
141
+ pred_img_path = os.path.join(pred_box_dir, 'vis', basename+"_base.png")
142
+
143
+ img_gt = Image.open(gt_img_path)
144
+ img_pred = Image.open(pred_img_path)
145
+
146
+ matcher = HungarianMatcher()
147
+ matched_idxes = matcher(box_gt, box_pred, img_gt.size, img_pred.size)
148
+ src = []
149
+ dst = []
150
+ for (idx1, idx2) in matched_idxes:
151
+ x1min, y1min, x1max, y1max = box_gt[idx1]['bbox']
152
+ x2min, y2min, x2max, y2max = box_pred[idx2]['bbox']
153
+ x1_c, y1_c = float((x1min+x1max)/2), float((y1min+y1max)/2)
154
+ x2_c, y2_c = float((x2min+x2max)/2), float((y2min+y2max)/2)
155
+ src.append([y1_c, x1_c])
156
+ dst.append([y2_c, x2_c])
157
+
158
+ src = np.array(src)
159
+ dst = np.array(dst)
160
+ if src.shape[0] <= min_samples:
161
+ inliers = np.array([True for _ in matched_idxes])
162
+ else:
163
+ inliers = np.array([False for _ in matched_idxes])
164
+ for i in range(max_iter):
165
+ if src[inliers==False].shape[0] <= min_samples:
166
+ break
167
+ model, inliers_1 = ransac((src[inliers==False], dst[inliers==False]), SimpleAffineTransform, min_samples=min_samples, residual_threshold=residual_threshold, max_trials=max_trials)
168
+ if inliers_1 is not None and inliers_1.any():
169
+ inliers = update_inliers(inliers, inliers_1)
170
+ else:
171
+ break
172
+ if len(inliers[inliers==True]) >= len(matched_idxes):
173
+ break
174
+
175
+ for idx, (a,b) in enumerate(matched_idxes):
176
+ if inliers[idx] == True and matcher.cost['token'][a, b] == 1:
177
+ inliers[idx] = False
178
+
179
+ final_match_num = len(inliers[inliers==True])
180
+ recall = round(final_match_num/(len(box_gt)), 3)
181
+ precision = round(final_match_num/(len(box_pred)), 3)
182
+ F1_score = round(2*final_match_num/(len(box_gt)+len(box_pred)), 3)
183
+ metrics_per_img[basename] = {
184
+ "recall": recall,
185
+ "precision": precision,
186
+ "F1_score": F1_score,
187
+ }
188
+
189
+ if True:
190
+ gap = 5
191
+ W1, H1 = img_gt.size
192
+ W2, H2 = img_pred.size
193
+ H = H1 + H2 + gap
194
+ W = max(W1, W2)
195
+
196
+ vis_img = Image.new('RGB', (W, H), (255, 255, 255))
197
+ vis_img.paste(img_gt, (0, 0))
198
+ vis_img.paste(Image.new('RGB', (W, gap), (0, 150, 200)), (0, H1))
199
+ vis_img.paste(img_pred, (0, H1+gap))
200
+
201
+ match_img = vis_img.copy()
202
+ match_draw = ImageDraw.Draw(match_img)
203
+
204
+ gt_matched_idx = {
205
+ a: flag
206
+ for (a,b), flag in
207
+ zip(matched_idxes, inliers)
208
+ }
209
+ pred_matched_idx = {
210
+ b: flag
211
+ for (a,b), flag in
212
+ zip(matched_idxes, inliers)
213
+ }
214
+
215
+ for idx, box in enumerate(box_gt):
216
+ if idx in gt_matched_idx and gt_matched_idx[idx]==True:
217
+ color = "green"
218
+ else:
219
+ color = "red"
220
+ x_min, y_min, x_max, y_max = box['bbox']
221
+ match_draw.rectangle([x_min-1, y_min-1, x_max+1, y_max+1], fill=None, outline=color, width=2)
222
+
223
+ for idx, box in enumerate(box_pred):
224
+ if idx in pred_matched_idx and pred_matched_idx[idx]==True:
225
+ color = "green"
226
+ else:
227
+ color = "red"
228
+ x_min, y_min, x_max, y_max = box['bbox']
229
+ match_draw.rectangle([x_min-1, y_min-1+H1+gap, x_max+1, y_max+1+H1+gap], fill=None, outline=color, width=2)
230
+
231
+ vis_img.save(os.path.join(match_vis_dir, basename+"_base.png"))
232
+ if W < 500:
233
+ padding = (500 - W)//2 + 1
234
+ reshape_match_img = Image.new('RGB', (500, H), (255, 255, 255))
235
+ reshape_match_img.paste(match_img, (padding, 0))
236
+ reshape_match_img.paste(Image.new('RGB', (500, gap), (0, 150, 200)), (0, H1))
237
+ reshape_match_img.save(os.path.join(match_vis_dir, basename+".png"))
238
+ else:
239
+ match_img.save(os.path.join(match_vis_dir, basename+".png"))
240
+
241
+ acc_list = [val['F1_score'] for _, val in metrics_per_img.items()]
242
+ metrics_res = {
243
+ "mean_score": round(np.mean(acc_list), 3),
244
+ "details": metrics_per_img
245
+ }
246
+ metric_res_path = os.path.join(data_root, "metrics_res.json")
247
+ with open(metric_res_path, "w") as f:
248
+ f.write(json.dumps(metrics_res, indent=2))
249
+ return metrics_res, metric_res_path, match_vis_dir
250
+
251
+ def calculate_metric_single(groundtruth, prediction):
252
+ user_id = datetime.now().strftime('%Y%m%d-%H%M%S')
253
+ process_latex([groundtruth], [prediction], user_id)
254
+ metrics_res, metric_res_path, match_vis_dir = evaluation(user_id)
255
+ basename = "sample_0"
256
+ image_path = os.path.join(match_vis_dir, basename+".png")
257
+ sample = metrics_res["details"][basename]
258
+ score = sample['F1_score']
259
+ recall = sample['recall']
260
+ precision = sample['precision']
261
+ return score, recall, precision, gr.Image(image_path)
262
+
263
+ def calculate_metric_batch(json_input):
264
+ user_id = datetime.now().strftime('%Y%m%d-%H%M%S')
265
+ with open(json_input.name, "r") as f:
266
+ input_data = json.load(f)
267
+ groundtruths = []
268
+ predictions = []
269
+ for item in input_data:
270
+ groundtruths.append(item['gt'])
271
+ predictions.append(item['pred'])
272
+ process_latex(groundtruths, predictions, user_id)
273
+ metrics_res, metric_res_path, match_vis_dir = evaluation(user_id)
274
+ return metric_res_path
275
+
276
+ def gradio_reset_single():
277
+ return gr.update(value=None, placeholder='type gt latex code here'), gr.update(value=None, placeholder='type pred latex code here'), \
278
+ gr.update(value=None), gr.update(value=None), gr.update(value=None), gr.update(value=None)
279
+
280
+ def gradio_reset_batch():
281
+ return gr.update(value=None), gr.update(value=None)
282
+
283
+ def select_example1():
284
+ gt = "y = 2x + 3z"
285
+ pred = "y = 2z + 3x"
286
+ return gr.update(value=gt, placeholder='type gt latex code here'), gr.update(value=pred, placeholder='type pred latex code here')
287
+
288
+ def select_example2():
289
+ gt = "r = \\frac { \\alpha } { \\beta } \\vert \\sin \\beta \\left( \\sigma _ { 1 } \\pm \\sigma _ { 2 } \\right) \\vert"
290
+ pred = "r={\\frac{\\alpha}{\\beta}}|\\sin\\beta\\left(\\sigma_{2}+\\sigma_{1}\\right)|"
291
+ return gr.update(value=gt, placeholder='type gt latex code here'), gr.update(value=pred, placeholder='type pred latex code here')
292
+
293
+ def select_example3():
294
+ gt = "\\begin{array} { r l r } & { } & { \\mathbf { J } _ { L } = \\left( \\begin{array} { c c } { 0 } & { 0 } \\\\ { v _ { n } } & { 0 } \\end{array} \\right) , ~ \\mathbf { J } _ { R } = \\left( \\begin{array} { c c } { u _ { n - 1 } } & { 0 } \\\\ { 0 } & { 0 } \\end{array} \\right) , ~ } \\\\ & { } & {\\mathbf { K } = \\left( \\begin{array} { c c } { V _ { n - 1 } } & { u _ { n } } \\\\ { v _ { n - 1 } } & { V _ { n } } \\end{array} \\right) , } \\end{array}"
295
+ pred = "\\mathbf{J}_{U}={\\left(\\begin{array}{l l}{0}&{0}\\\\ {v_{n}}&{0}\\end{array}\\right)}\\,,\\ \\mathbf{J}_{R}={\\left(\\begin{array}{l l}{u_{n-1}}&{0}\\\\ {0}&{0}\\end{array}\\right)}\\,,\\mathbf{K}={\\left(\\begin{array}{l l}{V_{n-1}}&{u_{n}}\\\\ {v_{n-1}}&{V_{n}}\\end{array}\\right)}\\,,"
296
+ return gr.update(value=gt, placeholder='type gt latex code here'), gr.update(value=pred, placeholder='type pred latex code here')
297
+
298
+
299
+
300
+ if __name__ == "__main__":
301
+ title = """<h1 align="center">Character Detection Matching (CDM)</h1>"""
302
+
303
+ with gr.Blocks() as demo:
304
+ gr.Markdown(title)
305
+
306
+ gr.Button(value="Quick Try: type latex code of gt and pred, get metrics and visualization.", interactive=False, variant="primary")
307
+
308
+ with gr.Row():
309
+ with gr.Column():
310
+ gt_input = gr.Textbox(label='gt', placeholder='type gt latex code here', interactive=True)
311
+ pred_input = gr.Textbox(label='pred', placeholder='type pred latex code here', interactive=True)
312
+ with gr.Row():
313
+ clear_single = gr.Button("Clear")
314
+ submit_single = gr.Button(value="Submit", interactive=True, variant="primary")
315
+ with gr.Accordion("Examples:"):
316
+ with gr.Row():
317
+ example1 = gr.Button("Example A(short)")
318
+ example2 = gr.Button("Example B(middle)")
319
+ example3 = gr.Button("Example C(long)")
320
+ with gr.Column():
321
+ with gr.Row():
322
+ score_output = gr.Number(label="F1 Score", interactive=False)
323
+ recall_output = gr.Number(label="Recall", interactive=False)
324
+ recision_output = gr.Number(label="Precision", interactive=False)
325
+ gr.Button(value="Visualization (green bbox means correcttlly matched, red bbox means missed or wrong.)", interactive=False)
326
+ vis_output = gr.Image(label=" ", interactive=False)
327
+
328
+ example1.click(select_example1, inputs=None, outputs=[gt_input, pred_input])
329
+ example2.click(select_example2, inputs=None, outputs=[gt_input, pred_input])
330
+ example3.click(select_example3, inputs=None, outputs=[gt_input, pred_input])
331
+
332
+ clear_single.click(gradio_reset_single, inputs=None, outputs=[gt_input, pred_input, score_output, recall_output, recision_output, vis_output])
333
+ submit_single.click(calculate_metric_single, inputs=[gt_input, pred_input], outputs=[score_output, recall_output, recision_output, vis_output])
334
+
335
+
336
+ gr.Button(value="Batch Run: upload a json file and batch processing, this may take some times according to your latex amount and length.", interactive=False, variant="primary")
337
+
338
+ with gr.Row():
339
+ with gr.Column():
340
+ json_input = gr.File(label="Input Json", file_types=[".json"])
341
+ json_example = gr.File(label="Input Example", value="assets/example/input_example.json")
342
+ with gr.Row():
343
+ clear_batch = gr.Button("Clear")
344
+ submit_batch = gr.Button(value="Submit", interactive=True, variant="primary")
345
+
346
+ metric_output = gr.File(label="Output Metrics")
347
+
348
+ clear_batch.click(gradio_reset_batch, inputs=None, outputs=[json_input, metric_output])
349
+ submit_batch.click(calculate_metric_batch, inputs=[json_input], outputs=[metric_output])
350
+
351
+ demo.launch(share=True, server_name="0.0.0.0", server_port=10005, debug=True)
assets/demo/cases.png ADDED
assets/demo/demo.png ADDED
assets/example/input_example.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "img_id": "case_1",
4
+ "gt": "r = \\frac { \\alpha } { \\beta } \\vert \\sin \\beta \\left( \\sigma _ { 1 } \\pm \\sigma _ { 2 } \\right) \\vert",
5
+ "pred": "r={\\frac{\\alpha}{\\beta}}|\\sin\\beta\\left(\\sigma_{2}+\\sigma_{1}\\right)|"
6
+ },
7
+ {
8
+ "img_id": "case_2",
9
+ "gt": "y = 2z + 3x",
10
+ "pred": "y = 2x + 3z"
11
+ },
12
+ {
13
+ "img_id": "case_3",
14
+ "gt": "\\begin{array} { r l r } & { } & { \\mathbf { J } _ { L } = \\left( \\begin{array} { c c } { 0 } & { 0 } \\\\ { v _ { n } } & { 0 } \\end{array} \\right) , ~ \\mathbf { J } _ { R } = \\left( \\begin{array} { c c } { u _ { n - 1 } } & { 0 } \\\\ { 0 } & { 0 } \\end{array} \\right) , ~ } \\\\ & { } & { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ \\mathbf { K } = \\left( \\begin{array} { c c } { V _ { n - 1 } } & { u _ { n } } \\\\ { v _ { n - 1 } } & { V _ { n } } \\end{array} \\right) , } \\end{array}",
15
+ "pred": "\\mathbf{J}_{R}={\\left(\\begin{array}{l l}{0}&{0}\\\\ {v_{n}}&{0}\\end{array}\\right)}\\,,\\ \\mathbf{J}_{L}={\\left(\\begin{array}{l l}{u_{n-1}}&{0}\\\\ {0}&{0}\\end{array}\\right)}\\,,\\mathbf{K}={\\left(\\begin{array}{l l}{V_{n-1}}&{u_{n}}\\\\ {v_{n-1}}&{V_{n}}\\end{array}\\right)}\\,,"
16
+ }
17
+ ]
assets/example/table_example.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "pred": "\\begin{tabular}{cccc}Property&Value\\\\ \\hline$e^{min}_c$&0.069\\\\ $e^{min}_d$&0.074\\\\ $e^{max}_c$&0.39\\\\ $e^{max}_d$&0.365\\\\ $\\epsilon$&0.17\\\\ $i^{min}_c$&$16.0^\\circ$\\\\ $i^{min}_d$&$11.6^\\circ$\\\\ $i^{max}_c$&$20.4^\\circ$\\\\ $i^{max}_d$&$16.7^\\circ$\\\\ $\\Psi^{min}$&$27.6^\\circ$\\\\ $\\Psi^{max}$&$37.1^\\circ$\\\\ $\\beta/\\beta_{crit}$&1.075\\\\ \\end{tabular} ",
4
+ "gt": "\\begin{tabular}{cc}Property&Value\\\\\\hline$e^{min}_c$&0.069\\\\$e^{min}_d$&0.074\\\\$e^{max}_c$&0.39\\\\$e^{max}_d$&0.365\\\\$\\epsilon$&0.17\\\\$i^{min}_c$&$16.0^\\circ$\\\\$i^{min}_d$&$11.6^\\circ$\\\\$i^{max}_c$&$20.4^\\circ$\\\\$i^{max}_d$&$16.7^\\circ$\\\\$\\Psi^{min}$&$27.6^\\circ$\\\\$\\Psi^{max}$&$37.1^\\circ$\\\\$\\beta/\\beta_{crit}$&1.075\\\\ \\end{tabular}\n"
5
+ },
6
+ {
7
+ "pred": "\\begin{tabular}{l r r r r}\\hline Element&\\multicolumn{2}{c}{fully convective}&\\multicolumn{2}{c}{convective at$\\tau_{\\rm R}=3.2$}\\\\ &$\\log\\tau_{\\rm diff}$[yrs]&$\\dot{m}$[g\\,s$^{-1}$]&$\\log\\tau_{\\rm diff}$[yrs]&$\\dot{m}$[g\\,s$^{-1}$]\\\\ \\hline$12$Mg&$-0.46$&$1.7\\times10^8$&$-2.2$&$7.3\\times10^7$\\\\ $14$Si&$-0.36$&$\\leq2.5\\times10^8$&$-2.5$&$\\leq2.9\\times10^8$\\\\ $20$Ca&$-0.37$&$7.2\\times10^6$&$-2.3$&$5.0\\times10^6$\\\\ \\hline\\end{tabular} ",
8
+ "gt": "\\begin{tabular}{lrrrr}\\hline\nElement&\\multicolumn{2}{c}{fully convective}&\\multicolumn{2}{c}{convective at$\\tau_\\mathrm{R}=3.2$}\\\\ &$\\log\\tau_\\mathrm{diff}$[yrs]&$\\dot m~[\\mathrm{g\\,s^{-1}}]$&$\\log\\tau_\\mathrm{diff}$[yrs]&$\\dot m~[\\mathrm{g\\,s^{-1}}]$\\\\\\hline12Mg&$-0.46$&$1.7\\times10^8$&$-2.2$&$7.3\\times10^7$\\\\14Si&$-0.36$&$\\leq2.5\\times10^8$&$-2.5$&$\\leq2.9\\times10^8$\\\\20Ca&$-0.37$&$7.2\\times10^6$&$-2.3$&$5.0\\times10^6$\\\\\\hline\\end{tabular}\n"
9
+ }
10
+ ]
evaluation.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ import re
4
+ import json
5
+ import time
6
+ import shutil
7
+ import argparse
8
+ import numpy as np
9
+ import matplotlib.pyplot as plt
10
+
11
+ from tqdm import tqdm
12
+ from datetime import datetime
13
+ from multiprocessing import Pool
14
+ from multiprocessing.dummy import Pool as ThreadPool
15
+ from PIL import Image, ImageDraw
16
+ from skimage.measure import ransac
17
+
18
+ from modules.latex2bbox_color import latex2bbox_color
19
+ from modules.tokenize_latex.tokenize_latex import tokenize_latex
20
+ from modules.visual_matcher import HungarianMatcher, SimpleAffineTransform
21
+
22
+
23
+ def gen_color_list(num=10, gap=15):
24
+ num += 1
25
+ single_num = 255 // gap + 1
26
+ max_num = single_num ** 3
27
+ num = min(num, max_num)
28
+ color_list = []
29
+ for idx in range(num):
30
+ R = idx // single_num**2
31
+ GB = idx % single_num**2
32
+ G = GB // single_num
33
+ B = GB % single_num
34
+
35
+ color_list.append((R*gap, G*gap, B*gap))
36
+ return color_list[1:]
37
+
38
+ def update_inliers(ori_inliers, sub_inliers):
39
+ inliers = np.copy(ori_inliers)
40
+ sub_idx = -1
41
+ for idx in range(len(ori_inliers)):
42
+ if ori_inliers[idx] == False:
43
+ sub_idx += 1
44
+ if sub_inliers[sub_idx] == True:
45
+ inliers[idx] = True
46
+ return inliers
47
+
48
+ def reshape_inliers(ori_inliers, sub_inliers):
49
+ inliers = np.copy(ori_inliers)
50
+ sub_idx = -1
51
+ for idx in range(len(ori_inliers)):
52
+ if ori_inliers[idx] == False:
53
+ sub_idx += 1
54
+ if sub_inliers[sub_idx] == True:
55
+ inliers[idx] = True
56
+ else:
57
+ inliers[idx] = False
58
+ return inliers
59
+
60
+ def gen_token_order(box_list):
61
+ new_box_list = copy.deepcopy(box_list)
62
+ for idx, box in enumerate(new_box_list):
63
+ new_box_list[idx]['order'] = idx / len(new_box_list)
64
+ return new_box_list
65
+
66
+ def evaluation(data_root, user_id="test"):
67
+ data_root = os.path.join(data_root, user_id)
68
+ gt_box_dir = os.path.join(data_root, "gt")
69
+ pred_box_dir = os.path.join(data_root, "pred")
70
+ match_vis_dir = os.path.join(data_root, "vis_match")
71
+ os.makedirs(match_vis_dir, exist_ok=True)
72
+
73
+ max_iter = 3
74
+ min_samples = 3
75
+ residual_threshold = 25
76
+ max_trials = 50
77
+
78
+ metrics_per_img = {}
79
+ gt_basename_list = [item.split(".")[0] for item in os.listdir(os.path.join(gt_box_dir, 'bbox'))]
80
+ for basename in tqdm(gt_basename_list):
81
+ gt_valid, pred_valid = True, True
82
+ if not os.path.exists(os.path.join(gt_box_dir, 'bbox', basename+".jsonl")):
83
+ gt_valid = False
84
+ else:
85
+ with open(os.path.join(gt_box_dir, 'bbox', basename+".jsonl"), 'r') as f:
86
+ box_gt = []
87
+ for line in f:
88
+ info = json.loads(line)
89
+ if info['bbox']:
90
+ box_gt.append(info)
91
+ if not box_gt:
92
+ gt_valid = False
93
+ if not gt_valid:
94
+ continue
95
+
96
+ if not os.path.exists(os.path.join(pred_box_dir, 'bbox', basename+".jsonl")):
97
+ pred_valid = False
98
+ else:
99
+ with open(os.path.join(pred_box_dir, 'bbox', basename+".jsonl"), 'r') as f:
100
+ box_pred = []
101
+ for line in f:
102
+ info = json.loads(line)
103
+ if info['bbox']:
104
+ box_pred.append(info)
105
+ if not box_pred:
106
+ pred_valid = False
107
+ if not pred_valid:
108
+ metrics_per_img[basename] = {
109
+ "recall": 0,
110
+ "precision": 0,
111
+ "F1_score": 0,
112
+ }
113
+ continue
114
+ gt_img_path = os.path.join(gt_box_dir, 'vis', basename+"_base.png")
115
+ pred_img_path = os.path.join(pred_box_dir, 'vis', basename+"_base.png")
116
+
117
+ img_gt = Image.open(gt_img_path)
118
+ img_pred = Image.open(pred_img_path)
119
+
120
+ matcher = HungarianMatcher()
121
+ matched_idxes = matcher(box_gt, box_pred, img_gt.size, img_pred.size)
122
+ src = []
123
+ dst = []
124
+ for (idx1, idx2) in matched_idxes:
125
+ x1min, y1min, x1max, y1max = box_gt[idx1]['bbox']
126
+ x2min, y2min, x2max, y2max = box_pred[idx2]['bbox']
127
+ x1_c, y1_c = float((x1min+x1max)/2), float((y1min+y1max)/2)
128
+ x2_c, y2_c = float((x2min+x2max)/2), float((y2min+y2max)/2)
129
+ src.append([y1_c, x1_c])
130
+ dst.append([y2_c, x2_c])
131
+
132
+ src = np.array(src)
133
+ dst = np.array(dst)
134
+ if src.shape[0] <= min_samples:
135
+ inliers = np.array([True for _ in matched_idxes])
136
+ else:
137
+ inliers = np.array([False for _ in matched_idxes])
138
+ for i in range(max_iter):
139
+ if src[inliers==False].shape[0] <= min_samples:
140
+ break
141
+ model, inliers_1 = ransac((src[inliers==False], dst[inliers==False]), SimpleAffineTransform, min_samples=min_samples, residual_threshold=residual_threshold, max_trials=max_trials, random_state=42)
142
+ if inliers_1 is not None and inliers_1.any():
143
+ inliers = update_inliers(inliers, inliers_1)
144
+ else:
145
+ break
146
+ if len(inliers[inliers==True]) >= len(matched_idxes):
147
+ break
148
+
149
+ for idx, (a,b) in enumerate(matched_idxes):
150
+ if inliers[idx] == True and matcher.cost['token'][a, b] == 1:
151
+ inliers[idx] = False
152
+
153
+ final_match_num = len(inliers[inliers==True])
154
+ recall = round(final_match_num/(len(box_gt)), 3)
155
+ precision = round(final_match_num/(len(box_pred)), 3)
156
+ F1_score = round(2*final_match_num/(len(box_gt)+len(box_pred)), 3)
157
+ metrics_per_img[basename] = {
158
+ "recall": recall,
159
+ "precision": precision,
160
+ "F1_score": F1_score,
161
+ }
162
+
163
+ if True:
164
+ gap = 5
165
+ W1, H1 = img_gt.size
166
+ W2, H2 = img_pred.size
167
+ H = H1 + H2 + gap
168
+ W = max(W1, W2)
169
+
170
+ vis_img = Image.new('RGB', (W, H), (255, 255, 255))
171
+ vis_img.paste(img_gt, (0, 0))
172
+ vis_img.paste(Image.new('RGB', (W, gap), (120, 120, 120)), (0, H1))
173
+ vis_img.paste(img_pred, (0, H1+gap))
174
+
175
+ match_img = vis_img.copy()
176
+ match_draw = ImageDraw.Draw(match_img)
177
+
178
+ gt_matched_idx = {
179
+ a: flag
180
+ for (a,b), flag in
181
+ zip(matched_idxes, inliers)
182
+ }
183
+ pred_matched_idx = {
184
+ b: flag
185
+ for (a,b), flag in
186
+ zip(matched_idxes, inliers)
187
+ }
188
+
189
+ for idx, box in enumerate(box_gt):
190
+ if idx in gt_matched_idx and gt_matched_idx[idx]==True:
191
+ color = "green"
192
+ else:
193
+ color = "red"
194
+ x_min, y_min, x_max, y_max = box['bbox']
195
+ match_draw.rectangle([x_min-1, y_min-1, x_max+1, y_max+1], fill=None, outline=color, width=2)
196
+
197
+ for idx, box in enumerate(box_pred):
198
+ if idx in pred_matched_idx and pred_matched_idx[idx]==True:
199
+ color = "green"
200
+ else:
201
+ color = "red"
202
+ x_min, y_min, x_max, y_max = box['bbox']
203
+ match_draw.rectangle([x_min-1, y_min-1+H1+gap, x_max+1, y_max+1+H1+gap], fill=None, outline=color, width=2)
204
+
205
+ vis_img.save(os.path.join(match_vis_dir, basename+"_base.png"))
206
+ match_img.save(os.path.join(match_vis_dir, basename+".png"))
207
+
208
+ score_list = [val['F1_score'] for _, val in metrics_per_img.items()]
209
+ exp_list = [1 if score==1 else 0 for score in score_list]
210
+ metrics_res = {
211
+ "mean_score": round(np.mean(score_list), 3),
212
+ "exp_rate": round(np.mean(exp_list), 3),
213
+ "details": metrics_per_img
214
+ }
215
+ metric_res_path = os.path.join(data_root, "metrics_res.json")
216
+ with open(metric_res_path, "w") as f:
217
+ f.write(json.dumps(metrics_res, indent=2))
218
+ return metrics_res, metric_res_path, match_vis_dir
219
+
220
+
221
+ if __name__ == '__main__':
222
+ parser = argparse.ArgumentParser()
223
+ parser.add_argument('--input', '-i', type=str, default="assets/example/input_example.json")
224
+ parser.add_argument('--output', '-o', type=str, default="output")
225
+ parser.add_argument('--pools', '-p', type=int, default=240)
226
+ args = parser.parse_args()
227
+ print(args)
228
+
229
+ json_input, data_root, pool_num = args.input, args.output, args.pools
230
+ temp_dir = os.path.join(data_root, "temp_dir")
231
+ exp_name = os.path.basename(json_input).split('.')[0]
232
+ with open(json_input, "r") as f:
233
+ input_data = json.load(f)
234
+ img_ids = []
235
+ groundtruths = []
236
+ predictions = []
237
+ for idx, item in enumerate(input_data):
238
+ if "img_id" in item:
239
+ img_ids.append(item["img_id"])
240
+ else:
241
+ img_ids.append(f"sample_{idx}")
242
+ groundtruths.append(item['gt'])
243
+ predictions.append(item['pred'])
244
+
245
+ a = time.time()
246
+ user_id = exp_name
247
+
248
+ total_color_list = gen_color_list(num=5800)
249
+
250
+ data_root = os.path.join(data_root, user_id)
251
+ output_dir_info = {}
252
+ input_args = []
253
+ for subset, latex_list in zip(['gt', 'pred'], [groundtruths, predictions]):
254
+ sub_temp_dir = os.path.join(temp_dir, f"{exp_name}_{subset}")
255
+ os.makedirs(sub_temp_dir, exist_ok=True)
256
+ output_path = os.path.join(data_root, subset)
257
+ output_dir_info[output_path] = []
258
+
259
+ os.makedirs(os.path.join(output_path, 'bbox'), exist_ok=True)
260
+ os.makedirs(os.path.join(output_path, 'vis'), exist_ok=True)
261
+
262
+ for idx, latex in tqdm(enumerate(latex_list), desc=f"collect {subset} latex ..."):
263
+ basename = img_ids[idx]
264
+ input_arg = latex, basename, output_path, sub_temp_dir, total_color_list
265
+ input_args.append(input_arg)
266
+
267
+ if pool_num > 1:
268
+ print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "using processpool, pool num:", pool_num, ", job num:", len(input_args))
269
+ myP = Pool(args.pools)
270
+ for input_arg in input_args:
271
+ myP.apply_async(latex2bbox_color, args=(input_arg,))
272
+ myP.close()
273
+ myP.join()
274
+ else:
275
+ for input_arg in input_args:
276
+ latex2bbox_color(input_arg)
277
+ b = time.time()
278
+ print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "extract bbox done, time cost:", round(b-a, 3), "s")
279
+
280
+ for subset in ['gt', 'pred']:
281
+ shutil.rmtree(os.path.join(temp_dir, f"{exp_name}_{subset}"))
282
+
283
+ c = time.time()
284
+ metrics_res, metric_res_path, match_vis_dir = evaluation(args.output, exp_name)
285
+ d = time.time()
286
+ print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "calculate metrics done, time cost:", round(d-c, 3), "s")
287
+
288
+ print(f"=> process done, mean f1 score: {metrics_res['mean_score']}.")
289
+ print(f"=> more details of metrics are saved in `{metric_res_path}`")
290
+ print(f"=> visulization images are saved under `{match_vis_dir}`")
modules/latex2bbox_color.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import json
4
+ import shutil
5
+ import logging
6
+ import subprocess
7
+ import numpy as np
8
+
9
+ from threading import Timer
10
+ from PIL import Image, ImageDraw
11
+ from modules.latex_processor import (
12
+ normalize_latex,
13
+ token_add_color_RGB,
14
+ clean_latex
15
+ )
16
+ from modules.tokenize_latex.tokenize_latex import tokenize_latex
17
+
18
+
19
+ tabular_template = r"""
20
+ \documentclass[12pt]{article}
21
+ \usepackage[landscape]{geometry}
22
+ \usepackage{geometry}
23
+ \geometry{a<PaperSize>paper,scale=0.98}
24
+ \pagestyle{empty}
25
+ \usepackage{booktabs}
26
+ \usepackage{multirow}
27
+ \usepackage{amssymb}
28
+ \usepackage{upgreek}
29
+ \usepackage{amsmath}
30
+ \usepackage{xcolor}
31
+ \begin{document}
32
+ \makeatletter
33
+ \renewcommand*{\@textcolor}[3]{%%
34
+ \protect\leavevmode
35
+ \begingroup
36
+ \color#1{#2}#3%%
37
+ \endgroup
38
+ }
39
+ \makeatother
40
+ \begin{displaymath}
41
+ %s
42
+ \end{displaymath}
43
+ \end{document}
44
+ """
45
+
46
+ formular_template = r"""
47
+ \documentclass[12pt]{article}
48
+ \usepackage[landscape]{geometry}
49
+ \usepackage{geometry}
50
+ \geometry{a<PaperSize>paper,scale=0.98}
51
+ \pagestyle{empty}
52
+ \usepackage{booktabs}
53
+ \usepackage{amsmath}
54
+ \usepackage{upgreek}
55
+ \usepackage{amssymb}
56
+ \usepackage{xcolor}
57
+ \begin{document}
58
+ \makeatletter
59
+ \renewcommand*{\@textcolor}[3]{%%
60
+ \protect\leavevmode
61
+ \begingroup
62
+ \color#1{#2}#3%%
63
+ \endgroup
64
+ }
65
+ \makeatother
66
+ \begin{displaymath}
67
+ %s
68
+ \end{displaymath}
69
+ \end{document}
70
+ """
71
+
72
+
73
+ def run_cmd(cmd, timeout_sec=30):
74
+ proc = subprocess.Popen(cmd, shell=True)
75
+ kill_proc = lambda p: p.kill()
76
+ timer = Timer(timeout_sec, kill_proc, [proc])
77
+ try:
78
+ timer.start()
79
+ stdout,stderr = proc.communicate()
80
+ finally:
81
+ timer.cancel()
82
+
83
+ def convert_pdf2img(pdf_filename, png_filename):
84
+ cmd = "magick -density 200 -quality 100 %s %s"%(pdf_filename, png_filename)
85
+ os.system(cmd)
86
+
87
+ def crop_image(image_path, pad=8):
88
+ img = Image.open(image_path).convert("L")
89
+ img_data = np.asarray(img, dtype=np.uint8)
90
+ nnz_inds = np.where(img_data!=255)
91
+ if len(nnz_inds[0]) == 0:
92
+ y_min = 0
93
+ y_max = 10
94
+ x_min = 0
95
+ x_max = 10
96
+ else:
97
+ y_min = np.min(nnz_inds[0])
98
+ y_max = np.max(nnz_inds[0])
99
+ x_min = np.min(nnz_inds[1])
100
+ x_max = np.max(nnz_inds[1])
101
+
102
+ img = Image.open(image_path).convert("RGB").crop((x_min-pad, y_min-pad, x_max+pad, y_max+pad))
103
+ img.save(image_path)
104
+
105
+ def extrac_bbox_from_color_image(image_path, color_list):
106
+ img = Image.open(image_path).convert("RGB")
107
+ W, H = img.size
108
+ pixels = list(img.getdata())
109
+
110
+ bbox_list = []
111
+ for target_color in color_list:
112
+ target_pixels = [ i for i, pixel in enumerate(pixels)if pixel == target_color ]
113
+ x_list = []
114
+ y_list = []
115
+ for idx in target_pixels:
116
+ x_list.append(idx % W)
117
+ y_list.append(idx // W)
118
+ try:
119
+ y_min, y_max, x_min, x_max = min(y_list), max(y_list), min(x_list), max(x_list)
120
+ bbox_list.append([x_min-1, y_min-1, x_max+1, y_max+1])
121
+
122
+ except:
123
+ bbox_list.append([])
124
+ continue
125
+
126
+ img = img.convert("L")
127
+ img_bw = img.point(lambda x: 255 if x == 255 else 0, '1')
128
+ img_bw.convert("RGB").save(image_path)
129
+ return bbox_list
130
+
131
+
132
+ def latex2bbox_color(input_arg):
133
+ latex, basename, output_path, temp_dir, total_color_list = input_arg
134
+ template = tabular_template if "tabular" in latex else formular_template
135
+ output_bbox_path = os.path.join(output_path, 'bbox', basename+'.jsonl')
136
+ output_vis_path = os.path.join(output_path, 'vis', basename+'.png')
137
+ output_base_path = os.path.join(output_path, 'vis', basename+'_base.png')
138
+
139
+ if os.path.exists(output_bbox_path) and os.path.exists(output_vis_path) and os.path.exists(output_base_path):
140
+ return
141
+
142
+ try:
143
+ ret, new_latex = tokenize_latex(latex, middle_file=os.path.join(temp_dir, basename+'.txt'))
144
+ if not(ret and new_latex):
145
+ log = f"ERROR, Tokenize latex failed: {basename}."
146
+ logging.info(log)
147
+ new_latex = latex
148
+ latex = normalize_latex(new_latex)
149
+ token_list = []
150
+ l_split = latex.strip().split(' ')
151
+ color_list = total_color_list[0:len(l_split)]
152
+ idx = 0
153
+ while idx < len(l_split):
154
+ l_split, idx, token_list = token_add_color_RGB(l_split, idx, token_list)
155
+
156
+ rgb_latex = " ".join(l_split)
157
+ for idx, color in enumerate(color_list):
158
+ R, G, B = color
159
+ rgb_latex = rgb_latex.replace(f"<color_{idx}>", f"{R},{G},{B}")
160
+
161
+ if len(token_list) > 1300:
162
+ paper_size = 3
163
+ elif len(token_list) > 600:
164
+ paper_size = 4
165
+ else:
166
+ paper_size = 5
167
+ final_latex = formular_template.replace("<PaperSize>", str(paper_size)) % rgb_latex
168
+
169
+ except Exception as e:
170
+ log = f"ERROR, Preprocess latex failed: {basename}; {e}."
171
+ logging.info(log)
172
+ return
173
+
174
+ pre_name = output_path.replace('/', '_').replace('.','_') + '_' + basename
175
+ tex_filename = os.path.join(temp_dir, pre_name+'.tex')
176
+ log_filename = os.path.join(temp_dir, pre_name+'.log')
177
+ aux_filename = os.path.join(temp_dir, pre_name+'.aux')
178
+
179
+ with open(tex_filename, "w") as w:
180
+ print(final_latex, file=w)
181
+ run_cmd(f"pdflatex -interaction=nonstopmode -output-directory={temp_dir} {tex_filename} >/dev/null")
182
+ try:
183
+ os.remove(tex_filename)
184
+ os.remove(log_filename)
185
+ os.remove(aux_filename)
186
+ except:
187
+ pass
188
+ pdf_filename = tex_filename[:-4]+'.pdf'
189
+ if not os.path.exists(pdf_filename):
190
+ log = f"ERROR, Compile pdf failed: {pdf_filename}"
191
+ logging.info(log)
192
+ else:
193
+ convert_pdf2img(pdf_filename, output_base_path)
194
+ os.remove(pdf_filename)
195
+
196
+ crop_image(output_base_path)
197
+ bbox_list = extrac_bbox_from_color_image(output_base_path, color_list)
198
+ vis = Image.open(output_base_path)
199
+ draw = ImageDraw.Draw(vis)
200
+
201
+ with open(output_bbox_path, 'w') as f:
202
+ for token, box in zip(token_list, bbox_list):
203
+ item = {
204
+ "bbox": box,
205
+ "token": token
206
+ }
207
+ f.write(json.dumps(item)+'\n')
208
+
209
+ if not box:
210
+ continue
211
+ x_min, y_min, x_max, y_max = box
212
+ draw.rectangle([x_min, y_min, x_max, y_max], fill=None, outline=(0,250,0), width=1)
213
+ draw.text((x_min, y_min), token, (250,0,0))
214
+
215
+ vis.save(output_vis_path)
modules/latex_processor.py ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import json
4
+ import shutil
5
+ import logging
6
+ import numpy as np
7
+ from PIL import Image
8
+
9
+
10
+ SKIP_PATTERNS = [r'\{', r'\}', r'[\[\]]', r'\\begin\{.*?\}', r'\\end\{.*?\}', r'\^', r'\_', r'\\.*rule.*', r'\\.*line.*', r'\[[\-.0-9]+[epm][xtm]\]']
11
+ SKIP_Tokens = ['\\', '\\\\', '\\index', '\\a', '&', '$', '\\multirow', '\\def', '\\raggedright', '\\url', '\\cr', '\\ensuremath', '\\left', '\\right',
12
+ '\\mathchoice', '\\scriptstyle', '\\displaystyle', '\\qquad', '\\quad', '\\,', '\\!', '~', '\\boldmath']
13
+ PHANTOM_Tokens = ['\\fontfamily', '\\vphantom', '\\phantom', '\\rowcolor', '\\ref']
14
+ TWO_Tail_Tokens = ['\\frac', '\\binom']
15
+ AB_Tail_Tokens = ['\\xrightarrow', '\\xleftarrow', '\\sqrt'] # special token \xxx [] {}
16
+ TWO_Tail_Invisb_Tokens = ['\\overset', '\\underset', '\\stackrel']
17
+ ONE_Tail_Tokens = ['\\widetilde', '\\overline', '\\hat', '\\widehat', '\\tilde', '\\Tilde', '\\dot', '\\bar', '\\vec', '\\underline', '\\underbrace', '\\check',
18
+ '\\breve', '\\Bar', '\\Vec', '\\mathring', '\\ddot']
19
+ ONE_Tail_Invisb_Tokens = ['\\boldsymbol', '\\pmb', '\\textbf', '\\mathrm', '\\mathbf', '\\mathbb', '\\mathcal', '\\textmd', '\\texttt', '\\textnormal',
20
+ '\\text', '\\textit', '\\textup', '\\mathop', '\\mathbin', '\\smash', '\\operatorname', '\\textrm', '\\mathfrak', '\\emph',
21
+ '\\textsf', '\\textsc']
22
+
23
+
24
+ def flatten_multiline(latex):
25
+ brace_map = {
26
+ "\\left(": "\\right)",
27
+ "\\left[": "\\right]",
28
+ "\\left{": "\\right}",
29
+ }
30
+ l_split = latex.split(' ')
31
+ if l_split[0] == "\\begin{array}":
32
+ if l_split[-1] == "\\end{array}":
33
+ l_split = l_split[2:-1]
34
+ else:
35
+ l_split = l_split[2:]
36
+
37
+ idx = 0
38
+ while idx < len(l_split):
39
+ token = l_split[idx]
40
+ if token.startswith("\\left") and token in brace_map.keys():
41
+ end_idx = find_matching_brace(l_split, idx, brace=[token, brace_map[token]])
42
+ if end_idx != -1:
43
+ idx = end_idx
44
+ elif token in ["\\\\", "~", "\\qquad"]:
45
+ l_split = l_split[0:idx] + l_split[idx+1:]
46
+ idx -= 1
47
+ idx += 1
48
+ latex = ' '.join(l_split)
49
+ return "$ "+latex+" $"
50
+
51
+
52
+ def clean_latex(text):
53
+ # TODO 让GPT写的去空格函数, 初步测了是没问题的, 不确定是否完全没有bug
54
+ cleaned_text = re.sub(r'(?<=[^\\])\s+(?=[^\\])', '', text)
55
+ # TODO 有一些不能去掉的空格给补充回来
56
+ for item in ["\\hline", "\\midrule", "\\times", "\\bf", "\\footnotesize", "\\cr", '\\log']:
57
+ cleaned_text = cleaned_text.replace(item, item+" ")
58
+ cleaned_text = cleaned_text.replace(" \\mathcolor{black}", "\\mathcolor{black}")
59
+ return cleaned_text
60
+
61
+ def remove_trailing_latex(formula):
62
+ pattern = r'(\\(hspace\*?\{[^{}]*?\}|vspace\*?\{[^{}]*?\}|smallskip|medskip|quad|qquad|bigskip|[;,])|\~|\.)*$'
63
+ # Replace the matched pattern with an empty string
64
+ cleaned_formula = re.sub(pattern, '', formula, count=1)
65
+ return cleaned_formula
66
+
67
+ def find_matching_brace(sequence, start_index, brace=['{', '}']):
68
+ # Finds the index of the matching brace for the one at start_index
69
+ left_brace, right_brace = brace
70
+ depth = 0
71
+ for i, char in enumerate(sequence[start_index:], start=start_index):
72
+ if char == left_brace:
73
+ depth += 1
74
+ elif char == right_brace:
75
+ depth -= 1
76
+ if depth == 0:
77
+ return i
78
+ if depth > 0:
79
+ error_info = "Warning! found no matching brace in sequence !"
80
+ raise ValueError(error_info)
81
+ return -1
82
+
83
+ def normalize_latex(l, rm_trail=False):
84
+ if "tabular" in l:
85
+ latex_type = "tabular"
86
+ else:
87
+ latex_type = "formula"
88
+
89
+ if rm_trail:
90
+ l = remove_trailing_latex(l)
91
+ l = l.strip().replace(r'\pmatrix', r'\mypmatrix').replace(r'\matrix', r'\mymatrix')
92
+
93
+ # TODO \raggedright \arraybackslash, these align method, difficult to handle, remove it.
94
+ for item in ['\\raggedright', '\\arraybackslash']:
95
+ l = l.replace(item, "")
96
+
97
+ for item in ['\\lowercase', '\\uppercase']:
98
+ l = l.replace(item, "")
99
+
100
+ # TODO \hspace {1 . 5 cm}, for formula, change to \hspace{1.5cm}, for table, remove it.
101
+ pattern = r'\\[hv]space { [.0-9a-z ]+ }'
102
+ old_token = re.findall(pattern, l, re.DOTALL)
103
+ if latex_type == "tabular":
104
+ new_token = ["" for item in old_token]
105
+ else:
106
+ new_token = [item.replace(" ", "") for item in old_token]
107
+ for bef, aft in zip(old_token, new_token):
108
+ l = l.replace(bef, aft)
109
+
110
+ # TODO take \begin {tabular} {} as one token
111
+ # TODO there are \begin{array} in table too,so the process should run in both formula and table.
112
+ if latex_type == "tabular":
113
+ l = l.replace("\\begin {tabular}", "\\begin{tabular}")
114
+ l = l.replace("\\end {tabular}", "\\end{tabular}")
115
+ l = l.replace("\\begin {array}", "\\begin{array}")
116
+ l = l.replace("\\end {array}", "\\end{array}")
117
+ l_split = l.split(' ')
118
+ idx = 0
119
+ while idx < len(l_split):
120
+ token = l_split[idx]
121
+ if token == "\\begin{tabular}":
122
+ sub_idx = idx + 1
123
+ end_idx = find_matching_brace(l_split, sub_idx)
124
+ new_token = "".join(l_split[idx: end_idx+1])
125
+ l_split = l_split[0:idx] + [new_token] + l_split[end_idx+1:]
126
+ break
127
+ idx += 1
128
+ l = ' '.join(l_split)
129
+
130
+ # TODO some complex format, hart to deal with re.match, so using brace match, such as:\cmidrule ( l { 3 p t } r { 3 p t } ) { 1 - 1 }
131
+ l_split = l.split(' ')
132
+ idx = 0
133
+ while idx < len(l_split):
134
+ token = l_split[idx]
135
+ if token in ["\\cmidrule", "\\cline"]:
136
+ sub_idx = idx + 1
137
+ if l_split[sub_idx] == "(":
138
+ mid_end = find_matching_brace(l_split, sub_idx, brace=['(', ')'])
139
+ end_idx = find_matching_brace(l_split, mid_end+1)
140
+ else:
141
+ end_idx = find_matching_brace(l_split, sub_idx)
142
+ new_token = "".join(l_split[idx: end_idx+1])
143
+ l_split = l_split[0:idx] + [new_token] + l_split[end_idx+1:]
144
+ idx += 1
145
+ l = ' '.join(l_split)
146
+
147
+ pattern = r'\\begin{array} { [lrc ]+ }'
148
+ old_token = re.findall(pattern, l, re.DOTALL)
149
+ new_token = [item.replace("\\begin{array} ", "<s>").replace(" ", "").replace("<s>", "\\begin{array} ") for item in old_token]
150
+ for bef, aft in zip(old_token, new_token):
151
+ l = l.replace(bef, aft)
152
+
153
+ # TODO token such \not= should be one token
154
+ pattern = r'\\not [<>+=\-]'
155
+ old_token = re.findall(pattern, l, re.DOTALL)
156
+ new_token = [item.replace(" ", "") for item in old_token]
157
+ for bef, aft in zip(old_token, new_token):
158
+ l = l.replace(bef, aft)
159
+
160
+ # TODO tokens such as \dots \exp \sinh, split them to parts, so the bbox match will be easier.
161
+
162
+ l = " "+l+" "
163
+ l = l.replace(" \\ldots ", " . . . ")
164
+ l = l.replace(" \\cdots ", " . . . ")
165
+ l = l.replace(" \\dots ", " . . . ")
166
+ l = l.replace(" \\dotsb ", " . . . ")
167
+ l = l.replace(" \\log ", " \\mathrm { l o g } ")
168
+ l = l.replace(" \\exp ", " \\mathrm { e x p } ")
169
+ l = l.replace(" \\sin ", " \\mathrm { s i n } ")
170
+ l = l.replace(" \\cos ", " \\mathrm { c o s } ")
171
+ l = l.replace(" \\tan ", " \\mathrm { t a n } ")
172
+ l = l.replace(" \\tanh ", " \\mathrm { t a n h } ")
173
+ l = l.replace(" \\cosh ", " \\mathrm { c o s h } ")
174
+ l = l.replace(" \\sinh ", " \\mathrm { s i n h } ")
175
+
176
+ # ** token such as \big( should be one token
177
+ pattern = r'\\[Bb]ig[g]?[glrm]? [(){}|\[\]] '
178
+ old_token = re.findall(pattern, l, re.DOTALL)
179
+ new_token = [item.replace(" ", "") for item in old_token]
180
+ for bef, aft in zip(old_token, new_token):
181
+ l = l.replace(bef, aft+" ")
182
+
183
+ pattern = r'\\[Bb]ig[g]?[glrm]? \\.*? '
184
+ old_token = re.findall(pattern, l, re.DOTALL)
185
+ new_token = [item.replace(" ", "") for item in old_token]
186
+ for bef, aft in zip(old_token, new_token):
187
+ l = l.replace(bef, aft+" ")
188
+
189
+ # TODO when \operatorname * meets mathcolor it comes error, yet the * is useless, so we simply remove it bynow.
190
+ pattern = r'\\operatorname \*'
191
+ old_token = re.findall(pattern, l, re.DOTALL)
192
+ new_token = ["\\operatorname" for item in old_token]
193
+ for bef, aft in zip(old_token, new_token):
194
+ l = l.replace(bef, aft)
195
+
196
+ # TODO \lefteqn will lead to letter overlap, it's harmfull for render, so simply remove it.
197
+ l = l.replace("\\lefteqn", "")
198
+
199
+ # TODO \footnote can not seem as ONE_Tail_Invisb_Tokens(usually this type token add color by \mathrm {\color(x)}, yet \footnode should be \color{\footnote{x}}), so we simple change it to "^".
200
+ l = l.replace("\\footnote ", "^ ")
201
+
202
+ # TODO \' can not be rendered separately(cause to different visulize performence), so we take these tokens as one token such as \' e -> \'e, on the other hand, if { after \' then render them separately.
203
+ pattern = r'\\\' [^{] '
204
+ old_token = re.findall(pattern, l, re.DOTALL)
205
+ new_token = [item.replace(" ", "") for item in old_token]
206
+ for bef, aft in zip(old_token, new_token):
207
+ l = l.replace(bef, aft+" ")
208
+
209
+ # TODO [ -1.5ex ] [ 1.5pt ] [ 3 mm ] some layout adjustment, no need to render. combine them as one token.
210
+ if latex_type == "tabular":
211
+ pattern = r'\[ [\-.0-9 ]+[exptcm ]+ \]'
212
+ old_token = re.findall(pattern, l, re.DOTALL)
213
+ new_token = [item.replace(" ", "") for item in old_token]
214
+ for bef, aft in zip(old_token, new_token):
215
+ l = l.replace(bef, aft)
216
+
217
+ # ** \parbox { 3cm } {} shoudle be combined as one token
218
+ pattern = r'\\parbox {[^{]+}'
219
+ old_token = re.findall(pattern, l, re.DOTALL)
220
+ new_token = [item.replace(" ", "") for item in old_token]
221
+ for bef, aft in zip(old_token, new_token):
222
+ l = l.replace(bef, aft)
223
+
224
+ # ** \raisebox{<lift>}[<height>][<depth>] {} shoudle be combined as one token, \raisebox{-1.5ex}[0pt]
225
+ pattern = r'\\raisebox {[^{]+} [\[\]0-9 exptcm]+{'
226
+ old_token = re.findall(pattern, l, re.DOTALL)
227
+ new_token = [item.replace(" ", "") for item in old_token]
228
+ for bef, aft in zip(old_token, new_token):
229
+ l = l.replace(bef, aft[0:-1]+" {")
230
+
231
+ # ** \char shoudle be combined as one token
232
+ pattern = r'{ \\char[0-9\' ]+}'
233
+ old_token = re.findall(pattern, l, re.DOTALL)
234
+ new_token = [item.replace(" ", "") for item in old_token]
235
+ for bef, aft in zip(old_token, new_token):
236
+ l = l.replace(bef, "{ "+aft[1:-1]+" }")
237
+
238
+ # ** \not xx shoudle be combined as one token
239
+ pattern = r'\\not [\\=\<\>][^ ]+ '
240
+ old_token = re.findall(pattern, l, re.DOTALL)
241
+ new_token = [item.replace(" ", "") for item in old_token]
242
+ for bef, aft in zip(old_token, new_token):
243
+ l = l.replace(bef, aft+" ")
244
+
245
+ # ** \specialrule{1pt}{2pt}{2pt}, special lines, shoudle be combined as one token
246
+ pattern = r'\\specialrule {[ .0-9a-z]+} {[ .0-9a-z]+} {[ .0-9a-z]+}'
247
+ old_token = re.findall(pattern, l, re.DOTALL)
248
+ new_token = [item.replace(" ", "") for item in old_token]
249
+ for bef, aft in zip(old_token, new_token):
250
+ l = l.replace(bef, aft)
251
+
252
+ # ** for easier add color, the original color should be removed, there are two type of color for now: \color[rgb]{0, 1, 0} and \color{red}
253
+ pattern = r'\\colorbox[ \[\]RGBrgb]+{ [A-Za-z 0-9,!]+ } |\\color[ \[\]RGBrgb]+{ [A-Za-z 0-9,!]+ } |\\textcolor[ \[\]RGBrgb]+{ [A-Za-z 0-9,!]+ } |\\cellcolor[ \[\]RGBrgb]+{ [A-Za-z 0-9,!]+ } '
254
+ old_token = re.findall(pattern, l, re.DOTALL)
255
+ for bef in old_token:
256
+ l = l.replace(bef, "")
257
+
258
+ # ** filling the missing brace [] and {} according to token.
259
+ l_split = l.split(' ')
260
+ idx = 0
261
+ while idx < len(l_split):
262
+ token = l_split[idx]
263
+ if token in ONE_Tail_Tokens + ONE_Tail_Invisb_Tokens:
264
+ # ** normalize tokens such as \hat, fill missing the {}, such as \hat \lambda -> \hat {\lambda}
265
+ sub_idx = idx + 1
266
+ while sub_idx < len(l_split) and l_split[sub_idx] in ONE_Tail_Tokens+ONE_Tail_Invisb_Tokens:
267
+ sub_idx += 1
268
+ new_split = l_split[0:idx]
269
+ for ii in range(idx, sub_idx):
270
+ new_split = new_split + [l_split[ii], "{"]
271
+ if l_split[sub_idx] != "{":
272
+ new_split = new_split + [l_split[sub_idx]] + ["}"]*(sub_idx-idx)
273
+ l_split = new_split + l_split[sub_idx+1:]
274
+ else:
275
+ end_idx = find_matching_brace(l_split, sub_idx)
276
+ new_split = new_split + l_split[sub_idx+1:end_idx] + ["}"]*(sub_idx-idx)
277
+ l_split = new_split + l_split[end_idx+1:]
278
+ elif token in AB_Tail_Tokens:
279
+ # ** normalize special tokens such as \sqrt, fill the missing [] {} in \sqrt [] {}, yet the [] is optional, for example: \sqrt A B -> \sqrt {A} B and \sqrt [A] B -> \sqrt [A] {B}
280
+ if l_split[idx + 1] != "[" and l_split[idx + 1] != "{":
281
+ l_split = l_split[0:idx+1] + ["{"] + [l_split[idx+1]] + ["}"] + l_split[idx+2:]
282
+ else:
283
+ if l_split[idx + 1] == "[":
284
+ end1 = find_matching_brace(l_split, idx+1, brace=['[', ']'])
285
+ else:
286
+ end1 = idx
287
+ if l_split[end1 + 1] != "{":
288
+ l_split = l_split[0:end1+1] + ["{"] + [l_split[end1+1]] + ["}"] + l_split[end1+2:]
289
+ elif token in TWO_Tail_Tokens + TWO_Tail_Invisb_Tokens:
290
+ # ** normalize special tokens such as \frac, add missing brace in \frac {A} {B} for example: \frac {\lambda} 2 -> \frac {\lambda} {2}
291
+ if l_split[idx + 1] != "{":
292
+ l_split = l_split[0:idx+1] + ["{"] + [l_split[idx+1]] + ["}"] + l_split[idx+2:]
293
+ end1 = find_matching_brace(l_split, idx+1)
294
+ if l_split[end1 + 1] != "{":
295
+ l_split = l_split[0:end1+1] + ["{"] + [l_split[end1+1]] + ["}"] + l_split[end1+2:]
296
+
297
+ idx += 1
298
+ l = ' '.join(l_split)
299
+
300
+ return l
301
+
302
+ def token_add_color(l_split, idx, render_dict):
303
+ token = l_split[idx]
304
+ if token in PHANTOM_Tokens:
305
+ # ** special tokens that do not need render, skip it
306
+ if l_split[idx + 1] == '{':
307
+ brace_end = find_matching_brace(l_split, idx + 1)
308
+ else:
309
+ brace_end = idx + 1
310
+ next_idx = brace_end + 1
311
+ elif token in TWO_Tail_Tokens:
312
+ # ** tokens such as \frac A B, and the token needs render too.
313
+ num_start = idx + 1
314
+ num_end = find_matching_brace(l_split, num_start)
315
+ den_start = num_end + 1
316
+ den_end = find_matching_brace(l_split, den_start)
317
+ l_split_copy = l_split[:idx] + [r'\mathcolor{black}{'+token+'{'] + \
318
+ [r'\mathcolor{gray}{'] + l_split[num_start + 1:num_end] + \
319
+ ['}'] + [r'}{'] + [r'\mathcolor{gray}{'] + l_split[den_start + 1:den_end] + \
320
+ ['}'] + ['}'] + ['}'] + l_split[den_end + 1:]
321
+
322
+ l_new = ' '.join(l_split_copy)
323
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
324
+ render_dict[str(idx)] = l_new, token
325
+ next_idx = idx + 1
326
+ elif token in ONE_Tail_Tokens:
327
+ # ** tokens such as \hat A, and the token needs render too.
328
+ num_start = idx + 1
329
+ num_end = find_matching_brace(l_split, num_start)
330
+ l_split_copy = l_split[:idx] + [r'\mathcolor{black}{'] + l_split[idx: num_start+1] + \
331
+ [r'\mathcolor{gray}{'] + l_split[num_start+1: num_end] + \
332
+ ['}'] + l_split[num_end: num_end+1] + ['}'] + l_split[num_end+1:]
333
+ l_new = ' '.join(l_split_copy)
334
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
335
+ render_dict[str(idx)] = l_new, token
336
+ next_idx = idx + 1
337
+ elif token in ONE_Tail_Invisb_Tokens:
338
+ # ** tokens such as \text A B, and the token does not need render.
339
+ num_start = idx + 1
340
+ num_end = find_matching_brace(l_split, num_start)
341
+ sub_idx = num_start+1
342
+ if num_end-num_start == 2:
343
+ l_split_copy = l_split.copy()
344
+ l_split_copy[sub_idx] = r'{\mathcolor{black}{' + l_split_copy[sub_idx] + '}}'
345
+ l_new = ' '.join(l_split_copy)
346
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
347
+ render_dict[str(idx)] = l_new, l_split[sub_idx]
348
+ next_idx = num_end
349
+ else:
350
+ while sub_idx < num_end:
351
+ l_split, sub_idx, render_dict = token_add_color(l_split, sub_idx, render_dict)
352
+ next_idx = num_end + 1
353
+ elif token in AB_Tail_Tokens:
354
+ # ** special token \xrightarrow, could be \xrightarrow [] {} or \xrightarrow {}, process method are different.
355
+ if l_split[idx+1] == '{':
356
+ num_start = idx + 1
357
+ num_end = find_matching_brace(l_split, num_start)
358
+ l_split_copy = l_split[:idx] + [r'\mathcolor{black}{'] + l_split[idx: idx+2] \
359
+ + [r'\mathcolor{gray}{'] + l_split[num_start+1: num_end] + ['}}'] + l_split[num_end:]
360
+ l_new = ' '.join(l_split_copy)
361
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
362
+ render_dict[str(idx)] = l_new, token
363
+ sub_idx = num_start+1
364
+ while sub_idx < num_end:
365
+ l_split, sub_idx, render_dict = token_add_color(l_split, sub_idx, render_dict)
366
+ next_idx = num_end + 1
367
+ elif l_split[idx+1] == '[':
368
+ num_start = idx + 1
369
+ num_end = find_matching_brace(l_split, num_start, brace=['[', ']'])
370
+ den_start = num_end + 1
371
+ den_end = find_matching_brace(l_split, den_start)
372
+ l_split_copy = l_split[:idx] + [r'{\mathcolor{black}{'] + l_split[idx: idx+2] \
373
+ + [r'\mathcolor{gray}{'] + l_split[idx+2: num_end] + ['}'] + l_split[num_end:den_start+1] \
374
+ + [r'\mathcolor{gray}{'] + l_split[den_start+1: den_end] + ['}'] + l_split[den_end: den_end+1] \
375
+ + ['}}'] + l_split[den_end+1:]
376
+ l_new = ' '.join(l_split_copy)
377
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
378
+ render_dict[str(idx)] = l_new, token
379
+ sub_idx = num_start + 1
380
+ while sub_idx < num_end:
381
+ l_split, sub_idx, render_dict = token_add_color(l_split, sub_idx, render_dict)
382
+ sub_idx = den_start + 1
383
+ while sub_idx < den_end:
384
+ l_split, sub_idx, render_dict = token_add_color(l_split, sub_idx, render_dict)
385
+ next_idx = den_end + 1
386
+ elif token in ["\\multicolumn", "\\multirow"]:
387
+ # ** tokens with three {}, such as \multicolumn {} {} {}, the text in third {} need be rendered.
388
+ first_start = idx + 1
389
+ first_end = find_matching_brace(l_split, first_start)
390
+ second_start = first_end + 1
391
+ second_end = find_matching_brace(l_split, second_start)
392
+ third_start = second_end + 1
393
+ third_end = find_matching_brace(l_split, third_start)
394
+
395
+ sub_idx = third_start+1
396
+ while sub_idx < third_end:
397
+ l_split, sub_idx, render_dict = token_add_color(l_split, sub_idx, render_dict)
398
+ next_idx = third_end + 1
399
+ elif token in SKIP_Tokens+TWO_Tail_Invisb_Tokens or any(re.match(pattern, token) for pattern in SKIP_PATTERNS):
400
+ # ** tokens no need render, just skip
401
+ # print('skip', idx, token)
402
+ # TODO special case :[], could be single, or in \sqrt[]{}.
403
+ if (token == "[" and l_split[idx-1]!="\\sqrt") or (token == "]" and idx>=3 and l_split[idx-3]!="\\sqrt"):
404
+ l_split_copy = l_split.copy()
405
+ l_split_copy[idx] = r'\mathcolor{black}{ ' + l_split_copy[idx] + ' }'
406
+ l_new = ' '.join(l_split_copy)
407
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
408
+ render_dict[str(idx)] = l_new, token
409
+ next_idx = idx + 1
410
+ else:
411
+ next_idx = idx + 1
412
+ else:
413
+ # ** nomal token
414
+ l_split_copy = l_split.copy()
415
+ # TODO sometimes there is translation after add color, the exp prove that \mathcolor{black}{ A } is better than \mathcolor{black}{A}
416
+ l_split_copy[idx] = r'\mathcolor{black}{ ' + l_split_copy[idx] + ' }'
417
+
418
+ l_new = ' '.join(l_split_copy)
419
+ l_new = r'\mathcolor{gray}{ ' + l_new + ' }'
420
+ render_dict[str(idx)] = l_new, token
421
+ next_idx = idx + 1
422
+
423
+ return l_split, next_idx, render_dict
424
+
425
+
426
+ def token_add_color_RGB(l_split, idx, token_list, brace_color=False):
427
+ """using \mathcolor[RGB]{r,g,b} to render latex.
428
+ """
429
+ token = l_split[idx]
430
+ if not token:
431
+ next_idx = idx + 1
432
+ elif token in PHANTOM_Tokens:
433
+ # ** special tokens that do not need render, skip it
434
+ if l_split[idx + 1] == '{':
435
+ brace_end = find_matching_brace(l_split, idx + 1)
436
+ else:
437
+ brace_end = idx + 1
438
+ next_idx = brace_end + 1
439
+ elif token in TWO_Tail_Tokens:
440
+ # ** tokens such as \frac A B, and the token needs render too.
441
+ num_start = idx + 1
442
+ num_end = find_matching_brace(l_split, num_start)
443
+ den_start = num_end + 1
444
+ den_end = find_matching_brace(l_split, den_start)
445
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
446
+ l_split = l_split[:idx] + [color_token+token] + l_split[idx+1: den_end+1] + ["}"] + l_split[den_end+1:]
447
+ token_list.append(token)
448
+ next_idx = idx + 1
449
+ elif token in ONE_Tail_Tokens:
450
+ # ** tokens such as \hat A, and the token needs render too.
451
+ num_start = idx + 1
452
+ num_end = find_matching_brace(l_split, num_start)
453
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
454
+ if token != "\\underbrace" and num_end+1 < len(l_split) and l_split[num_end+1] == "_":
455
+ l_split = l_split[:idx] + ["{"+color_token+token] + l_split[idx+1: num_end+1] + ["}}"] + l_split[num_end+1:]
456
+ else:
457
+ l_split = l_split[:idx] + [color_token+token] + l_split[idx+1: num_end+1] + ["}"] + l_split[num_end+1:]
458
+ token_list.append(token)
459
+ next_idx = idx + 1
460
+ elif token in ONE_Tail_Invisb_Tokens:
461
+ # ** tokens such as \text A B, and the token does not need render.
462
+ num_start = idx + 1
463
+ num_end = find_matching_brace(l_split, num_start)
464
+ sub_idx = num_start+1
465
+ if num_end-num_start == 2:
466
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
467
+ token_list.append(l_split[num_start+1])
468
+ l_split = l_split[:num_start+1] + [color_token+l_split[num_start+1]+"}"] + l_split[num_end:]
469
+ else:
470
+ while sub_idx < num_end:
471
+ l_split, sub_idx, token_list = token_add_color_RGB(l_split, sub_idx, token_list)
472
+ next_idx = num_end + 1
473
+ elif token in AB_Tail_Tokens:
474
+ # ** special token \xrightarrow, could be \xrightarrow [] {} or \xrightarrow {}, process method are different.
475
+ if l_split[idx+1] == '{':
476
+ num_start = idx + 1
477
+ num_end = find_matching_brace(l_split, num_start)
478
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
479
+ l_split = l_split[:idx] + [color_token+token] + l_split[idx+1: num_end+1] + ["}"] + l_split[num_end+1:]
480
+ token_list.append(token)
481
+ sub_idx = num_start+1
482
+ while sub_idx < num_end:
483
+ l_split, sub_idx, token_list = token_add_color_RGB(l_split, sub_idx, token_list)
484
+ next_idx = num_end + 1
485
+ elif l_split[idx+1] == '[':
486
+ num_start = idx + 1
487
+ num_end = find_matching_brace(l_split, num_start, brace=['[', ']'])
488
+ den_start = num_end + 1
489
+ den_end = find_matching_brace(l_split, den_start)
490
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
491
+ l_split = l_split[:idx] + [color_token+token] + l_split[idx+1: den_end+1] + ["}"] + l_split[den_end+1:]
492
+ token_list.append(token)
493
+ sub_idx = num_start + 1
494
+ while sub_idx < num_end:
495
+ l_split, sub_idx, token_list = token_add_color_RGB(l_split, sub_idx, token_list, brace_color=True)
496
+ sub_idx = den_start + 1
497
+ while sub_idx < den_end:
498
+ l_split, sub_idx, token_list = token_add_color_RGB(l_split, sub_idx, token_list)
499
+ next_idx = den_end + 1
500
+ elif token in ["\\multicolumn", "\\multirow"]:
501
+ # ** tokens with three {}, such as \multicolumn {} {} {}, the text in third {} need be rendered.
502
+ first_start = idx + 1
503
+ first_end = find_matching_brace(l_split, first_start)
504
+ second_start = first_end + 1
505
+ second_end = find_matching_brace(l_split, second_start)
506
+ third_start = second_end + 1
507
+ third_end = find_matching_brace(l_split, third_start)
508
+
509
+ sub_idx = third_start+1
510
+ while sub_idx < third_end:
511
+ l_split, sub_idx, token_list = token_add_color_RGB(l_split, sub_idx, token_list)
512
+ next_idx = third_end + 1
513
+ elif token in SKIP_Tokens+TWO_Tail_Invisb_Tokens or any(re.match(pattern, token) for pattern in SKIP_PATTERNS):
514
+ # ** tokens no need render, just skip
515
+ # print('skip', idx, token)
516
+ # TODO special case :[], could be single, or in \sqrt[]{}.
517
+ if (token == "[" and l_split[idx-1]!="\\sqrt") or (token == "]" and idx>=3 and l_split[idx-3]!="\\sqrt"):
518
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
519
+ l_split = l_split[:idx] + [color_token + l_split[idx] + "}"] + l_split[idx+1:]
520
+ token_list.append(token)
521
+ next_idx = idx + 1
522
+ else:
523
+ next_idx = idx + 1
524
+ else:
525
+ # ** nomal token
526
+ if brace_color or (idx > 1 and l_split[idx-1] == "_"):
527
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
528
+ l_split = l_split[:idx] + ["{" + color_token + l_split[idx] + "}}"] + l_split[idx+1:]
529
+ token_list.append(token)
530
+ next_idx = idx + 1
531
+ else:
532
+ color_token = "\\mathcolor[RGB]{<color_<idx>>}{".replace("<idx>", str(len(token_list)))
533
+ l_split = l_split[:idx] + [color_token + l_split[idx] + "}"] + l_split[idx+1:]
534
+ token_list.append(token)
535
+ next_idx = idx + 1
536
+ return l_split, next_idx, token_list
modules/latex_render_percentage.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import os
3
+ import json
4
+ import time
5
+ import shutil
6
+ import random
7
+ import argparse
8
+ import subprocess
9
+ import numpy as np
10
+
11
+ from tqdm import tqdm
12
+ from multiprocessing import Pool
13
+
14
+
15
+ formular_template = r"""
16
+ \documentclass[12pt]{article}
17
+ \usepackage[landscape]{geometry}
18
+ \usepackage{geometry}
19
+ \geometry{a5paper,scale=0.98}
20
+ \pagestyle{empty}
21
+ \usepackage{booktabs}
22
+ \usepackage{amsmath}
23
+ \usepackage{amssymb}
24
+ \usepackage{xcolor}
25
+ \begin{document}
26
+ \makeatletter
27
+ \renewcommand*{\@textcolor}[3]{%%
28
+ \protect\leavevmode
29
+ \begingroup
30
+ \color#1{#2}#3%%
31
+ \endgroup
32
+ }
33
+ \makeatother
34
+ \begin{displaymath}
35
+ %s
36
+ \end{displaymath}
37
+ \end{document}
38
+ """
39
+
40
+ def run_shell_cmd(cmd, max_time=15):
41
+ child = subprocess.Popen(cmd, shell=True)
42
+ for i in range(max_time):
43
+ if child.poll():
44
+ return True
45
+ if i == max_time-1:
46
+ child.kill()
47
+ return False
48
+ time.sleep(1)
49
+ return False
50
+
51
+ def render_latex(latex_code, basename, latex_dir, pdf_dir):
52
+ latex_path = os.path.join(latex_dir, basename + ".tex")
53
+ pdf_path = os.path.join(pdf_dir, basename + ".pdf")
54
+ with open(latex_path, "w") as f:
55
+ f.write(formular_template % latex_code)
56
+ cmd = f"pdflatex -interaction=nonstopmode -output-directory={pdf_dir} -output-format=pdf {latex_path} >/dev/null"
57
+ run_shell_cmd(cmd)
58
+ return pdf_path
59
+
60
+
61
+ if __name__ == "__main__":
62
+ parser = argparse.ArgumentParser()
63
+ parser.add_argument('--input', '-i', type=str, default='data/pred_results/test.json')
64
+ parser.add_argument('--clean', action='store_true', default=False)
65
+ parser.add_argument('--gt', action='store_true', default=False)
66
+ args = parser.parse_args()
67
+
68
+ if args.gt:
69
+ output_path = os.path.join("output", 'gt.json')
70
+ load_key = 'gt'
71
+ else:
72
+ load_key = 'pred'
73
+ output_path = os.path.join("output", os.path.basename(args.input))
74
+
75
+
76
+ temp_dir=f"render_temp_dir"
77
+ try:
78
+ shutil.rmtree(temp_dir)
79
+ except:
80
+ pass
81
+ latex_dir = os.path.join(temp_dir, "texes")
82
+ pdf_dir = os.path.join(temp_dir, "pdfs")
83
+ os.makedirs(latex_dir, exist_ok=True)
84
+ os.makedirs(pdf_dir, exist_ok=True)
85
+
86
+ with open(args.input, "r") as f:
87
+ input_data = json.load(f)
88
+
89
+ myP = Pool(200)
90
+ for idx, item in enumerate(input_data):
91
+ basename = f"sample_{idx}"
92
+ myP.apply_async(render_latex, args=(item[load_key], basename, latex_dir, pdf_dir))
93
+ myP.close()
94
+ print("processing, may take some times.")
95
+ myP.join()
96
+
97
+ success_num = 0
98
+ total_num = 0
99
+ for idx, item in enumerate(input_data):
100
+ basename = f"sample_{idx}"
101
+ total_num += 1
102
+ pdf_path = os.path.join(pdf_dir, basename + ".pdf")
103
+ if os.path.exists(pdf_path):
104
+ success_num += 1
105
+ item['renderable'] = 1
106
+ else:
107
+ item['renderable'] = 0
108
+
109
+ print("total num:", total_num, "render success num:", success_num)
110
+ with open(output_path, "w") as f:
111
+ f.write(json.dumps(input_data, indent=2))
112
+ if args.clean:
113
+ try:
114
+ shutil.rmtree(temp_dir)
115
+ except:
116
+ pass
modules/tokenize_latex/preprocess_formula.js ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require('path');
2
+ var katex = require(path.join(__dirname,"third_party/katex/katex.js"))
3
+ options = require(path.join(__dirname,"third_party/katex/src/Options.js"))
4
+ var readline = require('readline');
5
+ var rl = readline.createInterface({
6
+ input: process.stdin,
7
+ output: process.stdout,
8
+ terminal: false
9
+ });
10
+
11
+
12
+ rl.on('line', function(line){
13
+ a = line
14
+ if (line[0] == "%") {
15
+ line = line.substr(1, line.length - 1);
16
+ }
17
+ line = line.split('%')[0];
18
+
19
+ line = line.split('\\~').join(' ');
20
+
21
+ for (var i = 0; i < 300; i++) {
22
+ line = line.replace(/\\>/, " ");
23
+ line = line.replace('$', ' ');
24
+ line = line.replace(/\\label{.*?}/, "");
25
+ }
26
+
27
+ if (line.indexOf("matrix") == -1 && line.indexOf("cases")==-1 &&
28
+ line.indexOf("array")==-1 && line.indexOf("begin")==-1) {
29
+ for (var i = 0; i < 300; i++) {
30
+ line = line.replace(/\\\\/, "\\,");
31
+ }
32
+ }
33
+
34
+
35
+ line = line + " "
36
+ // global_str is tokenized version (build in parser.js)
37
+ // norm_str is normalized version build by renderer below.
38
+ try {
39
+
40
+
41
+ if (process.argv[2] == "tokenize") {
42
+ var tree = katex.__parse(line, {});
43
+ console.log(global_str.replace(/\\label { .*? }/, ""));
44
+ } else {
45
+ for (var i = 0; i < 300; ++i) {
46
+ line = line.replace(/{\\rm/, "\\mathrm{");
47
+ line = line.replace(/{ \\rm/, "\\mathrm{");
48
+ line = line.replace(/\\rm{/, "\\mathrm{");
49
+ }
50
+
51
+ var tree = katex.__parse(line, {});
52
+ buildExpression(tree, new options({}));
53
+ for (var i = 0; i < 300; ++i) {
54
+ norm_str = norm_str.replace('SSSSSS', '$');
55
+ norm_str = norm_str.replace(' S S S S S S', '$');
56
+ }
57
+ console.log(norm_str.replace(/\\label { .*? }/, ""));
58
+ }
59
+ } catch (e) {
60
+ console.error(line);
61
+ console.error(norm_str);
62
+ console.error(e);
63
+ console.log();
64
+ }
65
+ global_str = ""
66
+ norm_str = ""
67
+ })
68
+
69
+
70
+
71
+ // This is a LaTeX AST to LaTeX Renderer (modified version of KaTeX AST-> MathML).
72
+ norm_str = ""
73
+
74
+ var groupTypes = {};
75
+
76
+ groupTypes.mathord = function(group, options) {
77
+ if (options.font == "mathrm"){
78
+ for (i = 0; i < group.value.length; ++i ) {
79
+ if (group.value[i] == " ") {
80
+ norm_str = norm_str + group.value[i] + "\; ";
81
+ } else {
82
+ norm_str = norm_str + group.value[i] + " ";
83
+ }
84
+ }
85
+ } else {
86
+ norm_str = norm_str + group.value + " ";
87
+ }
88
+ };
89
+
90
+ groupTypes.textord = function(group, options) {
91
+ norm_str = norm_str + group.value + " ";
92
+ };
93
+
94
+ groupTypes.bin = function(group) {
95
+ norm_str = norm_str + group.value + " ";
96
+ };
97
+
98
+ groupTypes.rel = function(group) {
99
+ norm_str = norm_str + group.value + " ";
100
+ };
101
+
102
+ groupTypes.open = function(group) {
103
+ norm_str = norm_str + group.value + " ";
104
+ };
105
+
106
+ groupTypes.close = function(group) {
107
+ norm_str = norm_str + group.value + " ";
108
+ };
109
+
110
+ groupTypes.inner = function(group) {
111
+ norm_str = norm_str + group.value + " ";
112
+ };
113
+
114
+ groupTypes.punct = function(group) {
115
+ norm_str = norm_str + group.value + " ";
116
+ };
117
+
118
+ groupTypes.ordgroup = function(group, options) {
119
+ norm_str = norm_str + "{ ";
120
+
121
+ buildExpression(group.value, options);
122
+
123
+ norm_str = norm_str + "} ";
124
+ };
125
+
126
+ groupTypes.text = function(group, options) {
127
+
128
+ norm_str = norm_str + "\\mathrm { ";
129
+
130
+ buildExpression(group.value.body, options);
131
+ norm_str = norm_str + "} ";
132
+ };
133
+
134
+ groupTypes.color = function(group, options) {
135
+ var inner = buildExpression(group.value.value, options);
136
+
137
+ var node = new mathMLTree.MathNode("mstyle", inner);
138
+
139
+ node.setAttribute("mathcolor", group.value.color);
140
+
141
+ return node;
142
+ };
143
+
144
+ groupTypes.supsub = function(group, options) {
145
+ buildGroup(group.value.base, options);
146
+
147
+ if (group.value.sub) {
148
+ norm_str = norm_str + "_ ";
149
+ if (group.value.sub.type != 'ordgroup') {
150
+ norm_str = norm_str + " { ";
151
+ buildGroup(group.value.sub, options);
152
+ norm_str = norm_str + "} ";
153
+ } else {
154
+ buildGroup(group.value.sub, options);
155
+ }
156
+
157
+ }
158
+
159
+ if (group.value.sup) {
160
+ norm_str = norm_str + "^ ";
161
+ if (group.value.sup.type != 'ordgroup') {
162
+ norm_str = norm_str + " { ";
163
+ buildGroup(group.value.sup, options);
164
+ norm_str = norm_str + "} ";
165
+ } else {
166
+ buildGroup(group.value.sup, options);
167
+ }
168
+ }
169
+
170
+ };
171
+
172
+ groupTypes.genfrac = function(group, options) {
173
+ if (!group.value.hasBarLine) {
174
+ norm_str = norm_str + "\\binom ";
175
+ } else {
176
+ norm_str = norm_str + "\\frac ";
177
+ }
178
+ buildGroup(group.value.numer, options);
179
+ buildGroup(group.value.denom, options);
180
+
181
+ };
182
+
183
+ groupTypes.array = function(group, options) {
184
+ norm_str = norm_str + "\\begin{array} { ";
185
+ if (group.value.cols) {
186
+ group.value.cols.map(function(start) {
187
+ if (start && start.align) {
188
+ norm_str = norm_str + start.align + " ";}});
189
+ } else {
190
+ group.value.body[0].map(function(start) {
191
+ norm_str = norm_str + "l ";
192
+ } );
193
+ }
194
+ norm_str = norm_str + "} ";
195
+ group.value.body.map(function(row) {
196
+ if (row.some(cell => cell.value.length > 0)) { // orginal code: if (row[0].value.length > 0)
197
+ out = row.map(function(cell) {
198
+ buildGroup(cell, options);
199
+ if (norm_str.length > 4
200
+ && norm_str.substring(norm_str.length-4, norm_str.length) == "{ } ") {
201
+ norm_str = norm_str.substring(0, norm_str.length-4) ;
202
+ }
203
+ norm_str = norm_str + "& ";
204
+ });
205
+ norm_str = norm_str.substring(0, norm_str.length-2) + "\\\\ ";
206
+ }
207
+ });
208
+ norm_str = norm_str + "\\end{array} ";
209
+ };
210
+
211
+ groupTypes.sqrt = function(group, options) {
212
+ var node;
213
+ if (group.value.index) {
214
+ norm_str = norm_str + "\\sqrt [ ";
215
+ buildExpression(group.value.index.value, options);
216
+ norm_str = norm_str + "] ";
217
+ buildGroup(group.value.body, options);
218
+ } else {
219
+ norm_str = norm_str + "\\sqrt ";
220
+ buildGroup(group.value.body, options);
221
+ }
222
+ };
223
+
224
+ groupTypes.leftright = function(group, options) {
225
+
226
+
227
+
228
+ norm_str = norm_str + "\\left" + group.value.left + " ";
229
+ buildExpression(group.value.body, options);
230
+ norm_str = norm_str + "\\right" + group.value.right + " ";
231
+ };
232
+
233
+ groupTypes.accent = function(group, options) {
234
+ if (group.value.base.type != 'ordgroup') {
235
+ norm_str = norm_str + group.value.accent + " { ";
236
+ buildGroup(group.value.base, options);
237
+ norm_str = norm_str + "} ";
238
+ } else {
239
+ norm_str = norm_str + group.value.accent + " ";
240
+ buildGroup(group.value.base, options);
241
+ }
242
+ };
243
+
244
+ groupTypes.spacing = function(group) {
245
+ var node;
246
+ if (group.value == " ") {
247
+ norm_str = norm_str + "~ ";
248
+ } else {
249
+ norm_str = norm_str + group.value + " ";
250
+ }
251
+ return node;
252
+ };
253
+
254
+ groupTypes.op = function(group) {
255
+ var node;
256
+
257
+ // TODO(emily): handle big operators using the `largeop` attribute
258
+
259
+
260
+ if (group.value.symbol) {
261
+ // This is a symbol. Just add the symbol.
262
+ norm_str = norm_str + group.value.body + " ";
263
+
264
+ } else {
265
+ if (group.value.limits == false) {
266
+ norm_str = norm_str + "\\\operatorname { ";
267
+ } else {
268
+ norm_str = norm_str + "\\\operatorname* { ";
269
+ }
270
+ for (i = 1; i < group.value.body.length; ++i ) {
271
+ norm_str = norm_str + group.value.body[i] + " ";
272
+ }
273
+ norm_str = norm_str + "} ";
274
+ }
275
+ };
276
+
277
+ groupTypes.katex = function(group) {
278
+ var node = new mathMLTree.MathNode(
279
+ "mtext", [new mathMLTree.TextNode("KaTeX")]);
280
+
281
+ return node;
282
+ };
283
+
284
+
285
+
286
+ groupTypes.font = function(group, options) {
287
+ var font = group.value.font;
288
+ if (font == "mbox" || font == "hbox") {
289
+ font = "mathrm";
290
+ }
291
+ norm_str = norm_str + "\\" + font + " ";
292
+ buildGroup(group.value.body, options.withFont(font));
293
+ };
294
+
295
+ groupTypes.delimsizing = function(group) {
296
+ var children = [];
297
+ norm_str = norm_str + group.value.funcName + " " + group.value.value + " ";
298
+ };
299
+
300
+ groupTypes.styling = function(group, options) {
301
+ norm_str = norm_str + " " + group.value.original + " ";
302
+ buildExpression(group.value.value, options);
303
+
304
+ };
305
+
306
+ groupTypes.sizing = function(group, options) {
307
+
308
+ if (group.value.original == "\\rm") {
309
+ norm_str = norm_str + "\\mathrm { ";
310
+ buildExpression(group.value.value, options.withFont("mathrm"));
311
+ norm_str = norm_str + "} ";
312
+ } else {
313
+ norm_str = norm_str + " " + group.value.original + " ";
314
+ buildExpression(group.value.value, options);
315
+ }
316
+ };
317
+
318
+ groupTypes.overline = function(group, options) {
319
+ norm_str = norm_str + "\\overline { ";
320
+
321
+ buildGroup(group.value.body, options);
322
+ norm_str = norm_str + "} ";
323
+ norm_str = norm_str;
324
+
325
+ };
326
+
327
+ groupTypes.underline = function(group, options) {
328
+ norm_str = norm_str + "\\underline { ";
329
+ buildGroup(group.value.body, options);
330
+ norm_str = norm_str + "} ";
331
+
332
+ norm_str = norm_str;
333
+
334
+ };
335
+
336
+ groupTypes.rule = function(group) {
337
+ norm_str = norm_str + "\\rule { "+group.value.width.number+" "+group.value.width.unit+" } { "+group.value.height.number+" "+group.value.height.unit+ " } ";
338
+
339
+ };
340
+
341
+ groupTypes.llap = function(group, options) {
342
+ norm_str = norm_str + "\\llap ";
343
+ buildGroup(group.value.body, options);
344
+ };
345
+
346
+ groupTypes.rlap = function(group, options) {
347
+ norm_str = norm_str + "\\rlap ";
348
+ buildGroup(group.value.body, options);
349
+
350
+ };
351
+
352
+ groupTypes.phantom = function(group, options, prev) {
353
+ norm_str = norm_str + "\\phantom { ";
354
+ buildExpression(group.value.value, options);
355
+ norm_str = norm_str + "} ";
356
+
357
+ };
358
+
359
+ /**
360
+ * Takes a list of nodes, builds them, and returns a list of the generated
361
+ * MathML nodes. A little simpler than the HTML version because we don't do any
362
+ * previous-node handling.
363
+ */
364
+ var buildExpression = function(expression, options) {
365
+ var groups = [];
366
+ for (var i = 0; i < expression.length; i++) {
367
+ var group = expression[i];
368
+ buildGroup(group, options);
369
+ }
370
+ // console.log(norm_str);
371
+ // return groups;
372
+ };
373
+
374
+ /**
375
+ * Takes a group from the parser and calls the appropriate groupTypes function
376
+ * on it to produce a MathML node.
377
+ */
378
+ var buildGroup = function(group, options) {
379
+ if (groupTypes[group.type]) {
380
+ groupTypes[group.type](group, options);
381
+ } else {
382
+ throw new ParseError(
383
+ "Got group of unknown type: '" + group.type + "'");
384
+ }
385
+ };
386
+
387
+
modules/tokenize_latex/preprocess_tabular.js ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require('path');
2
+ var katex = require(path.join(__dirname,"third_party/katex/katex.js"))
3
+ options = require(path.join(__dirname,"third_party/katex/src/Options.js"))
4
+ var readline = require('readline');
5
+ var rl = readline.createInterface({
6
+ input: process.stdin,
7
+ output: process.stdout,
8
+ terminal: false
9
+ });
10
+
11
+
12
+ rl.on('line', function(line){
13
+ a = line
14
+ if (line[0] == "%") {
15
+ line = line.substr(1, line.length - 1);
16
+ }
17
+ // line = line.split('%')[0];
18
+
19
+ line = line.split('\\~').join(' ');
20
+
21
+ for (var i = 0; i < 300; i++) {
22
+ line = line.replace(/\\>/, " ");
23
+ // line = line.replace('$', ' ');
24
+ line = line.replace(/\\label{.*?}/, "");
25
+ }
26
+
27
+ if (line.indexOf("matrix") == -1 && line.indexOf("cases")==-1 &&
28
+ line.indexOf("array")==-1 && line.indexOf("begin")==-1) {
29
+ for (var i = 0; i < 300; i++) {
30
+ line = line.replace(/\\\\/, "\\,");
31
+ }
32
+ }
33
+
34
+
35
+ line = line + " "
36
+ // global_str is tokenized version (build in parser.js)
37
+ // norm_str is normalized version build by renderer below.
38
+ try {
39
+
40
+
41
+ if (process.argv[2] == "tokenize") {
42
+ var tree = katex.__parse(line, {});
43
+ console.log(global_str.replace(/\\label { .*? }/, ""));
44
+ } else {
45
+ for (var i = 0; i < 300; ++i) {
46
+ line = line.replace(/{\\rm/, "\\mathrm{");
47
+ line = line.replace(/{ \\rm/, "\\mathrm{");
48
+ line = line.replace(/\\rm{/, "\\mathrm{");
49
+ }
50
+
51
+ var tree = katex.__parse(line, {});
52
+ buildExpression(tree, new options({}));
53
+ for (var i = 0; i < 300; ++i) {
54
+ norm_str = norm_str.replace('SSSSSS', '$');
55
+ norm_str = norm_str.replace(' S S S S S S', '$');
56
+ }
57
+ console.log(norm_str.replace(/\\label { .*? }/, ""));
58
+ }
59
+ } catch (e) {
60
+ console.error(line);
61
+ console.error(norm_str);
62
+ console.error(e);
63
+ console.log("");
64
+ }
65
+ global_str = ""
66
+ norm_str = ""
67
+ })
68
+
69
+
70
+
71
+ // This is a LaTeX AST to LaTeX Renderer (modified version of KaTeX AST-> MathML).
72
+ norm_str = ""
73
+
74
+ var groupTypes = {};
75
+
76
+ groupTypes.mathord = function(group, options) {
77
+ if (options.font == "mathrm"){
78
+ for (i = 0; i < group.value.length; ++i ) {
79
+ if (group.value[i] == " ") {
80
+ norm_str = norm_str + group.value[i] + "\; ";
81
+ } else {
82
+ norm_str = norm_str + group.value[i] + " ";
83
+ }
84
+ }
85
+ } else {
86
+ norm_str = norm_str + group.value + " ";
87
+ }
88
+ };
89
+
90
+ groupTypes.textord = function(group, options) {
91
+ norm_str = norm_str + group.value + " ";
92
+ };
93
+
94
+ groupTypes.bin = function(group) {
95
+ norm_str = norm_str + group.value + " ";
96
+ };
97
+
98
+ groupTypes.rel = function(group) {
99
+ norm_str = norm_str + group.value + " ";
100
+ };
101
+
102
+ groupTypes.open = function(group) {
103
+ norm_str = norm_str + group.value + " ";
104
+ };
105
+
106
+ groupTypes.close = function(group) {
107
+ norm_str = norm_str + group.value + " ";
108
+ };
109
+
110
+ groupTypes.inner = function(group) {
111
+ norm_str = norm_str + group.value + " ";
112
+ };
113
+
114
+ groupTypes.punct = function(group) {
115
+ norm_str = norm_str + group.value + " ";
116
+ };
117
+
118
+ groupTypes.ordgroup = function(group, options) {
119
+ norm_str = norm_str + "{ ";
120
+
121
+ buildExpression(group.value, options);
122
+
123
+ norm_str = norm_str + "} ";
124
+ };
125
+
126
+ groupTypes.text = function(group, options) {
127
+
128
+ norm_str = norm_str + "\\mathrm { ";
129
+
130
+ buildExpression(group.value.body, options);
131
+ norm_str = norm_str + "} ";
132
+ };
133
+
134
+ groupTypes.color = function(group, options) {
135
+ var inner = buildExpression(group.value.value, options);
136
+
137
+ var node = new mathMLTree.MathNode("mstyle", inner);
138
+
139
+ node.setAttribute("mathcolor", group.value.color);
140
+
141
+ return node;
142
+ };
143
+
144
+ groupTypes.supsub = function(group, options) {
145
+ buildGroup(group.value.base, options);
146
+
147
+ if (group.value.sub) {
148
+ norm_str = norm_str + "_ ";
149
+ if (group.value.sub.type != 'ordgroup') {
150
+ norm_str = norm_str + " { ";
151
+ buildGroup(group.value.sub, options);
152
+ norm_str = norm_str + "} ";
153
+ } else {
154
+ buildGroup(group.value.sub, options);
155
+ }
156
+
157
+ }
158
+
159
+ if (group.value.sup) {
160
+ norm_str = norm_str + "^ ";
161
+ if (group.value.sup.type != 'ordgroup') {
162
+ norm_str = norm_str + " { ";
163
+ buildGroup(group.value.sup, options);
164
+ norm_str = norm_str + "} ";
165
+ } else {
166
+ buildGroup(group.value.sup, options);
167
+ }
168
+ }
169
+
170
+ };
171
+
172
+ groupTypes.genfrac = function(group, options) {
173
+ if (!group.value.hasBarLine) {
174
+ norm_str = norm_str + "\\binom ";
175
+ } else {
176
+ norm_str = norm_str + "\\frac ";
177
+ }
178
+ buildGroup(group.value.numer, options);
179
+ buildGroup(group.value.denom, options);
180
+
181
+ };
182
+
183
+ groupTypes.array = function(group, options) {
184
+ norm_str = norm_str + "\\begin{" + group.value.style + "} ";
185
+
186
+ if (group.value.style == "array" || group.value.style == "tabular" || group.value.style == "tabularx") {
187
+ norm_str = norm_str + "{ ";
188
+ if (group.value.cols) {
189
+ group.value.cols.map(function(start) {
190
+ if (start) {
191
+ if (start.type == "align") {
192
+ norm_str = norm_str + start.align + " ";
193
+ } else if (start.type == "separator") {
194
+ norm_str = norm_str + start.separator + " ";
195
+ }
196
+ }
197
+ });
198
+ } else {
199
+ group.value.body[0].map(function(start) {
200
+ norm_str = norm_str + "c ";
201
+ } );
202
+ }
203
+ norm_str = norm_str + "} ";
204
+ }
205
+ group.value.body.map(function(row) {
206
+ if (row.length > 1 || row[0].value.length > 0) {
207
+ if (row[0].value[0] && row[0].value[0].value == "\\hline") {
208
+ norm_str = norm_str + "\\hline ";
209
+ row[0].value = row[0].value.slice(1);
210
+ }
211
+ out = row.map(function(cell) {
212
+ buildGroup(cell, options);
213
+ norm_str = norm_str + "& ";
214
+ });
215
+ norm_str = norm_str.substring(0, norm_str.length-2) + "\\\\ ";
216
+ }
217
+ });
218
+ norm_str = norm_str + "\\end{" + group.value.style + "} ";
219
+ };
220
+
221
+ groupTypes.sqrt = function(group, options) {
222
+ var node;
223
+ if (group.value.index) {
224
+ norm_str = norm_str + "\\sqrt [ " + group.value.index + " ] ";
225
+ buildGroup(group.value.body, options);
226
+ } else {
227
+ norm_str = norm_str + "\\sqrt ";
228
+ buildGroup(group.value.body, options);
229
+ }
230
+ };
231
+
232
+ groupTypes.leftright = function(group, options) {
233
+
234
+
235
+
236
+ norm_str = norm_str + "\\left" + group.value.left + " ";
237
+ buildExpression(group.value.body, options);
238
+ norm_str = norm_str + "\\right" + group.value.right + " ";
239
+ };
240
+
241
+ groupTypes.accent = function(group, options) {
242
+ if (group.value.base.type != 'ordgroup') {
243
+ norm_str = norm_str + group.value.accent + " { ";
244
+ buildGroup(group.value.base, options);
245
+ norm_str = norm_str + "} ";
246
+ } else {
247
+ norm_str = norm_str + group.value.accent + " ";
248
+ buildGroup(group.value.base, options);
249
+ }
250
+ };
251
+
252
+ groupTypes.spacing = function(group) {
253
+ var node;
254
+ if (group.value == " ") {
255
+ norm_str = norm_str + "~ ";
256
+ } else {
257
+ norm_str = norm_str + group.value + " ";
258
+ }
259
+ return node;
260
+ };
261
+
262
+ groupTypes.op = function(group) {
263
+ var node;
264
+
265
+ // TODO(emily): handle big operators using the `largeop` attribute
266
+
267
+
268
+ if (group.value.symbol) {
269
+ // This is a symbol. Just add the symbol.
270
+ norm_str = norm_str + group.value.body + " ";
271
+
272
+ } else {
273
+ if (group.value.limits == false) {
274
+ norm_str = norm_str + "\\\operatorname { ";
275
+ } else {
276
+ norm_str = norm_str + "\\\operatorname* { ";
277
+ }
278
+ for (i = 1; i < group.value.body.length; ++i ) {
279
+ norm_str = norm_str + group.value.body[i] + " ";
280
+ }
281
+ norm_str = norm_str + "} ";
282
+ }
283
+ };
284
+
285
+ groupTypes.katex = function(group) {
286
+ var node = new mathMLTree.MathNode(
287
+ "mtext", [new mathMLTree.TextNode("KaTeX")]);
288
+
289
+ return node;
290
+ };
291
+
292
+
293
+
294
+ groupTypes.font = function(group, options) {
295
+ var font = group.value.font;
296
+ if (font == "mbox" || font == "hbox") {
297
+ font = "mathrm";
298
+ }
299
+ norm_str = norm_str + "\\" + font + " ";
300
+ buildGroup(group.value.body, options.withFont(font));
301
+ };
302
+
303
+ groupTypes.delimsizing = function(group) {
304
+ var children = [];
305
+ norm_str = norm_str + group.value.funcName + " " + group.value.value + " ";
306
+ };
307
+
308
+ groupTypes.styling = function(group, options) {
309
+ norm_str = norm_str + " " + group.value.original + " ";
310
+ buildExpression(group.value.value, options);
311
+
312
+ };
313
+
314
+ groupTypes.sizing = function(group, options) {
315
+
316
+ if (group.value.original == "\\rm") {
317
+ norm_str = norm_str + "\\mathrm { ";
318
+ buildExpression(group.value.value, options.withFont("mathrm"));
319
+ norm_str = norm_str + "} ";
320
+ } else {
321
+ norm_str = norm_str + " " + group.value.original + " ";
322
+ buildExpression(group.value.value, options);
323
+ }
324
+ };
325
+
326
+ groupTypes.overline = function(group, options) {
327
+ norm_str = norm_str + "\\overline { ";
328
+
329
+ buildGroup(group.value.body, options);
330
+ norm_str = norm_str + "} ";
331
+ norm_str = norm_str;
332
+
333
+ };
334
+
335
+ groupTypes.underline = function(group, options) {
336
+ norm_str = norm_str + "\\underline { ";
337
+ buildGroup(group.value.body, options);
338
+ norm_str = norm_str + "} ";
339
+
340
+ norm_str = norm_str;
341
+
342
+ };
343
+
344
+ groupTypes.rule = function(group) {
345
+ norm_str = norm_str + "\\rule { "+group.value.width.number+" "+group.value.width.unit+" } { "+group.value.height.number+" "+group.value.height.unit+ " } ";
346
+
347
+ };
348
+
349
+ groupTypes.llap = function(group, options) {
350
+ norm_str = norm_str + "\\llap ";
351
+ buildGroup(group.value.body, options);
352
+ };
353
+
354
+ groupTypes.rlap = function(group, options) {
355
+ norm_str = norm_str + "\\rlap ";
356
+ buildGroup(group.value.body, options);
357
+
358
+ };
359
+
360
+ groupTypes.phantom = function(group, options, prev) {
361
+ norm_str = norm_str + "\\phantom { ";
362
+ buildExpression(group.value.value, options);
363
+ norm_str = norm_str + "} ";
364
+
365
+ };
366
+
367
+ /**
368
+ * Takes a list of nodes, builds them, and returns a list of the generated
369
+ * MathML nodes. A little simpler than the HTML version because we don't do any
370
+ * previous-node handling.
371
+ */
372
+ var buildExpression = function(expression, options) {
373
+ var groups = [];
374
+ for (var i = 0; i < expression.length; i++) {
375
+ var group = expression[i];
376
+ buildGroup(group, options);
377
+ }
378
+ // console.log(norm_str);
379
+ // return groups;
380
+ };
381
+
382
+ /**
383
+ * Takes a group from the parser and calls the appropriate groupTypes function
384
+ * on it to produce a MathML node.
385
+ */
386
+ var buildGroup = function(group, options) {
387
+ if (groupTypes[group.type]) {
388
+ groupTypes[group.type](group, options);
389
+ } else {
390
+ throw new ParseError(
391
+ "Got group of unknown type: '" + group.type + "'");
392
+ }
393
+ };
394
+
395
+
modules/tokenize_latex/third_party/README.md ADDED
@@ -0,0 +1 @@
 
 
1
+ Directly taken from https://github.com/harvardnlp/im2markup
modules/tokenize_latex/third_party/katex/LICENSE.txt ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Khan Academy
4
+
5
+ This software also uses portions of the underscore.js project, which is
6
+ MIT licensed with the following copyright:
7
+
8
+ Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
9
+ Reporters & Editors
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
modules/tokenize_latex/third_party/katex/README.md ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # [<img src="https://khan.github.io/KaTeX/katex-logo.svg" width="130" alt="KaTeX">](https://khan.github.io/KaTeX/) [![Build Status](https://travis-ci.org/Khan/KaTeX.svg?branch=master)](https://travis-ci.org/Khan/KaTeX)
2
+
3
+ [![Join the chat at https://gitter.im/Khan/KaTeX](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Khan/KaTeX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
+
5
+ KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
6
+
7
+ * **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://jsperf.com/katex-vs-mathjax/).
8
+ * **Print quality:** KaTeX’s layout is based on Donald Knuth’s TeX, the gold standard for math typesetting.
9
+ * **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
10
+ * **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
11
+
12
+ KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, and IE 8 - IE 11. A list of supported commands can be on the [wiki](https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX).
13
+
14
+ ## Usage
15
+
16
+ You can [download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN:
17
+
18
+ ```html
19
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css">
20
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.js"></script>
21
+ ```
22
+
23
+ #### In-browser rendering
24
+
25
+ Call `katex.render` with a TeX expression and a DOM element to render into:
26
+
27
+ ```js
28
+ katex.render("c = \\pm\\sqrt{a^2 + b^2}", element);
29
+ ```
30
+
31
+ If KaTeX can't parse the expression, it throws a `katex.ParseError` error.
32
+
33
+ #### Server side rendering or rendering to a string
34
+
35
+ To generate HTML on the server or to generate an HTML string of the rendered math, you can use `katex.renderToString`:
36
+
37
+ ```js
38
+ var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}");
39
+ // '<span class="katex">...</span>'
40
+ ```
41
+
42
+ Make sure to include the CSS and font files, but there is no need to include the JavaScript. Like `render`, `renderToString` throws if it can't parse the expression.
43
+
44
+ #### Rendering options
45
+
46
+ You can provide an object of options as the last argument to `katex.render` and `katex.renderToString`. Available options are:
47
+
48
+ - `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`)
49
+ - `throwOnError`: `boolean`. If `true`, KaTeX will throw a `ParseError` when it encounters an unsupported command. If `false`, KaTeX will render the unsupported command as text in the color given by `errorColor`. (default: `true`)
50
+ - `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color which unsupported commands are rendered in. (default: `#cc0000`)
51
+
52
+ For example:
53
+
54
+ ```js
55
+ katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, { displayMode: true });
56
+ ```
57
+
58
+ #### Automatic rendering of math on a page
59
+
60
+ Math on the page can be automatically rendered using the auto-render extension. See [the Auto-render README](contrib/auto-render/README.md) for more information.
61
+
62
+ ## Contributing
63
+
64
+ See [CONTRIBUTING.md](CONTRIBUTING.md)
65
+
66
+ ## License
67
+
68
+ KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).
modules/tokenize_latex/third_party/katex/cli.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // Simple CLI for KaTeX.
3
+ // Reads TeX from stdin, outputs HTML to stdout.
4
+ /* eslint no-console:0 */
5
+
6
+ var katex = require("./");
7
+ var input = "";
8
+
9
+ // Skip the first two args, which are just "node" and "cli.js"
10
+ var args = process.argv.slice(2);
11
+
12
+ if (args.indexOf("--help") !== -1) {
13
+ console.log(process.argv[0] + " " + process.argv[1] +
14
+ " [ --help ]" +
15
+ " [ --display-mode ]");
16
+
17
+ console.log("\n" +
18
+ "Options:");
19
+ console.log(" --help Display this help message");
20
+ console.log(" --display-mode Render in display mode (not inline mode)");
21
+ process.exit();
22
+ }
23
+
24
+ process.stdin.on("data", function(chunk) {
25
+ input += chunk.toString();
26
+ });
27
+
28
+ process.stdin.on("end", function() {
29
+ var options = { displayMode: args.indexOf("--display-mode") !== -1 };
30
+ var output = katex.renderToString(input, options);
31
+ console.log(output);
32
+ });
modules/tokenize_latex/third_party/katex/katex.js ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint no-console:0 */
2
+ /**
3
+ * This is the main entry point for KaTeX. Here, we expose functions for
4
+ * rendering expressions either to DOM nodes or to markup strings.
5
+ *
6
+ * We also expose the ParseError class to check if errors thrown from KaTeX are
7
+ * errors in the expression, or errors in javascript handling.
8
+ */
9
+
10
+ var ParseError = require("./src/ParseError");
11
+ var Settings = require("./src/Settings");
12
+
13
+ var buildTree = require("./src/buildTree");
14
+ var parseTree = require("./src/parseTree");
15
+ var utils = require("./src/utils");
16
+
17
+ /**
18
+ * Parse and build an expression, and place that expression in the DOM node
19
+ * given.
20
+ */
21
+ var render = function(expression, baseNode, options) {
22
+ utils.clearNode(baseNode);
23
+
24
+ var settings = new Settings(options);
25
+
26
+ var tree = parseTree(expression, settings);
27
+ var node = buildTree(tree, expression, settings).toNode();
28
+
29
+ baseNode.appendChild(node);
30
+ };
31
+
32
+ // KaTeX's styles don't work properly in quirks mode. Print out an error, and
33
+ // disable rendering.
34
+ if (typeof document !== "undefined") {
35
+ if (document.compatMode !== "CSS1Compat") {
36
+ typeof console !== "undefined" && console.warn(
37
+ "Warning: KaTeX doesn't work in quirks mode. Make sure your " +
38
+ "website has a suitable doctype.");
39
+
40
+ render = function() {
41
+ throw new ParseError("KaTeX doesn't work in quirks mode.");
42
+ };
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Parse and build an expression, and return the markup for that.
48
+ */
49
+ var renderToString = function(expression, options) {
50
+ var settings = new Settings(options);
51
+
52
+ var tree = parseTree(expression, settings);
53
+ return buildTree(tree, expression, settings).toMarkup();
54
+ };
55
+
56
+ /**
57
+ * Parse an expression and return the parse tree.
58
+ */
59
+ var generateParseTree = function(expression, options) {
60
+ var settings = new Settings(options);
61
+ return parseTree(expression, settings);
62
+ };
63
+
64
+ module.exports = {
65
+ render: render,
66
+ renderToString: renderToString,
67
+ /**
68
+ * NOTE: This method is not currently recommended for public use.
69
+ * The internal tree representation is unstable and is very likely
70
+ * to change. Use at your own risk.
71
+ */
72
+ __parse: generateParseTree,
73
+ ParseError: ParseError,
74
+ };
modules/tokenize_latex/third_party/katex/package.json ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_args": [
3
+ [
4
+ "katex",
5
+ "/home/srush/Projects/im2latex"
6
+ ]
7
+ ],
8
+ "_from": "katex@latest",
9
+ "_id": "katex@0.6.0",
10
+ "_inCache": true,
11
+ "_installable": true,
12
+ "_location": "/katex",
13
+ "_nodeVersion": "4.2.1",
14
+ "_npmOperationalInternal": {
15
+ "host": "packages-12-west.internal.npmjs.com",
16
+ "tmp": "tmp/katex-0.6.0.tgz_1460769444991_0.38667152682319283"
17
+ },
18
+ "_npmUser": {
19
+ "email": "kevinb7@gmail.com",
20
+ "name": "kevinbarabash"
21
+ },
22
+ "_npmVersion": "2.15.2",
23
+ "_phantomChildren": {},
24
+ "_requested": {
25
+ "name": "katex",
26
+ "raw": "katex",
27
+ "rawSpec": "",
28
+ "scope": null,
29
+ "spec": "latest",
30
+ "type": "tag"
31
+ },
32
+ "_requiredBy": [
33
+ "#USER"
34
+ ],
35
+ "_resolved": "https://registry.npmjs.org/katex/-/katex-0.6.0.tgz",
36
+ "_shasum": "12418e09121c05c92041b6b3b9fb6bab213cb6f3",
37
+ "_shrinkwrap": null,
38
+ "_spec": "katex",
39
+ "_where": "/home/srush/Projects/im2latex",
40
+ "bin": {
41
+ "katex": "cli.js"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/Khan/KaTeX/issues"
45
+ },
46
+ "dependencies": {
47
+ "match-at": "^0.1.0"
48
+ },
49
+ "description": "Fast math typesetting for the web.",
50
+ "devDependencies": {
51
+ "browserify": "^10.2.4",
52
+ "clean-css": "~2.2.15",
53
+ "eslint": "^1.10.2",
54
+ "express": "~3.3.3",
55
+ "glob": "^5.0.15",
56
+ "jasmine": "^2.3.2",
57
+ "jasmine-core": "^2.3.4",
58
+ "js-yaml": "^3.3.1",
59
+ "jspngopt": "^0.1.0",
60
+ "less": "~1.7.5",
61
+ "nomnom": "^1.8.1",
62
+ "pako": "0.2.7",
63
+ "selenium-webdriver": "^2.46.1",
64
+ "uglify-js": "~2.4.15"
65
+ },
66
+ "directories": {},
67
+ "dist": {
68
+ "shasum": "12418e09121c05c92041b6b3b9fb6bab213cb6f3",
69
+ "tarball": "https://registry.npmjs.org/katex/-/katex-0.6.0.tgz"
70
+ },
71
+ "files": [
72
+ "cli.js",
73
+ "dist/",
74
+ "katex.js",
75
+ "src/"
76
+ ],
77
+ "gitHead": "b94fc6534d5c23f944906a52a592bee4e0090665",
78
+ "homepage": "https://github.com/Khan/KaTeX#readme",
79
+ "license": "MIT",
80
+ "main": "katex.js",
81
+ "maintainers": [
82
+ {
83
+ "name": "kevinbarabash",
84
+ "email": "kevinb7@gmail.com"
85
+ },
86
+ {
87
+ "name": "spicyj",
88
+ "email": "ben@benalpert.com"
89
+ },
90
+ {
91
+ "name": "xymostech",
92
+ "email": "xymostech@gmail.com"
93
+ }
94
+ ],
95
+ "name": "katex",
96
+ "optionalDependencies": {},
97
+ "readme": "ERROR: No README data found!",
98
+ "repository": {
99
+ "type": "git",
100
+ "url": "git://github.com/Khan/KaTeX.git"
101
+ },
102
+ "scripts": {
103
+ "prepublish": "make dist",
104
+ "start": "node server.js",
105
+ "test": "make lint test"
106
+ },
107
+ "version": "0.6.0"
108
+ }
modules/tokenize_latex/third_party/katex/src/Lexer.js ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * The Lexer class handles tokenizing the input in various ways. Since our
3
+ * parser expects us to be able to backtrack, the lexer allows lexing from any
4
+ * given starting point.
5
+ *
6
+ * Its main exposed function is the `lex` function, which takes a position to
7
+ * lex from and a type of token to lex. It defers to the appropriate `_innerLex`
8
+ * function.
9
+ *
10
+ * The various `_innerLex` functions perform the actual lexing of different
11
+ * kinds.
12
+ */
13
+
14
+ var matchAt = require("../../match-at");
15
+
16
+ var ParseError = require("./ParseError");
17
+
18
+ // The main lexer class
19
+ function Lexer(input) {
20
+ this._input = input;
21
+ }
22
+
23
+ // The resulting token returned from `lex`.
24
+ function Token(text, data, position) {
25
+ this.text = text;
26
+ this.data = data;
27
+ this.position = position;
28
+ }
29
+
30
+ /* The following tokenRegex
31
+ * - matches typical whitespace (but not NBSP etc.) using its first group
32
+ * - matches symbol combinations which result in a single output character
33
+ * - does not match any control character \x00-\x1f except whitespace
34
+ * - does not match a bare backslash
35
+ * - matches any ASCII character except those just mentioned
36
+ * - does not match the BMP private use area \uE000-\uF8FF
37
+ * - does not match bare surrogate code units
38
+ * - matches any BMP character except for those just described
39
+ * - matches any valid Unicode surrogate pair
40
+ * - matches a backslash followed by one or more letters
41
+ * - matches a backslash followed by any BMP character, including newline
42
+ * Just because the Lexer matches something doesn't mean it's valid input:
43
+ * If there is no matching function or symbol definition, the Parser will
44
+ * still reject the input.
45
+ */
46
+ var tokenRegex = new RegExp(
47
+ "([ \r\n\t]+)|(" + // whitespace
48
+ "---?" + // special combinations
49
+ "|[!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
50
+ "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
51
+ "|\\\\(?:[a-zA-Z]+|[^\uD800-\uDFFF])" + // function name
52
+ ")"
53
+ );
54
+
55
+ var whitespaceRegex = /\s*/;
56
+
57
+ /**
58
+ * This function lexes a single normal token. It takes a position and
59
+ * whether it should completely ignore whitespace or not.
60
+ */
61
+ Lexer.prototype._innerLex = function(pos, ignoreWhitespace) {
62
+ var input = this._input;
63
+ if (pos === input.length) {
64
+ return new Token("EOF", null, pos);
65
+ }
66
+ var match = matchAt(tokenRegex, input, pos);
67
+ if (match === null) {
68
+ throw new ParseError(
69
+ "Unexpected character: '" + input[pos] + "'",
70
+ this, pos);
71
+ } else if (match[2]) { // matched non-whitespace
72
+ return new Token(match[2], null, pos + match[2].length);
73
+ } else if (ignoreWhitespace) {
74
+ return this._innerLex(pos + match[1].length, true);
75
+ } else { // concatenate whitespace to a single space
76
+ return new Token(" ", null, pos + match[1].length);
77
+ }
78
+ };
79
+
80
+ // A regex to match a CSS color (like #ffffff or BlueViolet)
81
+ var cssColor = /#[a-z0-9]+|[a-z]+/i;
82
+
83
+ /**
84
+ * This function lexes a CSS color.
85
+ */
86
+ Lexer.prototype._innerLexColor = function(pos) {
87
+ var input = this._input;
88
+
89
+ // Ignore whitespace
90
+ var whitespace = matchAt(whitespaceRegex, input, pos)[0];
91
+ pos += whitespace.length;
92
+
93
+ var match;
94
+ if ((match = matchAt(cssColor, input, pos))) {
95
+ // If we look like a color, return a color
96
+ return new Token(match[0], null, pos + match[0].length);
97
+ } else {
98
+ throw new ParseError("Invalid color", this, pos);
99
+ }
100
+ };
101
+
102
+ // A regex to match a dimension. Dimensions look like
103
+ // "1.2em" or ".4pt" or "1 ex"
104
+ var sizeRegex = /(-?)\s*(\d+(?:\.\d*)?|\.\d+)\s*([a-z]{2})/;
105
+
106
+ /**
107
+ * This function lexes a dimension.
108
+ */
109
+ Lexer.prototype._innerLexSize = function(pos) {
110
+ var input = this._input;
111
+
112
+ // Ignore whitespace
113
+ var whitespace = matchAt(whitespaceRegex, input, pos)[0];
114
+ pos += whitespace.length;
115
+
116
+ var match;
117
+ if ((match = matchAt(sizeRegex, input, pos))) {
118
+ var unit = match[3];
119
+ // We only currently handle "em" and "ex" units
120
+ // if (unit !== "em" && unit !== "ex") {
121
+ // throw new ParseError("Invalid unit: '" + unit + "'", this, pos);
122
+ // }
123
+ return new Token(match[0], {
124
+ number: +(match[1] + match[2]),
125
+ unit: unit,
126
+ }, pos + match[0].length);
127
+ }
128
+
129
+ throw new ParseError("Invalid size", this, pos);
130
+ };
131
+
132
+ /**
133
+ * This function lexes a string of whitespace.
134
+ */
135
+ Lexer.prototype._innerLexWhitespace = function(pos) {
136
+ var input = this._input;
137
+
138
+ var whitespace = matchAt(whitespaceRegex, input, pos)[0];
139
+ pos += whitespace.length;
140
+
141
+ return new Token(whitespace[0], null, pos);
142
+ };
143
+
144
+ /**
145
+ * This function lexes a single token starting at `pos` and of the given mode.
146
+ * Based on the mode, we defer to one of the `_innerLex` functions.
147
+ */
148
+ Lexer.prototype.lex = function(pos, mode) {
149
+ if (mode === "math") {
150
+ return this._innerLex(pos, true);
151
+ } else if (mode === "text") {
152
+ return this._innerLex(pos, false);
153
+ } else if (mode === "color") {
154
+ return this._innerLexColor(pos);
155
+ } else if (mode === "size") {
156
+ return this._innerLexSize(pos);
157
+ } else if (mode === "whitespace") {
158
+ return this._innerLexWhitespace(pos);
159
+ }
160
+ };
161
+
162
+ module.exports = Lexer;
modules/tokenize_latex/third_party/katex/src/Options.js ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file contains information about the options that the Parser carries
3
+ * around with it while parsing. Data is held in an `Options` object, and when
4
+ * recursing, a new `Options` object can be created with the `.with*` and
5
+ * `.reset` functions.
6
+ */
7
+
8
+ /**
9
+ * This is the main options class. It contains the style, size, color, and font
10
+ * of the current parse level. It also contains the style and size of the parent
11
+ * parse level, so size changes can be handled efficiently.
12
+ *
13
+ * Each of the `.with*` and `.reset` functions passes its current style and size
14
+ * as the parentStyle and parentSize of the new options class, so parent
15
+ * handling is taken care of automatically.
16
+ */
17
+ function Options(data) {
18
+ this.style = data.style;
19
+ this.color = data.color;
20
+ this.size = data.size;
21
+ this.phantom = data.phantom;
22
+ this.font = data.font;
23
+
24
+ if (data.parentStyle === undefined) {
25
+ this.parentStyle = data.style;
26
+ } else {
27
+ this.parentStyle = data.parentStyle;
28
+ }
29
+
30
+ if (data.parentSize === undefined) {
31
+ this.parentSize = data.size;
32
+ } else {
33
+ this.parentSize = data.parentSize;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Returns a new options object with the same properties as "this". Properties
39
+ * from "extension" will be copied to the new options object.
40
+ */
41
+ Options.prototype.extend = function(extension) {
42
+ var data = {
43
+ style: this.style,
44
+ size: this.size,
45
+ color: this.color,
46
+ parentStyle: this.style,
47
+ parentSize: this.size,
48
+ phantom: this.phantom,
49
+ font: this.font,
50
+ };
51
+
52
+ for (var key in extension) {
53
+ if (extension.hasOwnProperty(key)) {
54
+ data[key] = extension[key];
55
+ }
56
+ }
57
+
58
+ return new Options(data);
59
+ };
60
+
61
+ /**
62
+ * Create a new options object with the given style.
63
+ */
64
+ Options.prototype.withStyle = function(style) {
65
+ return this.extend({
66
+ style: style,
67
+ });
68
+ };
69
+
70
+ /**
71
+ * Create a new options object with the given size.
72
+ */
73
+ Options.prototype.withSize = function(size) {
74
+ return this.extend({
75
+ size: size,
76
+ });
77
+ };
78
+
79
+ /**
80
+ * Create a new options object with the given color.
81
+ */
82
+ Options.prototype.withColor = function(color) {
83
+ return this.extend({
84
+ color: color,
85
+ });
86
+ };
87
+
88
+ /**
89
+ * Create a new options object with "phantom" set to true.
90
+ */
91
+ Options.prototype.withPhantom = function() {
92
+ return this.extend({
93
+ phantom: true,
94
+ });
95
+ };
96
+
97
+ /**
98
+ * Create a new options objects with the give font.
99
+ */
100
+ Options.prototype.withFont = function(font) {
101
+ return this.extend({
102
+ font: font,
103
+ });
104
+ };
105
+
106
+ /**
107
+ * Create a new options object with the same style, size, and color. This is
108
+ * used so that parent style and size changes are handled correctly.
109
+ */
110
+ Options.prototype.reset = function() {
111
+ return this.extend({});
112
+ };
113
+
114
+ /**
115
+ * A map of color names to CSS colors.
116
+ * TODO(emily): Remove this when we have real macros
117
+ */
118
+ var colorMap = {
119
+ "katex-blue": "#6495ed",
120
+ "katex-orange": "#ffa500",
121
+ "katex-pink": "#ff00af",
122
+ "katex-red": "#df0030",
123
+ "katex-green": "#28ae7b",
124
+ "katex-gray": "gray",
125
+ "katex-purple": "#9d38bd",
126
+ "katex-blueA": "#c7e9f1",
127
+ "katex-blueB": "#9cdceb",
128
+ "katex-blueC": "#58c4dd",
129
+ "katex-blueD": "#29abca",
130
+ "katex-blueE": "#1c758a",
131
+ "katex-tealA": "#acead7",
132
+ "katex-tealB": "#76ddc0",
133
+ "katex-tealC": "#5cd0b3",
134
+ "katex-tealD": "#55c1a7",
135
+ "katex-tealE": "#49a88f",
136
+ "katex-greenA": "#c9e2ae",
137
+ "katex-greenB": "#a6cf8c",
138
+ "katex-greenC": "#83c167",
139
+ "katex-greenD": "#77b05d",
140
+ "katex-greenE": "#699c52",
141
+ "katex-goldA": "#f7c797",
142
+ "katex-goldB": "#f9b775",
143
+ "katex-goldC": "#f0ac5f",
144
+ "katex-goldD": "#e1a158",
145
+ "katex-goldE": "#c78d46",
146
+ "katex-redA": "#f7a1a3",
147
+ "katex-redB": "#ff8080",
148
+ "katex-redC": "#fc6255",
149
+ "katex-redD": "#e65a4c",
150
+ "katex-redE": "#cf5044",
151
+ "katex-maroonA": "#ecabc1",
152
+ "katex-maroonB": "#ec92ab",
153
+ "katex-maroonC": "#c55f73",
154
+ "katex-maroonD": "#a24d61",
155
+ "katex-maroonE": "#94424f",
156
+ "katex-purpleA": "#caa3e8",
157
+ "katex-purpleB": "#b189c6",
158
+ "katex-purpleC": "#9a72ac",
159
+ "katex-purpleD": "#715582",
160
+ "katex-purpleE": "#644172",
161
+ "katex-mintA": "#f5f9e8",
162
+ "katex-mintB": "#edf2df",
163
+ "katex-mintC": "#e0e5cc",
164
+ "katex-grayA": "#fdfdfd",
165
+ "katex-grayB": "#f7f7f7",
166
+ "katex-grayC": "#eeeeee",
167
+ "katex-grayD": "#dddddd",
168
+ "katex-grayE": "#cccccc",
169
+ "katex-grayF": "#aaaaaa",
170
+ "katex-grayG": "#999999",
171
+ "katex-grayH": "#555555",
172
+ "katex-grayI": "#333333",
173
+ "katex-kaBlue": "#314453",
174
+ "katex-kaGreen": "#639b24",
175
+ };
176
+
177
+ /**
178
+ * Gets the CSS color of the current options object, accounting for the
179
+ * `colorMap`.
180
+ */
181
+ Options.prototype.getColor = function() {
182
+ if (this.phantom) {
183
+ return "transparent";
184
+ } else {
185
+ return colorMap[this.color] || this.color;
186
+ }
187
+ };
188
+
189
+ module.exports = Options;
modules/tokenize_latex/third_party/katex/src/ParseError.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This is the ParseError class, which is the main error thrown by KaTeX
3
+ * functions when something has gone wrong. This is used to distinguish internal
4
+ * errors from errors in the expression that the user provided.
5
+ */
6
+ function ParseError(message, lexer, position) {
7
+ var error = "KaTeX parse error: " + message;
8
+
9
+ if (lexer !== undefined && position !== undefined) {
10
+ // If we have the input and a position, make the error a bit fancier
11
+
12
+ // Prepend some information
13
+ error += " at position " + position + ": ";
14
+
15
+ // Get the input
16
+ var input = lexer._input;
17
+ // Insert a combining underscore at the correct position
18
+ input = input.slice(0, position) + "\u0332" +
19
+ input.slice(position);
20
+
21
+ // Extract some context from the input and add it to the error
22
+ var begin = Math.max(0, position - 15);
23
+ var end = position + 15;
24
+ error += input.slice(begin, end);
25
+ }
26
+
27
+ // Some hackery to make ParseError a prototype of Error
28
+ // See http://stackoverflow.com/a/8460753
29
+ var self = new Error(error);
30
+ self.name = "ParseError";
31
+ self.__proto__ = ParseError.prototype;
32
+
33
+ self.position = position;
34
+ return self;
35
+ }
36
+
37
+ // More hackery
38
+ ParseError.prototype.__proto__ = Error.prototype;
39
+
40
+ module.exports = ParseError;
modules/tokenize_latex/third_party/katex/src/Parser.js ADDED
@@ -0,0 +1,798 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint no-constant-condition:0 */
2
+ var functions = require("./functions");
3
+ var environments = require("./environments");
4
+ var Lexer = require("./Lexer");
5
+ var symbols = require("./symbols");
6
+ var utils = require("./utils");
7
+
8
+ var parseData = require("./parseData");
9
+ var ParseError = require("./ParseError");
10
+
11
+ global_str = ""
12
+
13
+ /**
14
+ * This file contains the parser used to parse out a TeX expression from the
15
+ * input. Since TeX isn't context-free, standard parsers don't work particularly
16
+ * well.
17
+ *
18
+ * The strategy of this parser is as such:
19
+ *
20
+ * The main functions (the `.parse...` ones) take a position in the current
21
+ * parse string to parse tokens from. The lexer (found in Lexer.js, stored at
22
+ * this.lexer) also supports pulling out tokens at arbitrary places. When
23
+ * individual tokens are needed at a position, the lexer is called to pull out a
24
+ * token, which is then used.
25
+ *
26
+ * The parser has a property called "mode" indicating the mode that
27
+ * the parser is currently in. Currently it has to be one of "math" or
28
+ * "text", which denotes whether the current environment is a math-y
29
+ * one or a text-y one (e.g. inside \text). Currently, this serves to
30
+ * limit the functions which can be used in text mode.
31
+ *
32
+ * The main functions then return an object which contains the useful data that
33
+ * was parsed at its given point, and a new position at the end of the parsed
34
+ * data. The main functions can call each other and continue the parsing by
35
+ * using the returned position as a new starting point.
36
+ *
37
+ * There are also extra `.handle...` functions, which pull out some reused
38
+ * functionality into self-contained functions.
39
+ *
40
+ * The earlier functions return ParseNodes.
41
+ * The later functions (which are called deeper in the parse) sometimes return
42
+ * ParseFuncOrArgument, which contain a ParseNode as well as some data about
43
+ * whether the parsed object is a function which is missing some arguments, or a
44
+ * standalone object which can be used as an argument to another function.
45
+ */
46
+
47
+ /**
48
+ * Main Parser class
49
+ */
50
+ function Parser(input, settings) {
51
+ // Make a new lexer
52
+ this.lexer = new Lexer(input);
53
+ // Store the settings for use in parsing
54
+ this.settings = settings;
55
+ }
56
+
57
+ var ParseNode = parseData.ParseNode;
58
+
59
+ /**
60
+ * An initial function (without its arguments), or an argument to a function.
61
+ * The `result` argument should be a ParseNode.
62
+ */
63
+ function ParseFuncOrArgument(result, isFunction) {
64
+ this.result = result;
65
+ // Is this a function (i.e. is it something defined in functions.js)?
66
+ this.isFunction = isFunction;
67
+ }
68
+
69
+ /**
70
+ * Checks a result to make sure it has the right type, and throws an
71
+ * appropriate error otherwise.
72
+ *
73
+ * @param {boolean=} consume whether to consume the expected token,
74
+ * defaults to true
75
+ */
76
+ Parser.prototype.expect = function(text, consume) {
77
+ if (this.nextToken.text !== text) {
78
+ throw new ParseError(
79
+ "Expected '" + text + "', got '" + this.nextToken.text + "'",
80
+ this.lexer, this.nextToken.position
81
+ );
82
+ }
83
+ if (consume !== false) {
84
+ this.consume();
85
+ }
86
+ };
87
+
88
+ /**
89
+ * Considers the current look ahead token as consumed,
90
+ * and fetches the one after that as the new look ahead.
91
+ */
92
+ Parser.prototype.consume = function() {
93
+ this.pos = this.nextToken.position;
94
+
95
+ global_str = global_str + " " + this.nextToken.text
96
+ this.nextToken = this.lexer.lex(this.pos, this.mode);
97
+ };
98
+
99
+ /**
100
+ * Main parsing function, which parses an entire input.
101
+ *
102
+ * @return {?Array.<ParseNode>}
103
+ */
104
+ Parser.prototype.parse = function() {
105
+ // Try to parse the input
106
+ this.mode = "math";
107
+ this.pos = 0;
108
+ this.nextToken = this.lexer.lex(this.pos, this.mode);
109
+ var parse = this.parseInput();
110
+ return parse;
111
+ };
112
+
113
+ /**
114
+ * Parses an entire input tree.
115
+ */
116
+ Parser.prototype.parseInput = function() {
117
+ // Parse an expression
118
+ var expression = this.parseExpression(false);
119
+ // If we succeeded, make sure there's an EOF at the end
120
+ this.expect("EOF", false);
121
+ return expression;
122
+ };
123
+
124
+ var endOfExpression = ["}", "\\end", "\\right", "&", "\\\\", "\\cr"];
125
+
126
+ /**
127
+ * Parses an "expression", which is a list of atoms.
128
+ *
129
+ * @param {boolean} breakOnInfix Should the parsing stop when we hit infix
130
+ * nodes? This happens when functions have higher precendence
131
+ * than infix nodes in implicit parses.
132
+ *
133
+ * @param {?string} breakOnToken The token that the expression should end with,
134
+ * or `null` if something else should end the expression.
135
+ *
136
+ * @return {ParseNode}
137
+ */
138
+ Parser.prototype.parseExpression = function(breakOnInfix, breakOnToken) {
139
+ var body = [];
140
+ // Keep adding atoms to the body until we can't parse any more atoms (either
141
+ // we reached the end, a }, or a \right)
142
+ while (true) {
143
+ var lex = this.nextToken;
144
+ var pos = this.pos;
145
+ if (endOfExpression.indexOf(lex.text) !== -1) {
146
+ break;
147
+ }
148
+ if (breakOnToken && lex.text === breakOnToken) {
149
+ break;
150
+ }
151
+ var atom = this.parseAtom();
152
+ if (!atom) {
153
+ if (!this.settings.throwOnError && lex.text[0] === "\\") {
154
+ var errorNode = this.handleUnsupportedCmd();
155
+ body.push(errorNode);
156
+
157
+ pos = lex.position;
158
+ continue;
159
+ }
160
+
161
+ break;
162
+ }
163
+ if (breakOnInfix && atom.type === "infix") {
164
+ // rewind so we can parse the infix atom again
165
+ this.pos = pos;
166
+ this.nextToken = lex;
167
+ break;
168
+ }
169
+ body.push(atom);
170
+ }
171
+ return this.handleInfixNodes(body);
172
+ };
173
+
174
+ /**
175
+ * Rewrites infix operators such as \over with corresponding commands such
176
+ * as \frac.
177
+ *
178
+ * There can only be one infix operator per group. If there's more than one
179
+ * then the expression is ambiguous. This can be resolved by adding {}.
180
+ *
181
+ * @returns {Array}
182
+ */
183
+ Parser.prototype.handleInfixNodes = function(body) {
184
+ var overIndex = -1;
185
+ var funcName;
186
+
187
+ for (var i = 0; i < body.length; i++) {
188
+ var node = body[i];
189
+ if (node.type === "infix") {
190
+ if (overIndex !== -1) {
191
+ throw new ParseError("only one infix operator per group",
192
+ this.lexer, -1);
193
+ }
194
+ overIndex = i;
195
+ funcName = node.value.replaceWith;
196
+ }
197
+ }
198
+
199
+ if (overIndex !== -1) {
200
+ var numerNode;
201
+ var denomNode;
202
+
203
+ var numerBody = body.slice(0, overIndex);
204
+ var denomBody = body.slice(overIndex + 1);
205
+
206
+ if (numerBody.length === 1 && numerBody[0].type === "ordgroup") {
207
+ numerNode = numerBody[0];
208
+ } else {
209
+ numerNode = new ParseNode("ordgroup", numerBody, this.mode);
210
+ }
211
+
212
+ if (denomBody.length === 1 && denomBody[0].type === "ordgroup") {
213
+ denomNode = denomBody[0];
214
+ } else {
215
+ denomNode = new ParseNode("ordgroup", denomBody, this.mode);
216
+ }
217
+
218
+ var value = this.callFunction(
219
+ funcName, [numerNode, denomNode], null);
220
+ return [new ParseNode(value.type, value, this.mode)];
221
+ } else {
222
+ return body;
223
+ }
224
+ };
225
+
226
+ // The greediness of a superscript or subscript
227
+ var SUPSUB_GREEDINESS = 1;
228
+
229
+ /**
230
+ * Handle a subscript or superscript with nice errors.
231
+ */
232
+ Parser.prototype.handleSupSubscript = function(name) {
233
+ var symbol = this.nextToken.text;
234
+ var symPos = this.pos;
235
+ this.consume();
236
+ var group = this.parseGroup();
237
+
238
+ if (!group) {
239
+ if (!this.settings.throwOnError && this.nextToken.text[0] === "\\") {
240
+ return this.handleUnsupportedCmd();
241
+ } else {
242
+ // throw new ParseError(
243
+ // "Expected group after '" + symbol + "'",
244
+ // this.lexer,
245
+ // symPos + 1
246
+ // );
247
+ }
248
+ } else if (group.isFunction) {
249
+ // ^ and _ have a greediness, so handle interactions with functions'
250
+ // greediness
251
+ var funcGreediness = functions[group.result].greediness;
252
+ if (funcGreediness > SUPSUB_GREEDINESS) {
253
+ return this.parseFunction(group);
254
+ } else {
255
+ throw new ParseError(
256
+ "Got function '" + group.result + "' with no arguments " +
257
+ "as " + name,
258
+ this.lexer, symPos + 1);
259
+ }
260
+ } else {
261
+ return group.result;
262
+ }
263
+ };
264
+
265
+ /**
266
+ * Converts the textual input of an unsupported command into a text node
267
+ * contained within a color node whose color is determined by errorColor
268
+ */
269
+ Parser.prototype.handleUnsupportedCmd = function() {
270
+ var text = this.nextToken.text;
271
+ var textordArray = [];
272
+
273
+ for (var i = 0; i < text.length; i++) {
274
+ textordArray.push(new ParseNode("textord", text[i], "text"));
275
+ }
276
+
277
+ var textNode = new ParseNode(
278
+ "text",
279
+ {
280
+ body: textordArray,
281
+ type: "text",
282
+ },
283
+ this.mode);
284
+
285
+ var colorNode = new ParseNode(
286
+ "color",
287
+ {
288
+ color: this.settings.errorColor,
289
+ value: [textNode],
290
+ type: "color",
291
+ },
292
+ this.mode);
293
+
294
+ this.consume();
295
+ return colorNode;
296
+ };
297
+
298
+ /**
299
+ * Parses a group with optional super/subscripts.
300
+ *
301
+ * @return {?ParseNode}
302
+ */
303
+ Parser.prototype.parseAtom = function() {
304
+ // The body of an atom is an implicit group, so that things like
305
+ // \left(x\right)^2 work correctly.
306
+ var base = this.parseImplicitGroup();
307
+
308
+ // In text mode, we don't have superscripts or subscripts
309
+ if (this.mode === "text") {
310
+ return base;
311
+ }
312
+
313
+ // Note that base may be empty (i.e. null) at this point.
314
+
315
+ var superscript;
316
+ var subscript;
317
+ while (true) {
318
+ // Lex the first token
319
+ var lex = this.nextToken;
320
+
321
+ if (lex.text === "\\limits" || lex.text === "\\nolimits") {
322
+ // We got a limit control
323
+ if (!base || base.type !== "op") {
324
+ throw new ParseError(
325
+ "Limit controls must follow a math operator",
326
+ this.lexer, this.pos);
327
+ } else {
328
+ var limits = lex.text === "\\limits";
329
+ base.value.limits = limits;
330
+ base.value.alwaysHandleSupSub = true;
331
+ }
332
+ this.consume();
333
+ } else if (lex.text === "^") {
334
+ // We got a superscript start
335
+ // if (superscript) {
336
+ // throw new ParseError(
337
+ // "Double superscript", this.lexer, this.pos);
338
+ // }
339
+ superscript = this.handleSupSubscript("superscript");
340
+ } else if (lex.text === "_") {
341
+ // We got a subscript start
342
+ // if (subscript) {
343
+ // throw new ParseError(
344
+ // "Double subscript", this.lexer, this.pos);
345
+ // }
346
+ subscript = this.handleSupSubscript("subscript");
347
+ } else if (lex.text === "'") {
348
+ // We got a prime
349
+ var prime = new ParseNode("textord", "\\prime", this.mode);
350
+
351
+ // Many primes can be grouped together, so we handle this here
352
+ var primes = [prime];
353
+ this.consume();
354
+ // Keep lexing tokens until we get something that's not a prime
355
+ while (this.nextToken.text === "'") {
356
+ // For each one, add another prime to the list
357
+ primes.push(prime);
358
+ this.consume();
359
+ }
360
+ // Put them into an ordgroup as the superscript
361
+ superscript = new ParseNode("ordgroup", primes, this.mode);
362
+ } else {
363
+ // If it wasn't ^, _, or ', stop parsing super/subscripts
364
+ break;
365
+ }
366
+ }
367
+
368
+ if (superscript || subscript) {
369
+ // If we got either a superscript or subscript, create a supsub
370
+ return new ParseNode("supsub", {
371
+ base: base,
372
+ sup: superscript,
373
+ sub: subscript,
374
+ }, this.mode);
375
+ } else {
376
+ // Otherwise return the original body
377
+ return base;
378
+ }
379
+ };
380
+
381
+ // A list of the size-changing functions, for use in parseImplicitGroup
382
+ var sizeFuncs = [
383
+ "\\tiny", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize",
384
+ "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", "\\textrm", "\\rm", "\\cal",
385
+ "\\bf", "\\siptstyle", "\\boldmath", "\\it"
386
+ ];
387
+
388
+ // A list of the style-changing functions, for use in parseImplicitGroup
389
+ var styleFuncs = [
390
+ "\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle",
391
+ ];
392
+
393
+ /**
394
+ * Parses an implicit group, which is a group that starts at the end of a
395
+ * specified, and ends right before a higher explicit group ends, or at EOL. It
396
+ * is used for functions that appear to affect the current style, like \Large or
397
+ * \textrm, where instead of keeping a style we just pretend that there is an
398
+ * implicit grouping after it until the end of the group. E.g.
399
+ * small text {\Large large text} small text again
400
+ * It is also used for \left and \right to get the correct grouping.
401
+ *
402
+ * @return {?ParseNode}
403
+ */
404
+ Parser.prototype.parseImplicitGroup = function() {
405
+ var start = this.parseSymbol();
406
+
407
+ if (start == null) {
408
+ // If we didn't get anything we handle, fall back to parseFunction
409
+ return this.parseFunction();
410
+ }
411
+
412
+ var func = start.result;
413
+ var body;
414
+ if (func === "\\left") {
415
+ // If we see a left:
416
+ // Parse the entire left function (including the delimiter)
417
+ var left = this.parseFunction(start);
418
+ // Parse out the implicit body
419
+ body = this.parseExpression(false);
420
+ // Check the next token
421
+ this.expect("\\right", false);
422
+ var right = this.parseFunction();
423
+ return new ParseNode("leftright", {
424
+ body: body,
425
+ left: left.value.value,
426
+ right: right.value.value,
427
+ }, this.mode);
428
+ } else if (func === "\\begin") {
429
+ // begin...end is similar to left...right
430
+ var begin = this.parseFunction(start);
431
+ var envName = begin.value.name;
432
+ var name = (begin.value.name + "")
433
+
434
+ global_str = global_str.substring(0, global_str.length - (name.length * 2 + 2)) + name + "}"
435
+
436
+ if (!environments.hasOwnProperty(envName)) {
437
+ throw new ParseError(
438
+ "No such environment: " + envName,
439
+ this.lexer, begin.value.namepos);
440
+ }
441
+ // Build the environment object. Arguments and other information will
442
+ // be made available to the begin and end methods using properties.
443
+ var env = environments[envName];
444
+ var args = this.parseArguments("\\begin{" + envName + "}", env);
445
+ var context = {
446
+ mode: this.mode,
447
+ envName: envName,
448
+ parser: this,
449
+ lexer: this.lexer,
450
+ positions: args.pop(),
451
+ };
452
+ var result = env.handler(context, args);
453
+ this.expect("\\end", false);
454
+ var end = this.parseFunction();
455
+
456
+ var name = (begin.value.name + "")
457
+
458
+ global_str = global_str.substring(0, global_str.length - (name.length * 2 + 2)) + name + "}"
459
+ if (end.value.name !== envName) {
460
+ throw new ParseError(
461
+ "Mismatch: \\begin{" + envName + "} matched " +
462
+ "by \\end{" + end.value.name + "}",
463
+ this.lexer /* , end.value.namepos */);
464
+ // TODO: Add position to the above line and adjust test case,
465
+ // requires #385 to get merged first
466
+ }
467
+ result.position = end.position;
468
+
469
+ return result;
470
+
471
+ } else if (func.value == "\\matrix" || func.value == "\\pmatrix" || func.value == "\\cases") {
472
+ // if (!environments.hasOwnProperty(envName)) {
473
+ // throw new ParseError(
474
+ // "No such environment: " + envName,
475
+ // this.lexer, begin.value.namepos);
476
+ // }
477
+ // Build the environment object. Arguments and other information will
478
+ // be made available to the begin and end methods using properties.
479
+
480
+ envName = func.value.slice(1);
481
+ var env = environments[envName];
482
+ // var args = this.parseArguments("\\matrix{", env);
483
+ this.expect("{", true);
484
+ var context = {
485
+ mode: this.mode,
486
+ envName: envName,
487
+ parser: this,
488
+ lexer: this.lexer
489
+ };
490
+
491
+ var result = env.handler(context, {} );
492
+ // exit();
493
+ this.expect("}", true);
494
+ // var end = this.parseFunction();
495
+ var next = this.nextToken.text;
496
+ // exit();
497
+ // console.log(next);
498
+ // var name = ( + "")
499
+
500
+ // global_str = global_str.substring(0, global_str.length - (name.length * 2 + 2)) + name + "}"
501
+ // result.position = end.position;
502
+
503
+ return result;
504
+
505
+ } else if (utils.contains(sizeFuncs, func)) {
506
+ // If we see a sizing function, parse out the implict body
507
+ body = this.parseExpression(false);
508
+
509
+ return new ParseNode("sizing", {
510
+ // Figure out what size to use based on the list of functions above
511
+ original: func,
512
+ size: "size" + (utils.indexOf(sizeFuncs, func) + 1),
513
+ value: body,
514
+ }, this.mode);
515
+ } else if (utils.contains(styleFuncs, func)) {
516
+ // If we see a styling function, parse out the implict body
517
+ body = this.parseExpression(true);
518
+ return new ParseNode("styling", {
519
+ // Figure out what style to use by pulling out the style from
520
+ // the function name
521
+ original: func,
522
+ style: func.slice(1, func.length - 5),
523
+ value: body,
524
+ }, this.mode);
525
+ } else {
526
+ // Defer to parseFunction if it's not a function we handle
527
+ return this.parseFunction(start);
528
+ }
529
+ };
530
+
531
+ /**
532
+ * Parses an entire function, including its base and all of its arguments.
533
+ * The base might either have been parsed already, in which case
534
+ * it is provided as an argument, or it's the next group in the input.
535
+ *
536
+ * @param {ParseFuncOrArgument=} baseGroup optional as described above
537
+ * @return {?ParseNode}
538
+ */
539
+ Parser.prototype.parseFunction = function(baseGroup) {
540
+ if (!baseGroup) {
541
+ baseGroup = this.parseGroup();
542
+ }
543
+
544
+ if (baseGroup) {
545
+ if (baseGroup.isFunction) {
546
+ var func = baseGroup.result;
547
+ var funcData = functions[func];
548
+ if (this.mode === "text" && !funcData.allowedInText) {
549
+ // throw new ParseError(
550
+ // "Can't use function '" + func + "' in text mode",
551
+ // this.lexer, baseGroup.position);
552
+ }
553
+
554
+ var args = this.parseArguments(func, funcData);
555
+ var result = this.callFunction(func, args, args.pop());
556
+ return new ParseNode(result.type, result, this.mode);
557
+ } else {
558
+ return baseGroup.result;
559
+ }
560
+ } else {
561
+ return null;
562
+ }
563
+ };
564
+
565
+ /**
566
+ * Call a function handler with a suitable context and arguments.
567
+ */
568
+ Parser.prototype.callFunction = function(name, args, positions) {
569
+ var context = {
570
+ funcName: name,
571
+ parser: this,
572
+ lexer: this.lexer,
573
+ positions: positions,
574
+ };
575
+ return functions[name].handler(context, args);
576
+ };
577
+
578
+ /**
579
+ * Parses the arguments of a function or environment
580
+ *
581
+ * @param {string} func "\name" or "\begin{name}"
582
+ * @param {{numArgs:number,numOptionalArgs:number|undefined}} funcData
583
+ * @return the array of arguments, with the list of positions as last element
584
+ */
585
+ Parser.prototype.parseArguments = function(func, funcData) {
586
+ var totalArgs = funcData.numArgs + funcData.numOptionalArgs;
587
+ if (totalArgs === 0) {
588
+ return [[this.pos]];
589
+ }
590
+
591
+ var baseGreediness = funcData.greediness;
592
+ var positions = [this.pos];
593
+ var args = [];
594
+
595
+ for (var i = 0; i < totalArgs; i++) {
596
+ var argType = funcData.argTypes && funcData.argTypes[i];
597
+ var arg;
598
+ if (i < funcData.numOptionalArgs) {
599
+ if (argType) {
600
+ arg = this.parseSpecialGroup(argType, true);
601
+ } else {
602
+ arg = this.parseOptionalGroup();
603
+ }
604
+ if (!arg) {
605
+ args.push(null);
606
+ positions.push(this.pos);
607
+ continue;
608
+ }
609
+ } else {
610
+ if (argType) {
611
+ arg = this.parseSpecialGroup(argType);
612
+ } else {
613
+ arg = this.parseGroup();
614
+ }
615
+ if (!arg) {
616
+ if (!this.settings.throwOnError &&
617
+ this.nextToken.text[0] === "\\") {
618
+ arg = new ParseFuncOrArgument(
619
+ this.handleUnsupportedCmd(this.nextToken.text),
620
+ false);
621
+ } else {
622
+ throw new ParseError(
623
+ "Expected group after '" + func + "'",
624
+ this.lexer, this.pos);
625
+ }
626
+ }
627
+ }
628
+ var argNode;
629
+ if (arg.isFunction) {
630
+ var argGreediness =
631
+ functions[arg.result].greediness;
632
+ if (argGreediness > baseGreediness) {
633
+ argNode = this.parseFunction(arg);
634
+ } else {
635
+ // throw new ParseError(
636
+ // "Got function '" + arg.result + "' as " +
637
+ // "argument to '" + func + "'",
638
+ // this.lexer, this.pos - 1);
639
+ }
640
+ } else {
641
+ argNode = arg.result;
642
+ }
643
+ args.push(argNode);
644
+ positions.push(this.pos);
645
+ }
646
+
647
+ args.push(positions);
648
+
649
+ return args;
650
+ };
651
+
652
+
653
+ /**
654
+ * Parses a group when the mode is changing. Takes a position, a new mode, and
655
+ * an outer mode that is used to parse the outside.
656
+ *
657
+ * @return {?ParseFuncOrArgument}
658
+ */
659
+ Parser.prototype.parseSpecialGroup = function(innerMode, optional) {
660
+ var outerMode = this.mode;
661
+ // Handle `original` argTypes
662
+ if (innerMode === "original") {
663
+ innerMode = outerMode;
664
+ }
665
+
666
+ if (innerMode === "color" || innerMode === "size") {
667
+ // color and size modes are special because they should have braces and
668
+ // should only lex a single symbol inside
669
+ var openBrace = this.nextToken;
670
+ if (optional && openBrace.text !== "[") {
671
+ // optional arguments should return null if they don't exist
672
+ return null;
673
+ }
674
+ // The call to expect will lex the token after the '{' in inner mode
675
+ this.mode = innerMode;
676
+ this.expect(optional ? "[" : "{");
677
+ var inner = this.nextToken;
678
+ this.mode = outerMode;
679
+ var data;
680
+ if (innerMode === "color") {
681
+ data = inner.text;
682
+ } else {
683
+ data = inner.data;
684
+ }
685
+ this.consume(); // consume the token stored in inner
686
+ this.expect(optional ? "]" : "}");
687
+ return new ParseFuncOrArgument(
688
+ new ParseNode(innerMode, data, outerMode),
689
+ false);
690
+ } else if (innerMode === "text") {
691
+ // text mode is special because it should ignore the whitespace before
692
+ // it
693
+ var whitespace = this.lexer.lex(this.pos, "whitespace");
694
+ this.pos = whitespace.position;
695
+ }
696
+
697
+ // By the time we get here, innerMode is one of "text" or "math".
698
+ // We switch the mode of the parser, recurse, then restore the old mode.
699
+ this.mode = innerMode;
700
+ this.nextToken = this.lexer.lex(this.pos, innerMode);
701
+ var res;
702
+ if (optional) {
703
+ res = this.parseOptionalGroup();
704
+ } else {
705
+ res = this.parseGroup();
706
+ }
707
+ this.mode = outerMode;
708
+ this.nextToken = this.lexer.lex(this.pos, outerMode);
709
+ return res;
710
+ };
711
+
712
+ /**
713
+ * Parses a group, which is either a single nucleus (like "x") or an expression
714
+ * in braces (like "{x+y}")
715
+ *
716
+ * @return {?ParseFuncOrArgument}
717
+ */
718
+ Parser.prototype.parseGroup = function() {
719
+ // Try to parse an open brace
720
+ if (this.nextToken.text === "{") {
721
+ // If we get a brace, parse an expression
722
+ this.consume();
723
+ var expression = this.parseExpression(false);
724
+ // Make sure we get a close brace
725
+ this.expect("}");
726
+ return new ParseFuncOrArgument(
727
+ new ParseNode("ordgroup", expression, this.mode),
728
+ false);
729
+ } else {
730
+ // Otherwise, just return a nucleus
731
+ return this.parseSymbol();
732
+ }
733
+ };
734
+
735
+ /**
736
+ * Parses a group, which is an expression in brackets (like "[x+y]")
737
+ *
738
+ * @return {?ParseFuncOrArgument}
739
+ */
740
+ Parser.prototype.parseOptionalGroup = function() {
741
+ // Try to parse an open bracket
742
+ if (this.nextToken.text === "[") {
743
+ // If we get a brace, parse an expression
744
+ this.consume();
745
+ var expression = this.parseExpression(false, "]");
746
+ // Make sure we get a close bracket
747
+ this.expect("]");
748
+ return new ParseFuncOrArgument(
749
+ new ParseNode("ordgroup", expression, this.mode),
750
+ false);
751
+ } else {
752
+ // Otherwise, return null,
753
+ return null;
754
+ }
755
+ };
756
+
757
+ /**
758
+ * Parse a single symbol out of the string. Here, we handle both the functions
759
+ * we have defined, as well as the single character symbols
760
+ *
761
+ * @return {?ParseFuncOrArgument}
762
+ */
763
+ Parser.prototype.parseSymbol = function() {
764
+ var nucleus = this.nextToken;
765
+
766
+ if (functions[nucleus.text]) {
767
+ this.consume();
768
+ // If there exists a function with this name, we return the function and
769
+ // say that it is a function.
770
+ return new ParseFuncOrArgument(
771
+ nucleus.text,
772
+ true);
773
+ } else if (symbols[this.mode][nucleus.text]) {
774
+ this.consume();
775
+ // Otherwise if this is a no-argument function, find the type it
776
+ // corresponds to in the symbols map
777
+ return new ParseFuncOrArgument(
778
+ new ParseNode(symbols[this.mode][nucleus.text].group,
779
+ nucleus.text, this.mode),
780
+ false);
781
+ } else if (nucleus.text == "EOF" || nucleus.text == "{") {
782
+ return null;
783
+
784
+ } else {
785
+ this.consume();
786
+ // console.error(nucleus);
787
+ return new ParseFuncOrArgument(
788
+ new ParseNode(symbols["math"]["\\sigma"].group,
789
+ nucleus.text, this.mode),
790
+ false);
791
+ // console.log(nucleus.text);
792
+ // return null;
793
+ }
794
+ };
795
+
796
+ Parser.prototype.ParseNode = ParseNode;
797
+
798
+ module.exports = Parser;
modules/tokenize_latex/third_party/katex/src/Settings.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This is a module for storing settings passed into KaTeX. It correctly handles
3
+ * default settings.
4
+ */
5
+
6
+ /**
7
+ * Helper function for getting a default value if the value is undefined
8
+ */
9
+ function get(option, defaultValue) {
10
+ return option === undefined ? defaultValue : option;
11
+ }
12
+
13
+ /**
14
+ * The main Settings object
15
+ *
16
+ * The current options stored are:
17
+ * - displayMode: Whether the expression should be typeset by default in
18
+ * textstyle or displaystyle (default false)
19
+ */
20
+ function Settings(options) {
21
+ // allow null options
22
+ options = options || {};
23
+ this.displayMode = get(options.displayMode, false);
24
+ this.throwOnError = get(options.throwOnError, true);
25
+ this.errorColor = get(options.errorColor, "#cc0000");
26
+ }
27
+
28
+ module.exports = Settings;
modules/tokenize_latex/third_party/katex/src/Style.js ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file contains information and classes for the various kinds of styles
3
+ * used in TeX. It provides a generic `Style` class, which holds information
4
+ * about a specific style. It then provides instances of all the different kinds
5
+ * of styles possible, and provides functions to move between them and get
6
+ * information about them.
7
+ */
8
+
9
+ /**
10
+ * The main style class. Contains a unique id for the style, a size (which is
11
+ * the same for cramped and uncramped version of a style), a cramped flag, and a
12
+ * size multiplier, which gives the size difference between a style and
13
+ * textstyle.
14
+ */
15
+ function Style(id, size, multiplier, cramped) {
16
+ this.id = id;
17
+ this.size = size;
18
+ this.cramped = cramped;
19
+ this.sizeMultiplier = multiplier;
20
+ }
21
+
22
+ /**
23
+ * Get the style of a superscript given a base in the current style.
24
+ */
25
+ Style.prototype.sup = function() {
26
+ return styles[sup[this.id]];
27
+ };
28
+
29
+ /**
30
+ * Get the style of a subscript given a base in the current style.
31
+ */
32
+ Style.prototype.sub = function() {
33
+ return styles[sub[this.id]];
34
+ };
35
+
36
+ /**
37
+ * Get the style of a fraction numerator given the fraction in the current
38
+ * style.
39
+ */
40
+ Style.prototype.fracNum = function() {
41
+ return styles[fracNum[this.id]];
42
+ };
43
+
44
+ /**
45
+ * Get the style of a fraction denominator given the fraction in the current
46
+ * style.
47
+ */
48
+ Style.prototype.fracDen = function() {
49
+ return styles[fracDen[this.id]];
50
+ };
51
+
52
+ /**
53
+ * Get the cramped version of a style (in particular, cramping a cramped style
54
+ * doesn't change the style).
55
+ */
56
+ Style.prototype.cramp = function() {
57
+ return styles[cramp[this.id]];
58
+ };
59
+
60
+ /**
61
+ * HTML class name, like "displaystyle cramped"
62
+ */
63
+ Style.prototype.cls = function() {
64
+ return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
65
+ };
66
+
67
+ /**
68
+ * HTML Reset class name, like "reset-textstyle"
69
+ */
70
+ Style.prototype.reset = function() {
71
+ return resetNames[this.size];
72
+ };
73
+
74
+ // IDs of the different styles
75
+ var D = 0;
76
+ var Dc = 1;
77
+ var T = 2;
78
+ var Tc = 3;
79
+ var S = 4;
80
+ var Sc = 5;
81
+ var SS = 6;
82
+ var SSc = 7;
83
+
84
+ // String names for the different sizes
85
+ var sizeNames = [
86
+ "displaystyle textstyle",
87
+ "textstyle",
88
+ "scriptstyle",
89
+ "scriptscriptstyle",
90
+ ];
91
+
92
+ // Reset names for the different sizes
93
+ var resetNames = [
94
+ "reset-textstyle",
95
+ "reset-textstyle",
96
+ "reset-scriptstyle",
97
+ "reset-scriptscriptstyle",
98
+ ];
99
+
100
+ // Instances of the different styles
101
+ var styles = [
102
+ new Style(D, 0, 1.0, false),
103
+ new Style(Dc, 0, 1.0, true),
104
+ new Style(T, 1, 1.0, false),
105
+ new Style(Tc, 1, 1.0, true),
106
+ new Style(S, 2, 0.7, false),
107
+ new Style(Sc, 2, 0.7, true),
108
+ new Style(SS, 3, 0.5, false),
109
+ new Style(SSc, 3, 0.5, true),
110
+ ];
111
+
112
+ // Lookup tables for switching from one style to another
113
+ var sup = [S, Sc, S, Sc, SS, SSc, SS, SSc];
114
+ var sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc];
115
+ var fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
116
+ var fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
117
+ var cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
118
+
119
+ // We only export some of the styles. Also, we don't export the `Style` class so
120
+ // no more styles can be generated.
121
+ module.exports = {
122
+ DISPLAY: styles[D],
123
+ TEXT: styles[T],
124
+ SCRIPT: styles[S],
125
+ SCRIPTSCRIPT: styles[SS],
126
+ };
modules/tokenize_latex/third_party/katex/src/buildCommon.js ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint no-console:0 */
2
+ /**
3
+ * This module contains general functions that can be used for building
4
+ * different kinds of domTree nodes in a consistent manner.
5
+ */
6
+
7
+ var domTree = require("./domTree");
8
+ var fontMetrics = require("./fontMetrics");
9
+ var symbols = require("./symbols");
10
+ var utils = require("./utils");
11
+
12
+ var greekCapitals = [
13
+ "\\Gamma",
14
+ "\\Delta",
15
+ "\\Theta",
16
+ "\\Lambda",
17
+ "\\Xi",
18
+ "\\Pi",
19
+ "\\Sigma",
20
+ "\\Upsilon",
21
+ "\\Phi",
22
+ "\\Psi",
23
+ "\\Omega",
24
+ ];
25
+
26
+ var dotlessLetters = [
27
+ "\u0131", // dotless i, \imath
28
+ "\u0237", // dotless j, \jmath
29
+ ];
30
+
31
+ /**
32
+ * Makes a symbolNode after translation via the list of symbols in symbols.js.
33
+ * Correctly pulls out metrics for the character, and optionally takes a list of
34
+ * classes to be attached to the node.
35
+ */
36
+ var makeSymbol = function(value, style, mode, color, classes) {
37
+ // Replace the value with its replaced value from symbol.js
38
+ if (symbols[mode][value] && symbols[mode][value].replace) {
39
+ value = symbols[mode][value].replace;
40
+ }
41
+
42
+ var metrics = fontMetrics.getCharacterMetrics(value, style);
43
+
44
+ var symbolNode;
45
+ if (metrics) {
46
+ symbolNode = new domTree.symbolNode(
47
+ value, metrics.height, metrics.depth, metrics.italic, metrics.skew,
48
+ classes);
49
+ } else {
50
+ // TODO(emily): Figure out a good way to only print this in development
51
+ typeof console !== "undefined" && console.warn(
52
+ "No character metrics for '" + value + "' in style '" +
53
+ style + "'");
54
+ symbolNode = new domTree.symbolNode(value, 0, 0, 0, 0, classes);
55
+ }
56
+
57
+ if (color) {
58
+ symbolNode.style.color = color;
59
+ }
60
+
61
+ return symbolNode;
62
+ };
63
+
64
+ /**
65
+ * Makes a symbol in Main-Regular or AMS-Regular.
66
+ * Used for rel, bin, open, close, inner, and punct.
67
+ */
68
+ var mathsym = function(value, mode, color, classes) {
69
+ // Decide what font to render the symbol in by its entry in the symbols
70
+ // table.
71
+ // Have a special case for when the value = \ because the \ is used as a
72
+ // textord in unsupported command errors but cannot be parsed as a regular
73
+ // text ordinal and is therefore not present as a symbol in the symbols
74
+ // table for text
75
+ if (value === "\\" || symbols[mode][value].font === "main") {
76
+ return makeSymbol(value, "Main-Regular", mode, color, classes);
77
+ } else {
78
+ return makeSymbol(
79
+ value, "AMS-Regular", mode, color, classes.concat(["amsrm"]));
80
+ }
81
+ };
82
+
83
+ /**
84
+ * Makes a symbol in the default font for mathords and textords.
85
+ */
86
+ var mathDefault = function(value, mode, color, classes, type) {
87
+ if (type === "mathord") {
88
+ return mathit(value, mode, color, classes);
89
+ } else if (type === "textord") {
90
+ return makeSymbol(
91
+ value, "Main-Regular", mode, color, classes.concat(["mathrm"]));
92
+ } else {
93
+ throw new Error("unexpected type: " + type + " in mathDefault");
94
+ }
95
+ };
96
+
97
+ /**
98
+ * Makes a symbol in the italic math font.
99
+ */
100
+ var mathit = function(value, mode, color, classes) {
101
+ if (/[0-9]/.test(value.charAt(0)) ||
102
+ // glyphs for \imath and \jmath do not exist in Math-Italic so we
103
+ // need to use Main-Italic instead
104
+ utils.contains(dotlessLetters, value) ||
105
+ utils.contains(greekCapitals, value)) {
106
+ return makeSymbol(
107
+ value, "Main-Italic", mode, color, classes.concat(["mainit"]));
108
+ } else {
109
+ return makeSymbol(
110
+ value, "Math-Italic", mode, color, classes.concat(["mathit"]));
111
+ }
112
+ };
113
+
114
+ /**
115
+ * Makes either a mathord or textord in the correct font and color.
116
+ */
117
+ var makeOrd = function(group, options, type) {
118
+ var mode = group.mode;
119
+ var value = group.value;
120
+ if (symbols[mode][value] && symbols[mode][value].replace) {
121
+ value = symbols[mode][value].replace;
122
+ }
123
+
124
+ var classes = ["mord"];
125
+ var color = options.getColor();
126
+
127
+ var font = options.font;
128
+ if (font) {
129
+ if (font === "mathit" || utils.contains(dotlessLetters, value)) {
130
+ return mathit(value, mode, color, classes);
131
+ } else {
132
+ var fontName = fontMap[font].fontName;
133
+ if (fontMetrics.getCharacterMetrics(value, fontName)) {
134
+ return makeSymbol(
135
+ value, fontName, mode, color, classes.concat([font]));
136
+ } else {
137
+ return mathDefault(value, mode, color, classes, type);
138
+ }
139
+ }
140
+ } else {
141
+ return mathDefault(value, mode, color, classes, type);
142
+ }
143
+ };
144
+
145
+ /**
146
+ * Calculate the height, depth, and maxFontSize of an element based on its
147
+ * children.
148
+ */
149
+ var sizeElementFromChildren = function(elem) {
150
+ var height = 0;
151
+ var depth = 0;
152
+ var maxFontSize = 0;
153
+
154
+ if (elem.children) {
155
+ for (var i = 0; i < elem.children.length; i++) {
156
+ if (elem.children[i].height > height) {
157
+ height = elem.children[i].height;
158
+ }
159
+ if (elem.children[i].depth > depth) {
160
+ depth = elem.children[i].depth;
161
+ }
162
+ if (elem.children[i].maxFontSize > maxFontSize) {
163
+ maxFontSize = elem.children[i].maxFontSize;
164
+ }
165
+ }
166
+ }
167
+
168
+ elem.height = height;
169
+ elem.depth = depth;
170
+ elem.maxFontSize = maxFontSize;
171
+ };
172
+
173
+ /**
174
+ * Makes a span with the given list of classes, list of children, and color.
175
+ */
176
+ var makeSpan = function(classes, children, color) {
177
+ var span = new domTree.span(classes, children);
178
+
179
+ sizeElementFromChildren(span);
180
+
181
+ if (color) {
182
+ span.style.color = color;
183
+ }
184
+
185
+ return span;
186
+ };
187
+
188
+ /**
189
+ * Makes a document fragment with the given list of children.
190
+ */
191
+ var makeFragment = function(children) {
192
+ var fragment = new domTree.documentFragment(children);
193
+
194
+ sizeElementFromChildren(fragment);
195
+
196
+ return fragment;
197
+ };
198
+
199
+ /**
200
+ * Makes an element placed in each of the vlist elements to ensure that each
201
+ * element has the same max font size. To do this, we create a zero-width space
202
+ * with the correct font size.
203
+ */
204
+ var makeFontSizer = function(options, fontSize) {
205
+ var fontSizeInner = makeSpan([], [new domTree.symbolNode("\u200b")]);
206
+ fontSizeInner.style.fontSize =
207
+ (fontSize / options.style.sizeMultiplier) + "em";
208
+
209
+ var fontSizer = makeSpan(
210
+ ["fontsize-ensurer", "reset-" + options.size, "size5"],
211
+ [fontSizeInner]);
212
+
213
+ return fontSizer;
214
+ };
215
+
216
+ /**
217
+ * Makes a vertical list by stacking elements and kerns on top of each other.
218
+ * Allows for many different ways of specifying the positioning method.
219
+ *
220
+ * Arguments:
221
+ * - children: A list of child or kern nodes to be stacked on top of each other
222
+ * (i.e. the first element will be at the bottom, and the last at
223
+ * the top). Element nodes are specified as
224
+ * {type: "elem", elem: node}
225
+ * while kern nodes are specified as
226
+ * {type: "kern", size: size}
227
+ * - positionType: The method by which the vlist should be positioned. Valid
228
+ * values are:
229
+ * - "individualShift": The children list only contains elem
230
+ * nodes, and each node contains an extra
231
+ * "shift" value of how much it should be
232
+ * shifted (note that shifting is always
233
+ * moving downwards). positionData is
234
+ * ignored.
235
+ * - "top": The positionData specifies the topmost point of
236
+ * the vlist (note this is expected to be a height,
237
+ * so positive values move up)
238
+ * - "bottom": The positionData specifies the bottommost point
239
+ * of the vlist (note this is expected to be a
240
+ * depth, so positive values move down
241
+ * - "shift": The vlist will be positioned such that its
242
+ * baseline is positionData away from the baseline
243
+ * of the first child. Positive values move
244
+ * downwards.
245
+ * - "firstBaseline": The vlist will be positioned such that
246
+ * its baseline is aligned with the
247
+ * baseline of the first child.
248
+ * positionData is ignored. (this is
249
+ * equivalent to "shift" with
250
+ * positionData=0)
251
+ * - positionData: Data used in different ways depending on positionType
252
+ * - options: An Options object
253
+ *
254
+ */
255
+ var makeVList = function(children, positionType, positionData, options) {
256
+ var depth;
257
+ var currPos;
258
+ var i;
259
+ if (positionType === "individualShift") {
260
+ var oldChildren = children;
261
+ children = [oldChildren[0]];
262
+
263
+ // Add in kerns to the list of children to get each element to be
264
+ // shifted to the correct specified shift
265
+ depth = -oldChildren[0].shift - oldChildren[0].elem.depth;
266
+ currPos = depth;
267
+ for (i = 1; i < oldChildren.length; i++) {
268
+ var diff = -oldChildren[i].shift - currPos -
269
+ oldChildren[i].elem.depth;
270
+ var size = diff -
271
+ (oldChildren[i - 1].elem.height +
272
+ oldChildren[i - 1].elem.depth);
273
+
274
+ currPos = currPos + diff;
275
+
276
+ children.push({type: "kern", size: size});
277
+ children.push(oldChildren[i]);
278
+ }
279
+ } else if (positionType === "top") {
280
+ // We always start at the bottom, so calculate the bottom by adding up
281
+ // all the sizes
282
+ var bottom = positionData;
283
+ for (i = 0; i < children.length; i++) {
284
+ if (children[i].type === "kern") {
285
+ bottom -= children[i].size;
286
+ } else {
287
+ bottom -= children[i].elem.height + children[i].elem.depth;
288
+ }
289
+ }
290
+ depth = bottom;
291
+ } else if (positionType === "bottom") {
292
+ depth = -positionData;
293
+ } else if (positionType === "shift") {
294
+ depth = -children[0].elem.depth - positionData;
295
+ } else if (positionType === "firstBaseline") {
296
+ depth = -children[0].elem.depth;
297
+ } else {
298
+ depth = 0;
299
+ }
300
+
301
+ // Make the fontSizer
302
+ var maxFontSize = 0;
303
+ for (i = 0; i < children.length; i++) {
304
+ if (children[i].type === "elem") {
305
+ maxFontSize = Math.max(maxFontSize, children[i].elem.maxFontSize);
306
+ }
307
+ }
308
+ var fontSizer = makeFontSizer(options, maxFontSize);
309
+
310
+ // Create a new list of actual children at the correct offsets
311
+ var realChildren = [];
312
+ currPos = depth;
313
+ for (i = 0; i < children.length; i++) {
314
+ if (children[i].type === "kern") {
315
+ currPos += children[i].size;
316
+ } else {
317
+ var child = children[i].elem;
318
+
319
+ var shift = -child.depth - currPos;
320
+ currPos += child.height + child.depth;
321
+
322
+ var childWrap = makeSpan([], [fontSizer, child]);
323
+ childWrap.height -= shift;
324
+ childWrap.depth += shift;
325
+ childWrap.style.top = shift + "em";
326
+
327
+ realChildren.push(childWrap);
328
+ }
329
+ }
330
+
331
+ // Add in an element at the end with no offset to fix the calculation of
332
+ // baselines in some browsers (namely IE, sometimes safari)
333
+ var baselineFix = makeSpan(
334
+ ["baseline-fix"], [fontSizer, new domTree.symbolNode("\u200b")]);
335
+ realChildren.push(baselineFix);
336
+
337
+ var vlist = makeSpan(["vlist"], realChildren);
338
+ // Fix the final height and depth, in case there were kerns at the ends
339
+ // since the makeSpan calculation won't take that in to account.
340
+ vlist.height = Math.max(currPos, vlist.height);
341
+ vlist.depth = Math.max(-depth, vlist.depth);
342
+ return vlist;
343
+ };
344
+
345
+ // A table of size -> font size for the different sizing functions
346
+ var sizingMultiplier = {
347
+ size1: 0.5,
348
+ size2: 0.7,
349
+ size3: 0.8,
350
+ size4: 0.9,
351
+ size5: 1.0,
352
+ size6: 1.2,
353
+ size7: 1.44,
354
+ size8: 1.73,
355
+ size9: 2.07,
356
+ size10: 2.49,
357
+ };
358
+
359
+ // A map of spacing functions to their attributes, like size and corresponding
360
+ // CSS class
361
+ var spacingFunctions = {
362
+ "\\qquad": {
363
+ size: "2em",
364
+ className: "qquad",
365
+ },
366
+ "\\quad": {
367
+ size: "1em",
368
+ className: "quad",
369
+ },
370
+ "\\enspace": {
371
+ size: "0.5em",
372
+ className: "enspace",
373
+ },
374
+ "\\;": {
375
+ size: "0.277778em",
376
+ className: "thickspace",
377
+ },
378
+ "\\:": {
379
+ size: "0.22222em",
380
+ className: "mediumspace",
381
+ },
382
+ "\\,": {
383
+ size: "0.16667em",
384
+ className: "thinspace",
385
+ },
386
+ "\\!": {
387
+ size: "-0.16667em",
388
+ className: "negativethinspace",
389
+ },
390
+ };
391
+
392
+ /**
393
+ * Maps TeX font commands to objects containing:
394
+ * - variant: string used for "mathvariant" attribute in buildMathML.js
395
+ * - fontName: the "style" parameter to fontMetrics.getCharacterMetrics
396
+ */
397
+ // A map between tex font commands an MathML mathvariant attribute values
398
+ var fontMap = {
399
+ // styles
400
+ "mathbf": {
401
+ variant: "bold",
402
+ fontName: "Main-Bold",
403
+ },
404
+ "mathrm": {
405
+ variant: "normal",
406
+ fontName: "Main-Regular",
407
+ },
408
+
409
+ // "mathit" is missing because it requires the use of two fonts: Main-Italic
410
+ // and Math-Italic. This is handled by a special case in makeOrd which ends
411
+ // up calling mathit.
412
+
413
+ // families
414
+ "mathbb": {
415
+ variant: "double-struck",
416
+ fontName: "AMS-Regular",
417
+ },
418
+ "mathcal": {
419
+ variant: "script",
420
+ fontName: "Caligraphic-Regular",
421
+ },
422
+ "mathfrak": {
423
+ variant: "fraktur",
424
+ fontName: "Fraktur-Regular",
425
+ },
426
+ "mathscr": {
427
+ variant: "script",
428
+ fontName: "Script-Regular",
429
+ },
430
+ "mathsf": {
431
+ variant: "sans-serif",
432
+ fontName: "SansSerif-Regular",
433
+ },
434
+ "mathtt": {
435
+ variant: "monospace",
436
+ fontName: "Typewriter-Regular",
437
+ },
438
+ };
439
+
440
+ module.exports = {
441
+ fontMap: fontMap,
442
+ makeSymbol: makeSymbol,
443
+ mathsym: mathsym,
444
+ makeSpan: makeSpan,
445
+ makeFragment: makeFragment,
446
+ makeVList: makeVList,
447
+ makeOrd: makeOrd,
448
+ sizingMultiplier: sizingMultiplier,
449
+ spacingFunctions: spacingFunctions,
450
+ };
modules/tokenize_latex/third_party/katex/src/buildHTML.js ADDED
@@ -0,0 +1,1402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint no-console:0 */
2
+ /**
3
+ * This file does the main work of building a domTree structure from a parse
4
+ * tree. The entry point is the `buildHTML` function, which takes a parse tree.
5
+ * Then, the buildExpression, buildGroup, and various groupTypes functions are
6
+ * called, to produce a final HTML tree.
7
+ */
8
+
9
+ var ParseError = require("./ParseError");
10
+ var Style = require("./Style");
11
+
12
+ var buildCommon = require("./buildCommon");
13
+ var delimiter = require("./delimiter");
14
+ var domTree = require("./domTree");
15
+ var fontMetrics = require("./fontMetrics");
16
+ var utils = require("./utils");
17
+
18
+ var makeSpan = buildCommon.makeSpan;
19
+
20
+ /**
21
+ * Take a list of nodes, build them in order, and return a list of the built
22
+ * nodes. This function handles the `prev` node correctly, and passes the
23
+ * previous element from the list as the prev of the next element.
24
+ */
25
+ var buildExpression = function(expression, options, prev) {
26
+ var groups = [];
27
+ for (var i = 0; i < expression.length; i++) {
28
+ var group = expression[i];
29
+ groups.push(buildGroup(group, options, prev));
30
+ prev = group;
31
+ }
32
+ return groups;
33
+ };
34
+
35
+ // List of types used by getTypeOfGroup,
36
+ // see https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
37
+ var groupToType = {
38
+ mathord: "mord",
39
+ textord: "mord",
40
+ bin: "mbin",
41
+ rel: "mrel",
42
+ text: "mord",
43
+ open: "mopen",
44
+ close: "mclose",
45
+ inner: "minner",
46
+ genfrac: "mord",
47
+ array: "mord",
48
+ spacing: "mord",
49
+ punct: "mpunct",
50
+ ordgroup: "mord",
51
+ op: "mop",
52
+ katex: "mord",
53
+ overline: "mord",
54
+ underline: "mord",
55
+ rule: "mord",
56
+ leftright: "minner",
57
+ sqrt: "mord",
58
+ accent: "mord",
59
+ };
60
+
61
+ /**
62
+ * Gets the final math type of an expression, given its group type. This type is
63
+ * used to determine spacing between elements, and affects bin elements by
64
+ * causing them to change depending on what types are around them. This type
65
+ * must be attached to the outermost node of an element as a CSS class so that
66
+ * spacing with its surrounding elements works correctly.
67
+ *
68
+ * Some elements can be mapped one-to-one from group type to math type, and
69
+ * those are listed in the `groupToType` table.
70
+ *
71
+ * Others (usually elements that wrap around other elements) often have
72
+ * recursive definitions, and thus call `getTypeOfGroup` on their inner
73
+ * elements.
74
+ */
75
+ var getTypeOfGroup = function(group) {
76
+ if (group == null) {
77
+ // Like when typesetting $^3$
78
+ return groupToType.mathord;
79
+ } else if (group.type === "supsub") {
80
+ return getTypeOfGroup(group.value.base);
81
+ } else if (group.type === "llap" || group.type === "rlap") {
82
+ return getTypeOfGroup(group.value);
83
+ } else if (group.type === "color") {
84
+ return getTypeOfGroup(group.value.value);
85
+ } else if (group.type === "sizing") {
86
+ return getTypeOfGroup(group.value.value);
87
+ } else if (group.type === "styling") {
88
+ return getTypeOfGroup(group.value.value);
89
+ } else if (group.type === "delimsizing") {
90
+ return groupToType[group.value.delimType];
91
+ } else {
92
+ return groupToType[group.type];
93
+ }
94
+ };
95
+
96
+ /**
97
+ * Sometimes, groups perform special rules when they have superscripts or
98
+ * subscripts attached to them. This function lets the `supsub` group know that
99
+ * its inner element should handle the superscripts and subscripts instead of
100
+ * handling them itself.
101
+ */
102
+ var shouldHandleSupSub = function(group, options) {
103
+ if (!group) {
104
+ return false;
105
+ } else if (group.type === "op") {
106
+ // Operators handle supsubs differently when they have limits
107
+ // (e.g. `\displaystyle\sum_2^3`)
108
+ return group.value.limits &&
109
+ (options.style.size === Style.DISPLAY.size ||
110
+ group.value.alwaysHandleSupSub);
111
+ } else if (group.type === "accent") {
112
+ return isCharacterBox(group.value.base);
113
+ } else {
114
+ return null;
115
+ }
116
+ };
117
+
118
+ /**
119
+ * Sometimes we want to pull out the innermost element of a group. In most
120
+ * cases, this will just be the group itself, but when ordgroups and colors have
121
+ * a single element, we want to pull that out.
122
+ */
123
+ var getBaseElem = function(group) {
124
+ if (!group) {
125
+ return false;
126
+ } else if (group.type === "ordgroup") {
127
+ if (group.value.length === 1) {
128
+ return getBaseElem(group.value[0]);
129
+ } else {
130
+ return group;
131
+ }
132
+ } else if (group.type === "color") {
133
+ if (group.value.value.length === 1) {
134
+ return getBaseElem(group.value.value[0]);
135
+ } else {
136
+ return group;
137
+ }
138
+ } else {
139
+ return group;
140
+ }
141
+ };
142
+
143
+ /**
144
+ * TeXbook algorithms often reference "character boxes", which are simply groups
145
+ * with a single character in them. To decide if something is a character box,
146
+ * we find its innermost group, and see if it is a single character.
147
+ */
148
+ var isCharacterBox = function(group) {
149
+ var baseElem = getBaseElem(group);
150
+
151
+ // These are all they types of groups which hold single characters
152
+ return baseElem.type === "mathord" ||
153
+ baseElem.type === "textord" ||
154
+ baseElem.type === "bin" ||
155
+ baseElem.type === "rel" ||
156
+ baseElem.type === "inner" ||
157
+ baseElem.type === "open" ||
158
+ baseElem.type === "close" ||
159
+ baseElem.type === "punct";
160
+ };
161
+
162
+ var makeNullDelimiter = function(options) {
163
+ return makeSpan([
164
+ "sizing", "reset-" + options.size, "size5",
165
+ options.style.reset(), Style.TEXT.cls(),
166
+ "nulldelimiter",
167
+ ]);
168
+ };
169
+
170
+ /**
171
+ * This is a map of group types to the function used to handle that type.
172
+ * Simpler types come at the beginning, while complicated types come afterwards.
173
+ */
174
+ var groupTypes = {};
175
+
176
+ groupTypes.mathord = function(group, options, prev) {
177
+ return buildCommon.makeOrd(group, options, "mathord");
178
+ };
179
+
180
+ groupTypes.textord = function(group, options, prev) {
181
+ return buildCommon.makeOrd(group, options, "textord");
182
+ };
183
+
184
+ groupTypes.bin = function(group, options, prev) {
185
+ var className = "mbin";
186
+ // Pull out the most recent element. Do some special handling to find
187
+ // things at the end of a \color group. Note that we don't use the same
188
+ // logic for ordgroups (which count as ords).
189
+ var prevAtom = prev;
190
+ while (prevAtom && prevAtom.type === "color") {
191
+ var atoms = prevAtom.value.value;
192
+ prevAtom = atoms[atoms.length - 1];
193
+ }
194
+ // See TeXbook pg. 442-446, Rules 5 and 6, and the text before Rule 19.
195
+ // Here, we determine whether the bin should turn into an ord. We
196
+ // currently only apply Rule 5.
197
+ if (!prev || utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
198
+ getTypeOfGroup(prevAtom))) {
199
+ group.type = "textord";
200
+ className = "mord";
201
+ }
202
+
203
+ return buildCommon.mathsym(
204
+ group.value, group.mode, options.getColor(), [className]);
205
+ };
206
+
207
+ groupTypes.rel = function(group, options, prev) {
208
+ return buildCommon.mathsym(
209
+ group.value, group.mode, options.getColor(), ["mrel"]);
210
+ };
211
+
212
+ groupTypes.open = function(group, options, prev) {
213
+ return buildCommon.mathsym(
214
+ group.value, group.mode, options.getColor(), ["mopen"]);
215
+ };
216
+
217
+ groupTypes.close = function(group, options, prev) {
218
+ return buildCommon.mathsym(
219
+ group.value, group.mode, options.getColor(), ["mclose"]);
220
+ };
221
+
222
+ groupTypes.inner = function(group, options, prev) {
223
+ return buildCommon.mathsym(
224
+ group.value, group.mode, options.getColor(), ["minner"]);
225
+ };
226
+
227
+ groupTypes.punct = function(group, options, prev) {
228
+ return buildCommon.mathsym(
229
+ group.value, group.mode, options.getColor(), ["mpunct"]);
230
+ };
231
+
232
+ groupTypes.ordgroup = function(group, options, prev) {
233
+ return makeSpan(
234
+ ["mord", options.style.cls()],
235
+ buildExpression(group.value, options.reset())
236
+ );
237
+ };
238
+
239
+ groupTypes.text = function(group, options, prev) {
240
+ return makeSpan(["text", "mord", options.style.cls()],
241
+ buildExpression(group.value.body, options.reset()));
242
+ };
243
+
244
+ groupTypes.color = function(group, options, prev) {
245
+ var elements = buildExpression(
246
+ group.value.value,
247
+ options.withColor(group.value.color),
248
+ prev
249
+ );
250
+
251
+ // \color isn't supposed to affect the type of the elements it contains.
252
+ // To accomplish this, we wrap the results in a fragment, so the inner
253
+ // elements will be able to directly interact with their neighbors. For
254
+ // example, `\color{red}{2 +} 3` has the same spacing as `2 + 3`
255
+ return new buildCommon.makeFragment(elements);
256
+ };
257
+
258
+ groupTypes.supsub = function(group, options, prev) {
259
+ // Superscript and subscripts are handled in the TeXbook on page
260
+ // 445-446, rules 18(a-f).
261
+
262
+ // Here is where we defer to the inner group if it should handle
263
+ // superscripts and subscripts itself.
264
+ if (shouldHandleSupSub(group.value.base, options)) {
265
+ return groupTypes[group.value.base.type](group, options, prev);
266
+ }
267
+
268
+ var base = buildGroup(group.value.base, options.reset());
269
+ var supmid;
270
+ var submid;
271
+ var sup;
272
+ var sub;
273
+
274
+ if (group.value.sup) {
275
+ sup = buildGroup(group.value.sup,
276
+ options.withStyle(options.style.sup()));
277
+ supmid = makeSpan(
278
+ [options.style.reset(), options.style.sup().cls()], [sup]);
279
+ }
280
+
281
+ if (group.value.sub) {
282
+ sub = buildGroup(group.value.sub,
283
+ options.withStyle(options.style.sub()));
284
+ submid = makeSpan(
285
+ [options.style.reset(), options.style.sub().cls()], [sub]);
286
+ }
287
+
288
+ // Rule 18a
289
+ var supShift;
290
+ var subShift;
291
+ if (isCharacterBox(group.value.base)) {
292
+ supShift = 0;
293
+ subShift = 0;
294
+ } else {
295
+ supShift = base.height - fontMetrics.metrics.supDrop;
296
+ subShift = base.depth + fontMetrics.metrics.subDrop;
297
+ }
298
+
299
+ // Rule 18c
300
+ var minSupShift;
301
+ if (options.style === Style.DISPLAY) {
302
+ minSupShift = fontMetrics.metrics.sup1;
303
+ } else if (options.style.cramped) {
304
+ minSupShift = fontMetrics.metrics.sup3;
305
+ } else {
306
+ minSupShift = fontMetrics.metrics.sup2;
307
+ }
308
+
309
+ // scriptspace is a font-size-independent size, so scale it
310
+ // appropriately
311
+ var multiplier = Style.TEXT.sizeMultiplier *
312
+ options.style.sizeMultiplier;
313
+ var scriptspace =
314
+ (0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
315
+
316
+ var supsub;
317
+ if (!group.value.sup) {
318
+ // Rule 18b
319
+ subShift = Math.max(
320
+ subShift, fontMetrics.metrics.sub1,
321
+ sub.height - 0.8 * fontMetrics.metrics.xHeight);
322
+
323
+ supsub = buildCommon.makeVList([
324
+ {type: "elem", elem: submid},
325
+ ], "shift", subShift, options);
326
+
327
+ supsub.children[0].style.marginRight = scriptspace;
328
+
329
+ // Subscripts shouldn't be shifted by the base's italic correction.
330
+ // Account for that by shifting the subscript back the appropriate
331
+ // amount. Note we only do this when the base is a single symbol.
332
+ if (base instanceof domTree.symbolNode) {
333
+ supsub.children[0].style.marginLeft = -base.italic + "em";
334
+ }
335
+ } else if (!group.value.sub) {
336
+ // Rule 18c, d
337
+ supShift = Math.max(supShift, minSupShift,
338
+ sup.depth + 0.25 * fontMetrics.metrics.xHeight);
339
+
340
+ supsub = buildCommon.makeVList([
341
+ {type: "elem", elem: supmid},
342
+ ], "shift", -supShift, options);
343
+
344
+ supsub.children[0].style.marginRight = scriptspace;
345
+ } else {
346
+ supShift = Math.max(
347
+ supShift, minSupShift,
348
+ sup.depth + 0.25 * fontMetrics.metrics.xHeight);
349
+ subShift = Math.max(subShift, fontMetrics.metrics.sub2);
350
+
351
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness;
352
+
353
+ // Rule 18e
354
+ if ((supShift - sup.depth) - (sub.height - subShift) <
355
+ 4 * ruleWidth) {
356
+ subShift = 4 * ruleWidth - (supShift - sup.depth) + sub.height;
357
+ var psi = 0.8 * fontMetrics.metrics.xHeight -
358
+ (supShift - sup.depth);
359
+ if (psi > 0) {
360
+ supShift += psi;
361
+ subShift -= psi;
362
+ }
363
+ }
364
+
365
+ supsub = buildCommon.makeVList([
366
+ {type: "elem", elem: submid, shift: subShift},
367
+ {type: "elem", elem: supmid, shift: -supShift},
368
+ ], "individualShift", null, options);
369
+
370
+ // See comment above about subscripts not being shifted
371
+ if (base instanceof domTree.symbolNode) {
372
+ supsub.children[0].style.marginLeft = -base.italic + "em";
373
+ }
374
+
375
+ supsub.children[0].style.marginRight = scriptspace;
376
+ supsub.children[1].style.marginRight = scriptspace;
377
+ }
378
+
379
+ return makeSpan([getTypeOfGroup(group.value.base)],
380
+ [base, supsub]);
381
+ };
382
+
383
+ groupTypes.genfrac = function(group, options, prev) {
384
+ // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
385
+ // Figure out what style this fraction should be in based on the
386
+ // function used
387
+ var fstyle = options.style;
388
+ if (group.value.size === "display") {
389
+ fstyle = Style.DISPLAY;
390
+ } else if (group.value.size === "text") {
391
+ fstyle = Style.TEXT;
392
+ }
393
+
394
+ var nstyle = fstyle.fracNum();
395
+ var dstyle = fstyle.fracDen();
396
+
397
+ var numer = buildGroup(group.value.numer, options.withStyle(nstyle));
398
+ var numerreset = makeSpan([fstyle.reset(), nstyle.cls()], [numer]);
399
+
400
+ var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
401
+ var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]);
402
+
403
+ var ruleWidth;
404
+ if (group.value.hasBarLine) {
405
+ ruleWidth = fontMetrics.metrics.defaultRuleThickness /
406
+ options.style.sizeMultiplier;
407
+ } else {
408
+ ruleWidth = 0;
409
+ }
410
+
411
+ // Rule 15b
412
+ var numShift;
413
+ var clearance;
414
+ var denomShift;
415
+ if (fstyle.size === Style.DISPLAY.size) {
416
+ numShift = fontMetrics.metrics.num1;
417
+ if (ruleWidth > 0) {
418
+ clearance = 3 * ruleWidth;
419
+ } else {
420
+ clearance = 7 * fontMetrics.metrics.defaultRuleThickness;
421
+ }
422
+ denomShift = fontMetrics.metrics.denom1;
423
+ } else {
424
+ if (ruleWidth > 0) {
425
+ numShift = fontMetrics.metrics.num2;
426
+ clearance = ruleWidth;
427
+ } else {
428
+ numShift = fontMetrics.metrics.num3;
429
+ clearance = 3 * fontMetrics.metrics.defaultRuleThickness;
430
+ }
431
+ denomShift = fontMetrics.metrics.denom2;
432
+ }
433
+
434
+ var frac;
435
+ if (ruleWidth === 0) {
436
+ // Rule 15c
437
+ var candiateClearance =
438
+ (numShift - numer.depth) - (denom.height - denomShift);
439
+ if (candiateClearance < clearance) {
440
+ numShift += 0.5 * (clearance - candiateClearance);
441
+ denomShift += 0.5 * (clearance - candiateClearance);
442
+ }
443
+
444
+ frac = buildCommon.makeVList([
445
+ {type: "elem", elem: denomreset, shift: denomShift},
446
+ {type: "elem", elem: numerreset, shift: -numShift},
447
+ ], "individualShift", null, options);
448
+ } else {
449
+ // Rule 15d
450
+ var axisHeight = fontMetrics.metrics.axisHeight;
451
+
452
+ if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) <
453
+ clearance) {
454
+ numShift +=
455
+ clearance - ((numShift - numer.depth) -
456
+ (axisHeight + 0.5 * ruleWidth));
457
+ }
458
+
459
+ if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift) <
460
+ clearance) {
461
+ denomShift +=
462
+ clearance - ((axisHeight - 0.5 * ruleWidth) -
463
+ (denom.height - denomShift));
464
+ }
465
+
466
+ var mid = makeSpan(
467
+ [options.style.reset(), Style.TEXT.cls(), "frac-line"]);
468
+ // Manually set the height of the line because its height is
469
+ // created in CSS
470
+ mid.height = ruleWidth;
471
+
472
+ var midShift = -(axisHeight - 0.5 * ruleWidth);
473
+
474
+ frac = buildCommon.makeVList([
475
+ {type: "elem", elem: denomreset, shift: denomShift},
476
+ {type: "elem", elem: mid, shift: midShift},
477
+ {type: "elem", elem: numerreset, shift: -numShift},
478
+ ], "individualShift", null, options);
479
+ }
480
+
481
+ // Since we manually change the style sometimes (with \dfrac or \tfrac),
482
+ // account for the possible size change here.
483
+ frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
484
+ frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
485
+
486
+ // Rule 15e
487
+ var delimSize;
488
+ if (fstyle.size === Style.DISPLAY.size) {
489
+ delimSize = fontMetrics.metrics.delim1;
490
+ } else {
491
+ delimSize = fontMetrics.metrics.getDelim2(fstyle);
492
+ }
493
+
494
+ var leftDelim;
495
+ var rightDelim;
496
+ if (group.value.leftDelim == null) {
497
+ leftDelim = makeNullDelimiter(options);
498
+ } else {
499
+ leftDelim = delimiter.customSizedDelim(
500
+ group.value.leftDelim, delimSize, true,
501
+ options.withStyle(fstyle), group.mode);
502
+ }
503
+ if (group.value.rightDelim == null) {
504
+ rightDelim = makeNullDelimiter(options);
505
+ } else {
506
+ rightDelim = delimiter.customSizedDelim(
507
+ group.value.rightDelim, delimSize, true,
508
+ options.withStyle(fstyle), group.mode);
509
+ }
510
+
511
+ return makeSpan(
512
+ ["mord", options.style.reset(), fstyle.cls()],
513
+ [leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
514
+ options.getColor());
515
+ };
516
+
517
+ groupTypes.array = function(group, options, prev) {
518
+ var r;
519
+ var c;
520
+ var nr = group.value.body.length;
521
+ var nc = 0;
522
+ var body = new Array(nr);
523
+
524
+ // Horizontal spacing
525
+ var pt = 1 / fontMetrics.metrics.ptPerEm;
526
+ var arraycolsep = 5 * pt; // \arraycolsep in article.cls
527
+
528
+ // Vertical spacing
529
+ var baselineskip = 12 * pt; // see size10.clo
530
+ // Default \arraystretch from lttab.dtx
531
+ // TODO(gagern): may get redefined once we have user-defined macros
532
+ var arraystretch = utils.deflt(group.value.arraystretch, 1);
533
+ var arrayskip = arraystretch * baselineskip;
534
+ var arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and
535
+ var arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx
536
+
537
+ var totalHeight = 0;
538
+ for (r = 0; r < group.value.body.length; ++r) {
539
+ var inrow = group.value.body[r];
540
+ var height = arstrutHeight; // \@array adds an \@arstrut
541
+ var depth = arstrutDepth; // to each tow (via the template)
542
+
543
+ if (nc < inrow.length) {
544
+ nc = inrow.length;
545
+ }
546
+
547
+ var outrow = new Array(inrow.length);
548
+ for (c = 0; c < inrow.length; ++c) {
549
+ var elt = buildGroup(inrow[c], options);
550
+ if (depth < elt.depth) {
551
+ depth = elt.depth;
552
+ }
553
+ if (height < elt.height) {
554
+ height = elt.height;
555
+ }
556
+ outrow[c] = elt;
557
+ }
558
+
559
+ var gap = 0;
560
+ if (group.value.rowGaps[r]) {
561
+ gap = group.value.rowGaps[r].value;
562
+ switch (gap.unit) {
563
+ case "em":
564
+ gap = gap.number;
565
+ break;
566
+ case "ex":
567
+ gap = gap.number * fontMetrics.metrics.emPerEx;
568
+ break;
569
+ default:
570
+ console.error("Can't handle unit " + gap.unit);
571
+ gap = 0;
572
+ }
573
+ if (gap > 0) { // \@argarraycr
574
+ gap += arstrutDepth;
575
+ if (depth < gap) {
576
+ depth = gap; // \@xargarraycr
577
+ }
578
+ gap = 0;
579
+ }
580
+ }
581
+
582
+ outrow.height = height;
583
+ outrow.depth = depth;
584
+ totalHeight += height;
585
+ outrow.pos = totalHeight;
586
+ totalHeight += depth + gap; // \@yargarraycr
587
+ body[r] = outrow;
588
+ }
589
+
590
+ var offset = totalHeight / 2 + fontMetrics.metrics.axisHeight;
591
+ var colDescriptions = group.value.cols || [];
592
+ var cols = [];
593
+ var colSep;
594
+ var colDescrNum;
595
+ for (c = 0, colDescrNum = 0;
596
+ // Continue while either there are more columns or more column
597
+ // descriptions, so trailing separators don't get lost.
598
+ c < nc || colDescrNum < colDescriptions.length;
599
+ ++c, ++colDescrNum) {
600
+
601
+ var colDescr = colDescriptions[colDescrNum] || {};
602
+
603
+ var firstSeparator = true;
604
+ while (colDescr.type === "separator") {
605
+ // If there is more than one separator in a row, add a space
606
+ // between them.
607
+ if (!firstSeparator) {
608
+ colSep = makeSpan(["arraycolsep"], []);
609
+ colSep.style.width =
610
+ fontMetrics.metrics.doubleRuleSep + "em";
611
+ cols.push(colSep);
612
+ }
613
+
614
+ if (colDescr.separator === "|") {
615
+ var separator = makeSpan(
616
+ ["vertical-separator"],
617
+ []);
618
+ separator.style.height = totalHeight + "em";
619
+ separator.style.verticalAlign =
620
+ -(totalHeight - offset) + "em";
621
+
622
+ cols.push(separator);
623
+ } else {
624
+ throw new ParseError(
625
+ "Invalid separator type: " + colDescr.separator);
626
+ }
627
+
628
+ colDescrNum++;
629
+ colDescr = colDescriptions[colDescrNum] || {};
630
+ firstSeparator = false;
631
+ }
632
+
633
+ if (c >= nc) {
634
+ continue;
635
+ }
636
+
637
+ var sepwidth;
638
+ if (c > 0 || group.value.hskipBeforeAndAfter) {
639
+ sepwidth = utils.deflt(colDescr.pregap, arraycolsep);
640
+ if (sepwidth !== 0) {
641
+ colSep = makeSpan(["arraycolsep"], []);
642
+ colSep.style.width = sepwidth + "em";
643
+ cols.push(colSep);
644
+ }
645
+ }
646
+
647
+ var col = [];
648
+ for (r = 0; r < nr; ++r) {
649
+ var row = body[r];
650
+ var elem = row[c];
651
+ if (!elem) {
652
+ continue;
653
+ }
654
+ var shift = row.pos - offset;
655
+ elem.depth = row.depth;
656
+ elem.height = row.height;
657
+ col.push({type: "elem", elem: elem, shift: shift});
658
+ }
659
+
660
+ col = buildCommon.makeVList(col, "individualShift", null, options);
661
+ col = makeSpan(
662
+ ["col-align-" + (colDescr.align || "c")],
663
+ [col]);
664
+ cols.push(col);
665
+
666
+ if (c < nc - 1 || group.value.hskipBeforeAndAfter) {
667
+ sepwidth = utils.deflt(colDescr.postgap, arraycolsep);
668
+ if (sepwidth !== 0) {
669
+ colSep = makeSpan(["arraycolsep"], []);
670
+ colSep.style.width = sepwidth + "em";
671
+ cols.push(colSep);
672
+ }
673
+ }
674
+ }
675
+ body = makeSpan(["mtable"], cols);
676
+ return makeSpan(["mord"], [body], options.getColor());
677
+ };
678
+
679
+ groupTypes.spacing = function(group, options, prev) {
680
+ if (group.value === "\\ " || group.value === "\\space" ||
681
+ group.value === " " || group.value === "~") {
682
+ // Spaces are generated by adding an actual space. Each of these
683
+ // things has an entry in the symbols table, so these will be turned
684
+ // into appropriate outputs.
685
+ return makeSpan(
686
+ ["mord", "mspace"],
687
+ [buildCommon.mathsym(group.value, group.mode)]
688
+ );
689
+ } else {
690
+ // Other kinds of spaces are of arbitrary width. We use CSS to
691
+ // generate these.
692
+ return makeSpan(
693
+ ["mord", "mspace",
694
+ buildCommon.spacingFunctions[group.value].className]);
695
+ }
696
+ };
697
+
698
+ groupTypes.llap = function(group, options, prev) {
699
+ var inner = makeSpan(
700
+ ["inner"], [buildGroup(group.value.body, options.reset())]);
701
+ var fix = makeSpan(["fix"], []);
702
+ return makeSpan(
703
+ ["llap", options.style.cls()], [inner, fix]);
704
+ };
705
+
706
+ groupTypes.rlap = function(group, options, prev) {
707
+ var inner = makeSpan(
708
+ ["inner"], [buildGroup(group.value.body, options.reset())]);
709
+ var fix = makeSpan(["fix"], []);
710
+ return makeSpan(
711
+ ["rlap", options.style.cls()], [inner, fix]);
712
+ };
713
+
714
+ groupTypes.op = function(group, options, prev) {
715
+ // Operators are handled in the TeXbook pg. 443-444, rule 13(a).
716
+ var supGroup;
717
+ var subGroup;
718
+ var hasLimits = false;
719
+ if (group.type === "supsub" ) {
720
+ // If we have limits, supsub will pass us its group to handle. Pull
721
+ // out the superscript and subscript and set the group to the op in
722
+ // its base.
723
+ supGroup = group.value.sup;
724
+ subGroup = group.value.sub;
725
+ group = group.value.base;
726
+ hasLimits = true;
727
+ }
728
+
729
+ // Most operators have a large successor symbol, but these don't.
730
+ var noSuccessor = [
731
+ "\\smallint",
732
+ ];
733
+
734
+ var large = false;
735
+ if (options.style.size === Style.DISPLAY.size &&
736
+ group.value.symbol &&
737
+ !utils.contains(noSuccessor, group.value.body)) {
738
+
739
+ // Most symbol operators get larger in displaystyle (rule 13)
740
+ large = true;
741
+ }
742
+
743
+ var base;
744
+ var baseShift = 0;
745
+ var slant = 0;
746
+ if (group.value.symbol) {
747
+ // If this is a symbol, create the symbol.
748
+ var style = large ? "Size2-Regular" : "Size1-Regular";
749
+ base = buildCommon.makeSymbol(
750
+ group.value.body, style, "math", options.getColor(),
751
+ ["op-symbol", large ? "large-op" : "small-op", "mop"]);
752
+
753
+ // Shift the symbol so its center lies on the axis (rule 13). It
754
+ // appears that our fonts have the centers of the symbols already
755
+ // almost on the axis, so these numbers are very small. Note we
756
+ // don't actually apply this here, but instead it is used either in
757
+ // the vlist creation or separately when there are no limits.
758
+ baseShift = (base.height - base.depth) / 2 -
759
+ fontMetrics.metrics.axisHeight *
760
+ options.style.sizeMultiplier;
761
+
762
+ // The slant of the symbol is just its italic correction.
763
+ slant = base.italic;
764
+ } else {
765
+ // Otherwise, this is a text operator. Build the text from the
766
+ // operator's name.
767
+ // TODO(emily): Add a space in the middle of some of these
768
+ // operators, like \limsup
769
+ var output = [];
770
+ for (var i = 1; i < group.value.body.length; i++) {
771
+ output.push(buildCommon.mathsym(group.value.body[i], group.mode));
772
+ }
773
+ base = makeSpan(["mop"], output, options.getColor());
774
+ }
775
+
776
+ if (hasLimits) {
777
+ // IE 8 clips \int if it is in a display: inline-block. We wrap it
778
+ // in a new span so it is an inline, and works.
779
+ base = makeSpan([], [base]);
780
+
781
+ var supmid;
782
+ var supKern;
783
+ var submid;
784
+ var subKern;
785
+ // We manually have to handle the superscripts and subscripts. This,
786
+ // aside from the kern calculations, is copied from supsub.
787
+ if (supGroup) {
788
+ var sup = buildGroup(
789
+ supGroup, options.withStyle(options.style.sup()));
790
+ supmid = makeSpan(
791
+ [options.style.reset(), options.style.sup().cls()], [sup]);
792
+
793
+ supKern = Math.max(
794
+ fontMetrics.metrics.bigOpSpacing1,
795
+ fontMetrics.metrics.bigOpSpacing3 - sup.depth);
796
+ }
797
+
798
+ if (subGroup) {
799
+ var sub = buildGroup(
800
+ subGroup, options.withStyle(options.style.sub()));
801
+ submid = makeSpan(
802
+ [options.style.reset(), options.style.sub().cls()],
803
+ [sub]);
804
+
805
+ subKern = Math.max(
806
+ fontMetrics.metrics.bigOpSpacing2,
807
+ fontMetrics.metrics.bigOpSpacing4 - sub.height);
808
+ }
809
+
810
+ // Build the final group as a vlist of the possible subscript, base,
811
+ // and possible superscript.
812
+ var finalGroup;
813
+ var top;
814
+ var bottom;
815
+ if (!supGroup) {
816
+ top = base.height - baseShift;
817
+
818
+ finalGroup = buildCommon.makeVList([
819
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
820
+ {type: "elem", elem: submid},
821
+ {type: "kern", size: subKern},
822
+ {type: "elem", elem: base},
823
+ ], "top", top, options);
824
+
825
+ // Here, we shift the limits by the slant of the symbol. Note
826
+ // that we are supposed to shift the limits by 1/2 of the slant,
827
+ // but since we are centering the limits adding a full slant of
828
+ // margin will shift by 1/2 that.
829
+ finalGroup.children[0].style.marginLeft = -slant + "em";
830
+ } else if (!subGroup) {
831
+ bottom = base.depth + baseShift;
832
+
833
+ finalGroup = buildCommon.makeVList([
834
+ {type: "elem", elem: base},
835
+ {type: "kern", size: supKern},
836
+ {type: "elem", elem: supmid},
837
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
838
+ ], "bottom", bottom, options);
839
+
840
+ // See comment above about slants
841
+ finalGroup.children[1].style.marginLeft = slant + "em";
842
+ } else if (!supGroup && !subGroup) {
843
+ // This case probably shouldn't occur (this would mean the
844
+ // supsub was sending us a group with no superscript or
845
+ // subscript) but be safe.
846
+ return base;
847
+ } else {
848
+ bottom = fontMetrics.metrics.bigOpSpacing5 +
849
+ submid.height + submid.depth +
850
+ subKern +
851
+ base.depth + baseShift;
852
+
853
+ finalGroup = buildCommon.makeVList([
854
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
855
+ {type: "elem", elem: submid},
856
+ {type: "kern", size: subKern},
857
+ {type: "elem", elem: base},
858
+ {type: "kern", size: supKern},
859
+ {type: "elem", elem: supmid},
860
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
861
+ ], "bottom", bottom, options);
862
+
863
+ // See comment above about slants
864
+ finalGroup.children[0].style.marginLeft = -slant + "em";
865
+ finalGroup.children[2].style.marginLeft = slant + "em";
866
+ }
867
+
868
+ return makeSpan(["mop", "op-limits"], [finalGroup]);
869
+ } else {
870
+ if (group.value.symbol) {
871
+ base.style.top = baseShift + "em";
872
+ }
873
+
874
+ return base;
875
+ }
876
+ };
877
+
878
+ groupTypes.katex = function(group, options, prev) {
879
+ // The KaTeX logo. The offsets for the K and a were chosen to look
880
+ // good, but the offsets for the T, E, and X were taken from the
881
+ // definition of \TeX in TeX (see TeXbook pg. 356)
882
+ var k = makeSpan(
883
+ ["k"], [buildCommon.mathsym("K", group.mode)]);
884
+ var a = makeSpan(
885
+ ["a"], [buildCommon.mathsym("A", group.mode)]);
886
+
887
+ a.height = (a.height + 0.2) * 0.75;
888
+ a.depth = (a.height - 0.2) * 0.75;
889
+
890
+ var t = makeSpan(
891
+ ["t"], [buildCommon.mathsym("T", group.mode)]);
892
+ var e = makeSpan(
893
+ ["e"], [buildCommon.mathsym("E", group.mode)]);
894
+
895
+ e.height = (e.height - 0.2155);
896
+ e.depth = (e.depth + 0.2155);
897
+
898
+ var x = makeSpan(
899
+ ["x"], [buildCommon.mathsym("X", group.mode)]);
900
+
901
+ return makeSpan(
902
+ ["katex-logo", "mord"], [k, a, t, e, x], options.getColor());
903
+ };
904
+
905
+ groupTypes.overline = function(group, options, prev) {
906
+ // Overlines are handled in the TeXbook pg 443, Rule 9.
907
+
908
+ // Build the inner group in the cramped style.
909
+ var innerGroup = buildGroup(group.value.body,
910
+ options.withStyle(options.style.cramp()));
911
+
912
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
913
+ options.style.sizeMultiplier;
914
+
915
+ // Create the line above the body
916
+ var line = makeSpan(
917
+ [options.style.reset(), Style.TEXT.cls(), "overline-line"]);
918
+ line.height = ruleWidth;
919
+ line.maxFontSize = 1.0;
920
+
921
+ // Generate the vlist, with the appropriate kerns
922
+ var vlist = buildCommon.makeVList([
923
+ {type: "elem", elem: innerGroup},
924
+ {type: "kern", size: 3 * ruleWidth},
925
+ {type: "elem", elem: line},
926
+ {type: "kern", size: ruleWidth},
927
+ ], "firstBaseline", null, options);
928
+
929
+ return makeSpan(["overline", "mord"], [vlist], options.getColor());
930
+ };
931
+
932
+ groupTypes.underline = function(group, options, prev) {
933
+ // Underlines are handled in the TeXbook pg 443, Rule 10.
934
+
935
+ // Build the inner group.
936
+ var innerGroup = buildGroup(group.value.body, options);
937
+
938
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
939
+ options.style.sizeMultiplier;
940
+
941
+ // Create the line above the body
942
+ var line = makeSpan(
943
+ [options.style.reset(), Style.TEXT.cls(), "underline-line"]);
944
+ line.height = ruleWidth;
945
+ line.maxFontSize = 1.0;
946
+
947
+ // Generate the vlist, with the appropriate kerns
948
+ var vlist = buildCommon.makeVList([
949
+ {type: "kern", size: ruleWidth},
950
+ {type: "elem", elem: line},
951
+ {type: "kern", size: 3 * ruleWidth},
952
+ {type: "elem", elem: innerGroup},
953
+ ], "top", innerGroup.height, options);
954
+
955
+ return makeSpan(["underline", "mord"], [vlist], options.getColor());
956
+ };
957
+
958
+ groupTypes.sqrt = function(group, options, prev) {
959
+ // Square roots are handled in the TeXbook pg. 443, Rule 11.
960
+
961
+ // First, we do the same steps as in overline to build the inner group
962
+ // and line
963
+ var inner = buildGroup(group.value.body,
964
+ options.withStyle(options.style.cramp()));
965
+
966
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
967
+ options.style.sizeMultiplier;
968
+
969
+ var line = makeSpan(
970
+ [options.style.reset(), Style.TEXT.cls(), "sqrt-line"], [],
971
+ options.getColor());
972
+ line.height = ruleWidth;
973
+ line.maxFontSize = 1.0;
974
+
975
+ var phi = ruleWidth;
976
+ if (options.style.id < Style.TEXT.id) {
977
+ phi = fontMetrics.metrics.xHeight;
978
+ }
979
+
980
+ // Calculate the clearance between the body and line
981
+ var lineClearance = ruleWidth + phi / 4;
982
+
983
+ var innerHeight =
984
+ (inner.height + inner.depth) * options.style.sizeMultiplier;
985
+ var minDelimiterHeight = innerHeight + lineClearance + ruleWidth;
986
+
987
+ // Create a \surd delimiter of the required minimum size
988
+ var delim = makeSpan(["sqrt-sign"], [
989
+ delimiter.customSizedDelim("\\surd", minDelimiterHeight,
990
+ false, options, group.mode)],
991
+ options.getColor());
992
+
993
+ var delimDepth = (delim.height + delim.depth) - ruleWidth;
994
+
995
+ // Adjust the clearance based on the delimiter size
996
+ if (delimDepth > inner.height + inner.depth + lineClearance) {
997
+ lineClearance =
998
+ (lineClearance + delimDepth - inner.height - inner.depth) / 2;
999
+ }
1000
+
1001
+ // Shift the delimiter so that its top lines up with the top of the line
1002
+ var delimShift = -(inner.height + lineClearance + ruleWidth) + delim.height;
1003
+ delim.style.top = delimShift + "em";
1004
+ delim.height -= delimShift;
1005
+ delim.depth += delimShift;
1006
+
1007
+ // We add a special case here, because even when `inner` is empty, we
1008
+ // still get a line. So, we use a simple heuristic to decide if we
1009
+ // should omit the body entirely. (note this doesn't work for something
1010
+ // like `\sqrt{\rlap{x}}`, but if someone is doing that they deserve for
1011
+ // it not to work.
1012
+ var body;
1013
+ if (inner.height === 0 && inner.depth === 0) {
1014
+ body = makeSpan();
1015
+ } else {
1016
+ body = buildCommon.makeVList([
1017
+ {type: "elem", elem: inner},
1018
+ {type: "kern", size: lineClearance},
1019
+ {type: "elem", elem: line},
1020
+ {type: "kern", size: ruleWidth},
1021
+ ], "firstBaseline", null, options);
1022
+ }
1023
+
1024
+ if (!group.value.index) {
1025
+ return makeSpan(["sqrt", "mord"], [delim, body]);
1026
+ } else {
1027
+ // Handle the optional root index
1028
+
1029
+ // The index is always in scriptscript style
1030
+ var root = buildGroup(
1031
+ group.value.index,
1032
+ options.withStyle(Style.SCRIPTSCRIPT));
1033
+ var rootWrap = makeSpan(
1034
+ [options.style.reset(), Style.SCRIPTSCRIPT.cls()],
1035
+ [root]);
1036
+
1037
+ // Figure out the height and depth of the inner part
1038
+ var innerRootHeight = Math.max(delim.height, body.height);
1039
+ var innerRootDepth = Math.max(delim.depth, body.depth);
1040
+
1041
+ // The amount the index is shifted by. This is taken from the TeX
1042
+ // source, in the definition of `\r@@t`.
1043
+ var toShift = 0.6 * (innerRootHeight - innerRootDepth);
1044
+
1045
+ // Build a VList with the superscript shifted up correctly
1046
+ var rootVList = buildCommon.makeVList(
1047
+ [{type: "elem", elem: rootWrap}],
1048
+ "shift", -toShift, options);
1049
+ // Add a class surrounding it so we can add on the appropriate
1050
+ // kerning
1051
+ var rootVListWrap = makeSpan(["root"], [rootVList]);
1052
+
1053
+ return makeSpan(["sqrt", "mord"], [rootVListWrap, delim, body]);
1054
+ }
1055
+ };
1056
+
1057
+ groupTypes.sizing = function(group, options, prev) {
1058
+ // Handle sizing operators like \Huge. Real TeX doesn't actually allow
1059
+ // these functions inside of math expressions, so we do some special
1060
+ // handling.
1061
+ var inner = buildExpression(group.value.value,
1062
+ options.withSize(group.value.size), prev);
1063
+
1064
+ var span = makeSpan(["mord"],
1065
+ [makeSpan(["sizing", "reset-" + options.size, group.value.size,
1066
+ options.style.cls()],
1067
+ inner)]);
1068
+
1069
+ // Calculate the correct maxFontSize manually
1070
+ var fontSize = buildCommon.sizingMultiplier[group.value.size];
1071
+ span.maxFontSize = fontSize * options.style.sizeMultiplier;
1072
+
1073
+ return span;
1074
+ };
1075
+
1076
+ groupTypes.styling = function(group, options, prev) {
1077
+ // Style changes are handled in the TeXbook on pg. 442, Rule 3.
1078
+
1079
+ // Figure out what style we're changing to.
1080
+ var style = {
1081
+ "display": Style.DISPLAY,
1082
+ "text": Style.TEXT,
1083
+ "script": Style.SCRIPT,
1084
+ "scriptscript": Style.SCRIPTSCRIPT,
1085
+ };
1086
+
1087
+ var newStyle = style[group.value.style];
1088
+
1089
+ // Build the inner expression in the new style.
1090
+ var inner = buildExpression(
1091
+ group.value.value, options.withStyle(newStyle), prev);
1092
+
1093
+ return makeSpan([options.style.reset(), newStyle.cls()], inner);
1094
+ };
1095
+
1096
+ groupTypes.font = function(group, options, prev) {
1097
+ var font = group.value.font;
1098
+ return buildGroup(group.value.body, options.withFont(font), prev);
1099
+ };
1100
+
1101
+ groupTypes.delimsizing = function(group, options, prev) {
1102
+ var delim = group.value.value;
1103
+
1104
+ if (delim === ".") {
1105
+ // Empty delimiters still count as elements, even though they don't
1106
+ // show anything.
1107
+ return makeSpan([groupToType[group.value.delimType]]);
1108
+ }
1109
+
1110
+ // Use delimiter.sizedDelim to generate the delimiter.
1111
+ return makeSpan(
1112
+ [groupToType[group.value.delimType]],
1113
+ [delimiter.sizedDelim(
1114
+ delim, group.value.size, options, group.mode)]);
1115
+ };
1116
+
1117
+ groupTypes.leftright = function(group, options, prev) {
1118
+ // Build the inner expression
1119
+ var inner = buildExpression(group.value.body, options.reset());
1120
+
1121
+ var innerHeight = 0;
1122
+ var innerDepth = 0;
1123
+
1124
+ // Calculate its height and depth
1125
+ for (var i = 0; i < inner.length; i++) {
1126
+ innerHeight = Math.max(inner[i].height, innerHeight);
1127
+ innerDepth = Math.max(inner[i].depth, innerDepth);
1128
+ }
1129
+
1130
+ // The size of delimiters is the same, regardless of what style we are
1131
+ // in. Thus, to correctly calculate the size of delimiter we need around
1132
+ // a group, we scale down the inner size based on the size.
1133
+ innerHeight *= options.style.sizeMultiplier;
1134
+ innerDepth *= options.style.sizeMultiplier;
1135
+
1136
+ var leftDelim;
1137
+ if (group.value.left === ".") {
1138
+ // Empty delimiters in \left and \right make null delimiter spaces.
1139
+ leftDelim = makeNullDelimiter(options);
1140
+ } else {
1141
+ // Otherwise, use leftRightDelim to generate the correct sized
1142
+ // delimiter.
1143
+ leftDelim = delimiter.leftRightDelim(
1144
+ group.value.left, innerHeight, innerDepth, options,
1145
+ group.mode);
1146
+ }
1147
+ // Add it to the beginning of the expression
1148
+ inner.unshift(leftDelim);
1149
+
1150
+ var rightDelim;
1151
+ // Same for the right delimiter
1152
+ if (group.value.right === ".") {
1153
+ rightDelim = makeNullDelimiter(options);
1154
+ } else {
1155
+ rightDelim = delimiter.leftRightDelim(
1156
+ group.value.right, innerHeight, innerDepth, options,
1157
+ group.mode);
1158
+ }
1159
+ // Add it to the end of the expression.
1160
+ inner.push(rightDelim);
1161
+
1162
+ return makeSpan(
1163
+ ["minner", options.style.cls()], inner, options.getColor());
1164
+ };
1165
+
1166
+ groupTypes.rule = function(group, options, prev) {
1167
+ // Make an empty span for the rule
1168
+ var rule = makeSpan(["mord", "rule"], [], options.getColor());
1169
+
1170
+ // Calculate the shift, width, and height of the rule, and account for units
1171
+ var shift = 0;
1172
+ if (group.value.shift) {
1173
+ shift = group.value.shift.number;
1174
+ if (group.value.shift.unit === "ex") {
1175
+ shift *= fontMetrics.metrics.xHeight;
1176
+ }
1177
+ }
1178
+
1179
+ var width = group.value.width.number;
1180
+ if (group.value.width.unit === "ex") {
1181
+ width *= fontMetrics.metrics.xHeight;
1182
+ }
1183
+
1184
+ var height = group.value.height.number;
1185
+ if (group.value.height.unit === "ex") {
1186
+ height *= fontMetrics.metrics.xHeight;
1187
+ }
1188
+
1189
+ // The sizes of rules are absolute, so make it larger if we are in a
1190
+ // smaller style.
1191
+ shift /= options.style.sizeMultiplier;
1192
+ width /= options.style.sizeMultiplier;
1193
+ height /= options.style.sizeMultiplier;
1194
+
1195
+ // Style the rule to the right size
1196
+ rule.style.borderRightWidth = width + "em";
1197
+ rule.style.borderTopWidth = height + "em";
1198
+ rule.style.bottom = shift + "em";
1199
+
1200
+ // Record the height and width
1201
+ rule.width = width;
1202
+ rule.height = height + shift;
1203
+ rule.depth = -shift;
1204
+
1205
+ return rule;
1206
+ };
1207
+
1208
+ groupTypes.accent = function(group, options, prev) {
1209
+ // Accents are handled in the TeXbook pg. 443, rule 12.
1210
+ var base = group.value.base;
1211
+
1212
+ var supsubGroup;
1213
+ if (group.type === "supsub") {
1214
+ // If our base is a character box, and we have superscripts and
1215
+ // subscripts, the supsub will defer to us. In particular, we want
1216
+ // to attach the superscripts and subscripts to the inner body (so
1217
+ // that the position of the superscripts and subscripts won't be
1218
+ // affected by the height of the accent). We accomplish this by
1219
+ // sticking the base of the accent into the base of the supsub, and
1220
+ // rendering that, while keeping track of where the accent is.
1221
+
1222
+ // The supsub group is the group that was passed in
1223
+ var supsub = group;
1224
+ // The real accent group is the base of the supsub group
1225
+ group = supsub.value.base;
1226
+ // The character box is the base of the accent group
1227
+ base = group.value.base;
1228
+ // Stick the character box into the base of the supsub group
1229
+ supsub.value.base = base;
1230
+
1231
+ // Rerender the supsub group with its new base, and store that
1232
+ // result.
1233
+ supsubGroup = buildGroup(
1234
+ supsub, options.reset(), prev);
1235
+ }
1236
+
1237
+ // Build the base group
1238
+ var body = buildGroup(
1239
+ base, options.withStyle(options.style.cramp()));
1240
+
1241
+ // Calculate the skew of the accent. This is based on the line "If the
1242
+ // nucleus is not a single character, let s = 0; otherwise set s to the
1243
+ // kern amount for the nucleus followed by the \skewchar of its font."
1244
+ // Note that our skew metrics are just the kern between each character
1245
+ // and the skewchar.
1246
+ var skew;
1247
+ if (isCharacterBox(base)) {
1248
+ // If the base is a character box, then we want the skew of the
1249
+ // innermost character. To do that, we find the innermost character:
1250
+ var baseChar = getBaseElem(base);
1251
+ // Then, we render its group to get the symbol inside it
1252
+ var baseGroup = buildGroup(
1253
+ baseChar, options.withStyle(options.style.cramp()));
1254
+ // Finally, we pull the skew off of the symbol.
1255
+ skew = baseGroup.skew;
1256
+ // Note that we now throw away baseGroup, because the layers we
1257
+ // removed with getBaseElem might contain things like \color which
1258
+ // we can't get rid of.
1259
+ // TODO(emily): Find a better way to get the skew
1260
+ } else {
1261
+ skew = 0;
1262
+ }
1263
+
1264
+ // calculate the amount of space between the body and the accent
1265
+ var clearance = Math.min(body.height, fontMetrics.metrics.xHeight);
1266
+
1267
+ // Build the accent
1268
+ var accent = buildCommon.makeSymbol(
1269
+ group.value.accent, "Main-Regular", "math", options.getColor());
1270
+ // Remove the italic correction of the accent, because it only serves to
1271
+ // shift the accent over to a place we don't want.
1272
+ accent.italic = 0;
1273
+
1274
+ // The \vec character that the fonts use is a combining character, and
1275
+ // thus shows up much too far to the left. To account for this, we add a
1276
+ // specific class which shifts the accent over to where we want it.
1277
+ // TODO(emily): Fix this in a better way, like by changing the font
1278
+ var vecClass = group.value.accent === "\\vec" ? "accent-vec" : null;
1279
+
1280
+ var accentBody = makeSpan(["accent-body", vecClass], [
1281
+ makeSpan([], [accent])]);
1282
+
1283
+ accentBody = buildCommon.makeVList([
1284
+ {type: "elem", elem: body},
1285
+ {type: "kern", size: -clearance},
1286
+ {type: "elem", elem: accentBody},
1287
+ ], "firstBaseline", null, options);
1288
+
1289
+ // Shift the accent over by the skew. Note we shift by twice the skew
1290
+ // because we are centering the accent, so by adding 2*skew to the left,
1291
+ // we shift it to the right by 1*skew.
1292
+ accentBody.children[1].style.marginLeft = 2 * skew + "em";
1293
+
1294
+ var accentWrap = makeSpan(["mord", "accent"], [accentBody]);
1295
+
1296
+ if (supsubGroup) {
1297
+ // Here, we replace the "base" child of the supsub with our newly
1298
+ // generated accent.
1299
+ supsubGroup.children[0] = accentWrap;
1300
+
1301
+ // Since we don't rerun the height calculation after replacing the
1302
+ // accent, we manually recalculate height.
1303
+ supsubGroup.height = Math.max(accentWrap.height, supsubGroup.height);
1304
+
1305
+ // Accents should always be ords, even when their innards are not.
1306
+ supsubGroup.classes[0] = "mord";
1307
+
1308
+ return supsubGroup;
1309
+ } else {
1310
+ return accentWrap;
1311
+ }
1312
+ };
1313
+
1314
+ groupTypes.phantom = function(group, options, prev) {
1315
+ var elements = buildExpression(
1316
+ group.value.value,
1317
+ options.withPhantom(),
1318
+ prev
1319
+ );
1320
+
1321
+ // \phantom isn't supposed to affect the elements it contains.
1322
+ // See "color" for more details.
1323
+ return new buildCommon.makeFragment(elements);
1324
+ };
1325
+
1326
+ /**
1327
+ * buildGroup is the function that takes a group and calls the correct groupType
1328
+ * function for it. It also handles the interaction of size and style changes
1329
+ * between parents and children.
1330
+ */
1331
+ var buildGroup = function(group, options, prev) {
1332
+ if (!group) {
1333
+ return makeSpan();
1334
+ }
1335
+
1336
+ if (groupTypes[group.type]) {
1337
+ // Call the groupTypes function
1338
+ var groupNode = groupTypes[group.type](group, options, prev);
1339
+ var multiplier;
1340
+
1341
+ // If the style changed between the parent and the current group,
1342
+ // account for the size difference
1343
+ if (options.style !== options.parentStyle) {
1344
+ multiplier = options.style.sizeMultiplier /
1345
+ options.parentStyle.sizeMultiplier;
1346
+
1347
+ groupNode.height *= multiplier;
1348
+ groupNode.depth *= multiplier;
1349
+ }
1350
+
1351
+ // If the size changed between the parent and the current group, account
1352
+ // for that size difference.
1353
+ if (options.size !== options.parentSize) {
1354
+ multiplier = buildCommon.sizingMultiplier[options.size] /
1355
+ buildCommon.sizingMultiplier[options.parentSize];
1356
+
1357
+ groupNode.height *= multiplier;
1358
+ groupNode.depth *= multiplier;
1359
+ }
1360
+
1361
+ return groupNode;
1362
+ } else {
1363
+ throw new ParseError(
1364
+ "Got group of unknown type: '" + group.type + "'");
1365
+ }
1366
+ };
1367
+
1368
+ /**
1369
+ * Take an entire parse tree, and build it into an appropriate set of HTML
1370
+ * nodes.
1371
+ */
1372
+ var buildHTML = function(tree, options) {
1373
+ // buildExpression is destructive, so we need to make a clone
1374
+ // of the incoming tree so that it isn't accidentally changed
1375
+ tree = JSON.parse(JSON.stringify(tree));
1376
+
1377
+ // Build the expression contained in the tree
1378
+ var expression = buildExpression(tree, options);
1379
+ var body = makeSpan(["base", options.style.cls()], expression);
1380
+
1381
+ // Add struts, which ensure that the top of the HTML element falls at the
1382
+ // height of the expression, and the bottom of the HTML element falls at the
1383
+ // depth of the expression.
1384
+ var topStrut = makeSpan(["strut"]);
1385
+ var bottomStrut = makeSpan(["strut", "bottom"]);
1386
+
1387
+ topStrut.style.height = body.height + "em";
1388
+ bottomStrut.style.height = (body.height + body.depth) + "em";
1389
+ // We'd like to use `vertical-align: top` but in IE 9 this lowers the
1390
+ // baseline of the box to the bottom of this strut (instead staying in the
1391
+ // normal place) so we use an absolute value for vertical-align instead
1392
+ bottomStrut.style.verticalAlign = -body.depth + "em";
1393
+
1394
+ // Wrap the struts and body together
1395
+ var htmlNode = makeSpan(["katex-html"], [topStrut, bottomStrut, body]);
1396
+
1397
+ htmlNode.setAttribute("aria-hidden", "true");
1398
+
1399
+ return htmlNode;
1400
+ };
1401
+
1402
+ module.exports = buildHTML;
modules/tokenize_latex/third_party/katex/src/buildMathML.js ADDED
@@ -0,0 +1,533 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file converts a parse tree into a cooresponding MathML tree. The main
3
+ * entry point is the `buildMathML` function, which takes a parse tree from the
4
+ * parser.
5
+ */
6
+
7
+ var buildCommon = require("./buildCommon");
8
+ var fontMetrics = require("./fontMetrics");
9
+ var mathMLTree = require("./mathMLTree");
10
+ var ParseError = require("./ParseError");
11
+ var symbols = require("./symbols");
12
+ var utils = require("./utils");
13
+
14
+ var makeSpan = buildCommon.makeSpan;
15
+ var fontMap = buildCommon.fontMap;
16
+
17
+ /**
18
+ * Takes a symbol and converts it into a MathML text node after performing
19
+ * optional replacement from symbols.js.
20
+ */
21
+ var makeText = function(text, mode) {
22
+ if (symbols[mode][text] && symbols[mode][text].replace) {
23
+ text = symbols[mode][text].replace;
24
+ }
25
+
26
+ return new mathMLTree.TextNode(text);
27
+ };
28
+
29
+ /**
30
+ * Returns the math variant as a string or null if none is required.
31
+ */
32
+ var getVariant = function(group, options) {
33
+ var font = options.font;
34
+ if (!font) {
35
+ return null;
36
+ }
37
+
38
+ var mode = group.mode;
39
+ if (font === "mathit") {
40
+ return "italic";
41
+ }
42
+
43
+ var value = group.value;
44
+ if (utils.contains(["\\imath", "\\jmath"], value)) {
45
+ return null;
46
+ }
47
+
48
+ if (symbols[mode][value] && symbols[mode][value].replace) {
49
+ value = symbols[mode][value].replace;
50
+ }
51
+
52
+ var fontName = fontMap[font].fontName;
53
+ if (fontMetrics.getCharacterMetrics(value, fontName)) {
54
+ return fontMap[options.font].variant;
55
+ }
56
+
57
+ return null;
58
+ };
59
+
60
+ /**
61
+ * Functions for handling the different types of groups found in the parse
62
+ * tree. Each function should take a parse group and return a MathML node.
63
+ */
64
+ var groupTypes = {};
65
+
66
+ groupTypes.mathord = function(group, options) {
67
+ var node = new mathMLTree.MathNode(
68
+ "mi",
69
+ [makeText(group.value, group.mode)]);
70
+
71
+ var variant = getVariant(group, options);
72
+ if (variant) {
73
+ node.setAttribute("mathvariant", variant);
74
+ }
75
+ return node;
76
+ };
77
+
78
+ groupTypes.textord = function(group, options) {
79
+ var text = makeText(group.value, group.mode);
80
+
81
+ var variant = getVariant(group, options) || "normal";
82
+
83
+ var node;
84
+ if (/[0-9]/.test(group.value)) {
85
+ // TODO(kevinb) merge adjacent <mn> nodes
86
+ // do it as a post processing step
87
+ node = new mathMLTree.MathNode("mn", [text]);
88
+ if (options.font) {
89
+ node.setAttribute("mathvariant", variant);
90
+ }
91
+ } else {
92
+ node = new mathMLTree.MathNode("mi", [text]);
93
+ node.setAttribute("mathvariant", variant);
94
+ }
95
+
96
+ return node;
97
+ };
98
+
99
+ groupTypes.bin = function(group) {
100
+ var node = new mathMLTree.MathNode(
101
+ "mo", [makeText(group.value, group.mode)]);
102
+
103
+ return node;
104
+ };
105
+
106
+ groupTypes.rel = function(group) {
107
+ var node = new mathMLTree.MathNode(
108
+ "mo", [makeText(group.value, group.mode)]);
109
+
110
+ return node;
111
+ };
112
+
113
+ groupTypes.open = function(group) {
114
+ var node = new mathMLTree.MathNode(
115
+ "mo", [makeText(group.value, group.mode)]);
116
+
117
+ return node;
118
+ };
119
+
120
+ groupTypes.close = function(group) {
121
+ var node = new mathMLTree.MathNode(
122
+ "mo", [makeText(group.value, group.mode)]);
123
+
124
+ return node;
125
+ };
126
+
127
+ groupTypes.inner = function(group) {
128
+ var node = new mathMLTree.MathNode(
129
+ "mo", [makeText(group.value, group.mode)]);
130
+
131
+ return node;
132
+ };
133
+
134
+ groupTypes.punct = function(group) {
135
+ var node = new mathMLTree.MathNode(
136
+ "mo", [makeText(group.value, group.mode)]);
137
+
138
+ node.setAttribute("separator", "true");
139
+
140
+ return node;
141
+ };
142
+
143
+ groupTypes.ordgroup = function(group, options) {
144
+ var inner = buildExpression(group.value, options);
145
+
146
+ var node = new mathMLTree.MathNode("mrow", inner);
147
+
148
+ return node;
149
+ };
150
+
151
+ groupTypes.text = function(group, options) {
152
+ var inner = buildExpression(group.value.body, options);
153
+
154
+ var node = new mathMLTree.MathNode("mtext", inner);
155
+
156
+ return node;
157
+ };
158
+
159
+ groupTypes.color = function(group, options) {
160
+ var inner = buildExpression(group.value.value, options);
161
+
162
+ var node = new mathMLTree.MathNode("mstyle", inner);
163
+
164
+ node.setAttribute("mathcolor", group.value.color);
165
+
166
+ return node;
167
+ };
168
+
169
+ groupTypes.supsub = function(group, options) {
170
+ var children = [buildGroup(group.value.base, options)];
171
+
172
+ if (group.value.sub) {
173
+ children.push(buildGroup(group.value.sub, options));
174
+ }
175
+
176
+ if (group.value.sup) {
177
+ children.push(buildGroup(group.value.sup, options));
178
+ }
179
+
180
+ var nodeType;
181
+ if (!group.value.sub) {
182
+ nodeType = "msup";
183
+ } else if (!group.value.sup) {
184
+ nodeType = "msub";
185
+ } else {
186
+ nodeType = "msubsup";
187
+ }
188
+
189
+ var node = new mathMLTree.MathNode(nodeType, children);
190
+
191
+ return node;
192
+ };
193
+
194
+ groupTypes.genfrac = function(group, options) {
195
+ var node = new mathMLTree.MathNode(
196
+ "mfrac",
197
+ [buildGroup(group.value.numer, options),
198
+ buildGroup(group.value.denom, options)]);
199
+
200
+ if (!group.value.hasBarLine) {
201
+ node.setAttribute("linethickness", "0px");
202
+ }
203
+
204
+ if (group.value.leftDelim != null || group.value.rightDelim != null) {
205
+ var withDelims = [];
206
+
207
+ if (group.value.leftDelim != null) {
208
+ var leftOp = new mathMLTree.MathNode(
209
+ "mo", [new mathMLTree.TextNode(group.value.leftDelim)]);
210
+
211
+ leftOp.setAttribute("fence", "true");
212
+
213
+ withDelims.push(leftOp);
214
+ }
215
+
216
+ withDelims.push(node);
217
+
218
+ if (group.value.rightDelim != null) {
219
+ var rightOp = new mathMLTree.MathNode(
220
+ "mo", [new mathMLTree.TextNode(group.value.rightDelim)]);
221
+
222
+ rightOp.setAttribute("fence", "true");
223
+
224
+ withDelims.push(rightOp);
225
+ }
226
+
227
+ var outerNode = new mathMLTree.MathNode("mrow", withDelims);
228
+
229
+ return outerNode;
230
+ }
231
+
232
+ return node;
233
+ };
234
+
235
+ groupTypes.array = function(group, options) {
236
+ return new mathMLTree.MathNode(
237
+ "mtable", group.value.body.map(function(row) {
238
+ return new mathMLTree.MathNode(
239
+ "mtr", row.map(function(cell) {
240
+ return new mathMLTree.MathNode(
241
+ "mtd", [buildGroup(cell, options)]);
242
+ }));
243
+ }));
244
+ };
245
+
246
+ groupTypes.sqrt = function(group, options) {
247
+ var node;
248
+ if (group.value.index) {
249
+ node = new mathMLTree.MathNode(
250
+ "mroot", [
251
+ buildGroup(group.value.body, options),
252
+ buildGroup(group.value.index, options),
253
+ ]);
254
+ } else {
255
+ node = new mathMLTree.MathNode(
256
+ "msqrt", [buildGroup(group.value.body, options)]);
257
+ }
258
+
259
+ return node;
260
+ };
261
+
262
+ groupTypes.leftright = function(group, options) {
263
+ var inner = buildExpression(group.value.body, options);
264
+
265
+ if (group.value.left !== ".") {
266
+ var leftNode = new mathMLTree.MathNode(
267
+ "mo", [makeText(group.value.left, group.mode)]);
268
+
269
+ leftNode.setAttribute("fence", "true");
270
+
271
+ inner.unshift(leftNode);
272
+ }
273
+
274
+ if (group.value.right !== ".") {
275
+ var rightNode = new mathMLTree.MathNode(
276
+ "mo", [makeText(group.value.right, group.mode)]);
277
+
278
+ rightNode.setAttribute("fence", "true");
279
+
280
+ inner.push(rightNode);
281
+ }
282
+
283
+ var outerNode = new mathMLTree.MathNode("mrow", inner);
284
+
285
+ return outerNode;
286
+ };
287
+
288
+ groupTypes.accent = function(group, options) {
289
+ var accentNode = new mathMLTree.MathNode(
290
+ "mo", [makeText(group.value.accent, group.mode)]);
291
+
292
+ var node = new mathMLTree.MathNode(
293
+ "mover",
294
+ [buildGroup(group.value.base, options),
295
+ accentNode]);
296
+
297
+ node.setAttribute("accent", "true");
298
+
299
+ return node;
300
+ };
301
+
302
+ groupTypes.spacing = function(group) {
303
+ var node;
304
+
305
+ if (group.value === "\\ " || group.value === "\\space" ||
306
+ group.value === " " || group.value === "~") {
307
+ node = new mathMLTree.MathNode(
308
+ "mtext", [new mathMLTree.TextNode("\u00a0")]);
309
+ } else {
310
+ node = new mathMLTree.MathNode("mspace");
311
+
312
+ node.setAttribute(
313
+ "width", buildCommon.spacingFunctions[group.value].size);
314
+ }
315
+
316
+ return node;
317
+ };
318
+
319
+ groupTypes.op = function(group) {
320
+ var node;
321
+
322
+ // TODO(emily): handle big operators using the `largeop` attribute
323
+
324
+ if (group.value.symbol) {
325
+ // This is a symbol. Just add the symbol.
326
+ node = new mathMLTree.MathNode(
327
+ "mo", [makeText(group.value.body, group.mode)]);
328
+ } else {
329
+ // This is a text operator. Add all of the characters from the
330
+ // operator's name.
331
+ // TODO(emily): Add a space in the middle of some of these
332
+ // operators, like \limsup.
333
+ node = new mathMLTree.MathNode(
334
+ "mi", [new mathMLTree.TextNode(group.value.body.slice(1))]);
335
+ }
336
+
337
+ return node;
338
+ };
339
+
340
+ groupTypes.katex = function(group) {
341
+ var node = new mathMLTree.MathNode(
342
+ "mtext", [new mathMLTree.TextNode("KaTeX")]);
343
+
344
+ return node;
345
+ };
346
+
347
+ groupTypes.font = function(group, options) {
348
+ var font = group.value.font;
349
+ return buildGroup(group.value.body, options.withFont(font));
350
+ };
351
+
352
+ groupTypes.delimsizing = function(group) {
353
+ var children = [];
354
+
355
+ if (group.value.value !== ".") {
356
+ children.push(makeText(group.value.value, group.mode));
357
+ }
358
+
359
+ var node = new mathMLTree.MathNode("mo", children);
360
+
361
+ if (group.value.delimType === "open" ||
362
+ group.value.delimType === "close") {
363
+ // Only some of the delimsizing functions act as fences, and they
364
+ // return "open" or "close" delimTypes.
365
+ node.setAttribute("fence", "true");
366
+ } else {
367
+ // Explicitly disable fencing if it's not a fence, to override the
368
+ // defaults.
369
+ node.setAttribute("fence", "false");
370
+ }
371
+
372
+ return node;
373
+ };
374
+
375
+ groupTypes.styling = function(group, options) {
376
+ var inner = buildExpression(group.value.value, options);
377
+
378
+ var node = new mathMLTree.MathNode("mstyle", inner);
379
+
380
+ var styleAttributes = {
381
+ "display": ["0", "true"],
382
+ "text": ["0", "false"],
383
+ "script": ["1", "false"],
384
+ "scriptscript": ["2", "false"],
385
+ };
386
+
387
+ var attr = styleAttributes[group.value.style];
388
+
389
+ node.setAttribute("scriptlevel", attr[0]);
390
+ node.setAttribute("displaystyle", attr[1]);
391
+
392
+ return node;
393
+ };
394
+
395
+ groupTypes.sizing = function(group, options) {
396
+ var inner = buildExpression(group.value.value, options);
397
+
398
+ var node = new mathMLTree.MathNode("mstyle", inner);
399
+
400
+ // TODO(emily): This doesn't produce the correct size for nested size
401
+ // changes, because we don't keep state of what style we're currently
402
+ // in, so we can't reset the size to normal before changing it. Now
403
+ // that we're passing an options parameter we should be able to fix
404
+ // this.
405
+ node.setAttribute(
406
+ "mathsize", buildCommon.sizingMultiplier[group.value.size] + "em");
407
+
408
+ return node;
409
+ };
410
+
411
+ groupTypes.overline = function(group, options) {
412
+ var operator = new mathMLTree.MathNode(
413
+ "mo", [new mathMLTree.TextNode("\u203e")]);
414
+ operator.setAttribute("stretchy", "true");
415
+
416
+ var node = new mathMLTree.MathNode(
417
+ "mover",
418
+ [buildGroup(group.value.body, options),
419
+ operator]);
420
+ node.setAttribute("accent", "true");
421
+
422
+ return node;
423
+ };
424
+
425
+ groupTypes.underline = function(group, options) {
426
+ var operator = new mathMLTree.MathNode(
427
+ "mo", [new mathMLTree.TextNode("\u203e")]);
428
+ operator.setAttribute("stretchy", "true");
429
+
430
+ var node = new mathMLTree.MathNode(
431
+ "munder",
432
+ [buildGroup(group.value.body, options),
433
+ operator]);
434
+ node.setAttribute("accentunder", "true");
435
+
436
+ return node;
437
+ };
438
+
439
+ groupTypes.rule = function(group) {
440
+ // TODO(emily): Figure out if there's an actual way to draw black boxes
441
+ // in MathML.
442
+ var node = new mathMLTree.MathNode("mrow");
443
+
444
+ return node;
445
+ };
446
+
447
+ groupTypes.llap = function(group, options) {
448
+ var node = new mathMLTree.MathNode(
449
+ "mpadded", [buildGroup(group.value.body, options)]);
450
+
451
+ node.setAttribute("lspace", "-1width");
452
+ node.setAttribute("width", "0px");
453
+
454
+ return node;
455
+ };
456
+
457
+ groupTypes.rlap = function(group, options) {
458
+ var node = new mathMLTree.MathNode(
459
+ "mpadded", [buildGroup(group.value.body, options)]);
460
+
461
+ node.setAttribute("width", "0px");
462
+
463
+ return node;
464
+ };
465
+
466
+ groupTypes.phantom = function(group, options, prev) {
467
+ var inner = buildExpression(group.value.value, options);
468
+ return new mathMLTree.MathNode("mphantom", inner);
469
+ };
470
+
471
+ /**
472
+ * Takes a list of nodes, builds them, and returns a list of the generated
473
+ * MathML nodes. A little simpler than the HTML version because we don't do any
474
+ * previous-node handling.
475
+ */
476
+ var buildExpression = function(expression, options) {
477
+ var groups = [];
478
+ for (var i = 0; i < expression.length; i++) {
479
+ var group = expression[i];
480
+ groups.push(buildGroup(group, options));
481
+ }
482
+ return groups;
483
+ };
484
+
485
+ /**
486
+ * Takes a group from the parser and calls the appropriate groupTypes function
487
+ * on it to produce a MathML node.
488
+ */
489
+ var buildGroup = function(group, options) {
490
+ if (!group) {
491
+ return new mathMLTree.MathNode("mrow");
492
+ }
493
+
494
+ if (groupTypes[group.type]) {
495
+ // Call the groupTypes function
496
+ return groupTypes[group.type](group, options);
497
+ } else {
498
+ throw new ParseError(
499
+ "Got group of unknown type: '" + group.type + "'");
500
+ }
501
+ };
502
+
503
+ /**
504
+ * Takes a full parse tree and settings and builds a MathML representation of
505
+ * it. In particular, we put the elements from building the parse tree into a
506
+ * <semantics> tag so we can also include that TeX source as an annotation.
507
+ *
508
+ * Note that we actually return a domTree element with a `<math>` inside it so
509
+ * we can do appropriate styling.
510
+ */
511
+ var buildMathML = function(tree, texExpression, options) {
512
+ var expression = buildExpression(tree, options);
513
+
514
+ // Wrap up the expression in an mrow so it is presented in the semantics
515
+ // tag correctly.
516
+ var wrapper = new mathMLTree.MathNode("mrow", expression);
517
+
518
+ // Build a TeX annotation of the source
519
+ var annotation = new mathMLTree.MathNode(
520
+ "annotation", [new mathMLTree.TextNode(texExpression)]);
521
+
522
+ annotation.setAttribute("encoding", "application/x-tex");
523
+
524
+ var semantics = new mathMLTree.MathNode(
525
+ "semantics", [wrapper, annotation]);
526
+
527
+ var math = new mathMLTree.MathNode("math", [semantics]);
528
+
529
+ // You can't style <math> nodes, so we wrap the node in a span.
530
+ return makeSpan(["katex-mathml"], [math]);
531
+ };
532
+
533
+ module.exports = buildMathML;
modules/tokenize_latex/third_party/katex/src/buildTree.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var buildHTML = require("./buildHTML");
2
+ var buildMathML = require("./buildMathML");
3
+ var buildCommon = require("./buildCommon");
4
+ var Options = require("./Options");
5
+ var Settings = require("./Settings");
6
+ var Style = require("./Style");
7
+
8
+ var makeSpan = buildCommon.makeSpan;
9
+
10
+ var buildTree = function(tree, expression, settings) {
11
+ settings = settings || new Settings({});
12
+
13
+ var startStyle = Style.TEXT;
14
+ if (settings.displayMode) {
15
+ startStyle = Style.DISPLAY;
16
+ }
17
+
18
+ // Setup the default options
19
+ var options = new Options({
20
+ style: startStyle,
21
+ size: "size5",
22
+ });
23
+
24
+ // `buildHTML` sometimes messes with the parse tree (like turning bins ->
25
+ // ords), so we build the MathML version first.
26
+ var mathMLNode = buildMathML(tree, expression, options);
27
+ var htmlNode = buildHTML(tree, options);
28
+
29
+ var katexNode = makeSpan(["katex"], [
30
+ mathMLNode, htmlNode,
31
+ ]);
32
+
33
+ if (settings.displayMode) {
34
+ return makeSpan(["katex-display"], [katexNode]);
35
+ } else {
36
+ return katexNode;
37
+ }
38
+ };
39
+
40
+ module.exports = buildTree;
modules/tokenize_latex/third_party/katex/src/delimiter.js ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file deals with creating delimiters of various sizes. The TeXbook
3
+ * discusses these routines on page 441-442, in the "Another subroutine sets box
4
+ * x to a specified variable delimiter" paragraph.
5
+ *
6
+ * There are three main routines here. `makeSmallDelim` makes a delimiter in the
7
+ * normal font, but in either text, script, or scriptscript style.
8
+ * `makeLargeDelim` makes a delimiter in textstyle, but in one of the Size1,
9
+ * Size2, Size3, or Size4 fonts. `makeStackedDelim` makes a delimiter out of
10
+ * smaller pieces that are stacked on top of one another.
11
+ *
12
+ * The functions take a parameter `center`, which determines if the delimiter
13
+ * should be centered around the axis.
14
+ *
15
+ * Then, there are three exposed functions. `sizedDelim` makes a delimiter in
16
+ * one of the given sizes. This is used for things like `\bigl`.
17
+ * `customSizedDelim` makes a delimiter with a given total height+depth. It is
18
+ * called in places like `\sqrt`. `leftRightDelim` makes an appropriate
19
+ * delimiter which surrounds an expression of a given height an depth. It is
20
+ * used in `\left` and `\right`.
21
+ */
22
+
23
+ var ParseError = require("./ParseError");
24
+ var Style = require("./Style");
25
+
26
+ var buildCommon = require("./buildCommon");
27
+ var fontMetrics = require("./fontMetrics");
28
+ var symbols = require("./symbols");
29
+ var utils = require("./utils");
30
+
31
+ var makeSpan = buildCommon.makeSpan;
32
+
33
+ /**
34
+ * Get the metrics for a given symbol and font, after transformation (i.e.
35
+ * after following replacement from symbols.js)
36
+ */
37
+ var getMetrics = function(symbol, font) {
38
+ if (symbols.math[symbol] && symbols.math[symbol].replace) {
39
+ return fontMetrics.getCharacterMetrics(
40
+ symbols.math[symbol].replace, font);
41
+ } else {
42
+ return fontMetrics.getCharacterMetrics(
43
+ symbol, font);
44
+ }
45
+ };
46
+
47
+ /**
48
+ * Builds a symbol in the given font size (note size is an integer)
49
+ */
50
+ var mathrmSize = function(value, size, mode) {
51
+ return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode);
52
+ };
53
+
54
+ /**
55
+ * Puts a delimiter span in a given style, and adds appropriate height, depth,
56
+ * and maxFontSizes.
57
+ */
58
+ var styleWrap = function(delim, toStyle, options) {
59
+ var span = makeSpan(
60
+ ["style-wrap", options.style.reset(), toStyle.cls()], [delim]);
61
+
62
+ var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
63
+
64
+ span.height *= multiplier;
65
+ span.depth *= multiplier;
66
+ span.maxFontSize = toStyle.sizeMultiplier;
67
+
68
+ return span;
69
+ };
70
+
71
+ /**
72
+ * Makes a small delimiter. This is a delimiter that comes in the Main-Regular
73
+ * font, but is restyled to either be in textstyle, scriptstyle, or
74
+ * scriptscriptstyle.
75
+ */
76
+ var makeSmallDelim = function(delim, style, center, options, mode) {
77
+ var text = buildCommon.makeSymbol(delim, "Main-Regular", mode);
78
+
79
+ var span = styleWrap(text, style, options);
80
+
81
+ if (center) {
82
+ var shift =
83
+ (1 - options.style.sizeMultiplier / style.sizeMultiplier) *
84
+ fontMetrics.metrics.axisHeight;
85
+
86
+ span.style.top = shift + "em";
87
+ span.height -= shift;
88
+ span.depth += shift;
89
+ }
90
+
91
+ return span;
92
+ };
93
+
94
+ /**
95
+ * Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
96
+ * Size3, or Size4 fonts. It is always rendered in textstyle.
97
+ */
98
+ var makeLargeDelim = function(delim, size, center, options, mode) {
99
+ var inner = mathrmSize(delim, size, mode);
100
+
101
+ var span = styleWrap(
102
+ makeSpan(["delimsizing", "size" + size],
103
+ [inner], options.getColor()),
104
+ Style.TEXT, options);
105
+
106
+ if (center) {
107
+ var shift = (1 - options.style.sizeMultiplier) *
108
+ fontMetrics.metrics.axisHeight;
109
+
110
+ span.style.top = shift + "em";
111
+ span.height -= shift;
112
+ span.depth += shift;
113
+ }
114
+
115
+ return span;
116
+ };
117
+
118
+ /**
119
+ * Make an inner span with the given offset and in the given font. This is used
120
+ * in `makeStackedDelim` to make the stacking pieces for the delimiter.
121
+ */
122
+ var makeInner = function(symbol, font, mode) {
123
+ var sizeClass;
124
+ // Apply the correct CSS class to choose the right font.
125
+ if (font === "Size1-Regular") {
126
+ sizeClass = "delim-size1";
127
+ } else if (font === "Size4-Regular") {
128
+ sizeClass = "delim-size4";
129
+ }
130
+
131
+ var inner = makeSpan(
132
+ ["delimsizinginner", sizeClass],
133
+ [makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]);
134
+
135
+ // Since this will be passed into `makeVList` in the end, wrap the element
136
+ // in the appropriate tag that VList uses.
137
+ return {type: "elem", elem: inner};
138
+ };
139
+
140
+ /**
141
+ * Make a stacked delimiter out of a given delimiter, with the total height at
142
+ * least `heightTotal`. This routine is mentioned on page 442 of the TeXbook.
143
+ */
144
+ var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
145
+ // There are four parts, the top, an optional middle, a repeated part, and a
146
+ // bottom.
147
+ var top;
148
+ var middle;
149
+ var repeat;
150
+ var bottom;
151
+ top = repeat = bottom = delim;
152
+ middle = null;
153
+ // Also keep track of what font the delimiters are in
154
+ var font = "Size1-Regular";
155
+
156
+ // We set the parts and font based on the symbol. Note that we use
157
+ // '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the
158
+ // repeats of the arrows
159
+ if (delim === "\\uparrow") {
160
+ repeat = bottom = "\u23d0";
161
+ } else if (delim === "\\Uparrow") {
162
+ repeat = bottom = "\u2016";
163
+ } else if (delim === "\\downarrow") {
164
+ top = repeat = "\u23d0";
165
+ } else if (delim === "\\Downarrow") {
166
+ top = repeat = "\u2016";
167
+ } else if (delim === "\\updownarrow") {
168
+ top = "\\uparrow";
169
+ repeat = "\u23d0";
170
+ bottom = "\\downarrow";
171
+ } else if (delim === "\\Updownarrow") {
172
+ top = "\\Uparrow";
173
+ repeat = "\u2016";
174
+ bottom = "\\Downarrow";
175
+ } else if (delim === "[" || delim === "\\lbrack") {
176
+ top = "\u23a1";
177
+ repeat = "\u23a2";
178
+ bottom = "\u23a3";
179
+ font = "Size4-Regular";
180
+ } else if (delim === "]" || delim === "\\rbrack") {
181
+ top = "\u23a4";
182
+ repeat = "\u23a5";
183
+ bottom = "\u23a6";
184
+ font = "Size4-Regular";
185
+ } else if (delim === "\\lfloor") {
186
+ repeat = top = "\u23a2";
187
+ bottom = "\u23a3";
188
+ font = "Size4-Regular";
189
+ } else if (delim === "\\lceil") {
190
+ top = "\u23a1";
191
+ repeat = bottom = "\u23a2";
192
+ font = "Size4-Regular";
193
+ } else if (delim === "\\rfloor") {
194
+ repeat = top = "\u23a5";
195
+ bottom = "\u23a6";
196
+ font = "Size4-Regular";
197
+ } else if (delim === "\\rceil") {
198
+ top = "\u23a4";
199
+ repeat = bottom = "\u23a5";
200
+ font = "Size4-Regular";
201
+ } else if (delim === "(") {
202
+ top = "\u239b";
203
+ repeat = "\u239c";
204
+ bottom = "\u239d";
205
+ font = "Size4-Regular";
206
+ } else if (delim === ")") {
207
+ top = "\u239e";
208
+ repeat = "\u239f";
209
+ bottom = "\u23a0";
210
+ font = "Size4-Regular";
211
+ } else if (delim === "\\{" || delim === "\\lbrace") {
212
+ top = "\u23a7";
213
+ middle = "\u23a8";
214
+ bottom = "\u23a9";
215
+ repeat = "\u23aa";
216
+ font = "Size4-Regular";
217
+ } else if (delim === "\\}" || delim === "\\rbrace") {
218
+ top = "\u23ab";
219
+ middle = "\u23ac";
220
+ bottom = "\u23ad";
221
+ repeat = "\u23aa";
222
+ font = "Size4-Regular";
223
+ } else if (delim === "\\lgroup") {
224
+ top = "\u23a7";
225
+ bottom = "\u23a9";
226
+ repeat = "\u23aa";
227
+ font = "Size4-Regular";
228
+ } else if (delim === "\\rgroup") {
229
+ top = "\u23ab";
230
+ bottom = "\u23ad";
231
+ repeat = "\u23aa";
232
+ font = "Size4-Regular";
233
+ } else if (delim === "\\lmoustache") {
234
+ top = "\u23a7";
235
+ bottom = "\u23ad";
236
+ repeat = "\u23aa";
237
+ font = "Size4-Regular";
238
+ } else if (delim === "\\rmoustache") {
239
+ top = "\u23ab";
240
+ bottom = "\u23a9";
241
+ repeat = "\u23aa";
242
+ font = "Size4-Regular";
243
+ } else if (delim === "\\surd") {
244
+ top = "\ue001";
245
+ bottom = "\u23b7";
246
+ repeat = "\ue000";
247
+ font = "Size4-Regular";
248
+ }
249
+
250
+ // Get the metrics of the four sections
251
+ var topMetrics = getMetrics(top, font);
252
+ var topHeightTotal = topMetrics.height + topMetrics.depth;
253
+ var repeatMetrics = getMetrics(repeat, font);
254
+ var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth;
255
+ var bottomMetrics = getMetrics(bottom, font);
256
+ var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth;
257
+ var middleHeightTotal = 0;
258
+ var middleFactor = 1;
259
+ if (middle !== null) {
260
+ var middleMetrics = getMetrics(middle, font);
261
+ middleHeightTotal = middleMetrics.height + middleMetrics.depth;
262
+ middleFactor = 2; // repeat symmetrically above and below middle
263
+ }
264
+
265
+ // Calcuate the minimal height that the delimiter can have.
266
+ // It is at least the size of the top, bottom, and optional middle combined.
267
+ var minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal;
268
+
269
+ // Compute the number of copies of the repeat symbol we will need
270
+ var repeatCount = Math.ceil(
271
+ (heightTotal - minHeight) / (middleFactor * repeatHeightTotal));
272
+
273
+ // Compute the total height of the delimiter including all the symbols
274
+ var realHeightTotal =
275
+ minHeight + repeatCount * middleFactor * repeatHeightTotal;
276
+
277
+ // The center of the delimiter is placed at the center of the axis. Note
278
+ // that in this context, "center" means that the delimiter should be
279
+ // centered around the axis in the current style, while normally it is
280
+ // centered around the axis in textstyle.
281
+ var axisHeight = fontMetrics.metrics.axisHeight;
282
+ if (center) {
283
+ axisHeight *= options.style.sizeMultiplier;
284
+ }
285
+ // Calculate the depth
286
+ var depth = realHeightTotal / 2 - axisHeight;
287
+
288
+ // Now, we start building the pieces that will go into the vlist
289
+
290
+ // Keep a list of the inner pieces
291
+ var inners = [];
292
+
293
+ // Add the bottom symbol
294
+ inners.push(makeInner(bottom, font, mode));
295
+
296
+ var i;
297
+ if (middle === null) {
298
+ // Add that many symbols
299
+ for (i = 0; i < repeatCount; i++) {
300
+ inners.push(makeInner(repeat, font, mode));
301
+ }
302
+ } else {
303
+ // When there is a middle bit, we need the middle part and two repeated
304
+ // sections
305
+ for (i = 0; i < repeatCount; i++) {
306
+ inners.push(makeInner(repeat, font, mode));
307
+ }
308
+ inners.push(makeInner(middle, font, mode));
309
+ for (i = 0; i < repeatCount; i++) {
310
+ inners.push(makeInner(repeat, font, mode));
311
+ }
312
+ }
313
+
314
+ // Add the top symbol
315
+ inners.push(makeInner(top, font, mode));
316
+
317
+ // Finally, build the vlist
318
+ var inner = buildCommon.makeVList(inners, "bottom", depth, options);
319
+
320
+ return styleWrap(
321
+ makeSpan(["delimsizing", "mult"], [inner], options.getColor()),
322
+ Style.TEXT, options);
323
+ };
324
+
325
+ // There are three kinds of delimiters, delimiters that stack when they become
326
+ // too large
327
+ var stackLargeDelimiters = [
328
+ "(", ")", "[", "\\lbrack", "]", "\\rbrack",
329
+ "\\{", "\\lbrace", "\\}", "\\rbrace",
330
+ "\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
331
+ "\\surd",
332
+ ];
333
+
334
+ // delimiters that always stack
335
+ var stackAlwaysDelimiters = [
336
+ "\\uparrow", "\\downarrow", "\\updownarrow",
337
+ "\\Uparrow", "\\Downarrow", "\\Updownarrow",
338
+ "|", "\\|", "\\vert", "\\Vert",
339
+ "\\lvert", "\\rvert", "\\lVert", "\\rVert",
340
+ "\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache",
341
+ ];
342
+
343
+ // and delimiters that never stack
344
+ var stackNeverDelimiters = [
345
+ "<", ">", "\\langle", "\\rangle", "/", "\\backslash", "\\lt", "\\gt",
346
+ ];
347
+
348
+ // Metrics of the different sizes. Found by looking at TeX's output of
349
+ // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
350
+ // Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
351
+ var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
352
+
353
+ /**
354
+ * Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4.
355
+ */
356
+ var makeSizedDelim = function(delim, size, options, mode) {
357
+ // < and > turn into \langle and \rangle in delimiters
358
+ if (delim === "<" || delim === "\\lt") {
359
+ delim = "\\langle";
360
+ } else if (delim === ">" || delim === "\\gt") {
361
+ delim = "\\rangle";
362
+ }
363
+
364
+ // Sized delimiters are never centered.
365
+ if (utils.contains(stackLargeDelimiters, delim) ||
366
+ utils.contains(stackNeverDelimiters, delim)) {
367
+ return makeLargeDelim(delim, size, false, options, mode);
368
+ } else if (utils.contains(stackAlwaysDelimiters, delim)) {
369
+ return makeStackedDelim(
370
+ delim, sizeToMaxHeight[size], false, options, mode);
371
+ } else {
372
+ throw new ParseError("Illegal delimiter: '" + delim + "'");
373
+ }
374
+ };
375
+
376
+ /**
377
+ * There are three different sequences of delimiter sizes that the delimiters
378
+ * follow depending on the kind of delimiter. This is used when creating custom
379
+ * sized delimiters to decide whether to create a small, large, or stacked
380
+ * delimiter.
381
+ *
382
+ * In real TeX, these sequences aren't explicitly defined, but are instead
383
+ * defined inside the font metrics. Since there are only three sequences that
384
+ * are possible for the delimiters that TeX defines, it is easier to just encode
385
+ * them explicitly here.
386
+ */
387
+
388
+ // Delimiters that never stack try small delimiters and large delimiters only
389
+ var stackNeverDelimiterSequence = [
390
+ {type: "small", style: Style.SCRIPTSCRIPT},
391
+ {type: "small", style: Style.SCRIPT},
392
+ {type: "small", style: Style.TEXT},
393
+ {type: "large", size: 1},
394
+ {type: "large", size: 2},
395
+ {type: "large", size: 3},
396
+ {type: "large", size: 4},
397
+ ];
398
+
399
+ // Delimiters that always stack try the small delimiters first, then stack
400
+ var stackAlwaysDelimiterSequence = [
401
+ {type: "small", style: Style.SCRIPTSCRIPT},
402
+ {type: "small", style: Style.SCRIPT},
403
+ {type: "small", style: Style.TEXT},
404
+ {type: "stack"},
405
+ ];
406
+
407
+ // Delimiters that stack when large try the small and then large delimiters, and
408
+ // stack afterwards
409
+ var stackLargeDelimiterSequence = [
410
+ {type: "small", style: Style.SCRIPTSCRIPT},
411
+ {type: "small", style: Style.SCRIPT},
412
+ {type: "small", style: Style.TEXT},
413
+ {type: "large", size: 1},
414
+ {type: "large", size: 2},
415
+ {type: "large", size: 3},
416
+ {type: "large", size: 4},
417
+ {type: "stack"},
418
+ ];
419
+
420
+ /**
421
+ * Get the font used in a delimiter based on what kind of delimiter it is.
422
+ */
423
+ var delimTypeToFont = function(type) {
424
+ if (type.type === "small") {
425
+ return "Main-Regular";
426
+ } else if (type.type === "large") {
427
+ return "Size" + type.size + "-Regular";
428
+ } else if (type.type === "stack") {
429
+ return "Size4-Regular";
430
+ }
431
+ };
432
+
433
+ /**
434
+ * Traverse a sequence of types of delimiters to decide what kind of delimiter
435
+ * should be used to create a delimiter of the given height+depth.
436
+ */
437
+ var traverseSequence = function(delim, height, sequence, options) {
438
+ // Here, we choose the index we should start at in the sequences. In smaller
439
+ // sizes (which correspond to larger numbers in style.size) we start earlier
440
+ // in the sequence. Thus, scriptscript starts at index 3-3=0, script starts
441
+ // at index 3-2=1, text starts at 3-1=2, and display starts at min(2,3-0)=2
442
+ var start = Math.min(2, 3 - options.style.size);
443
+ for (var i = start; i < sequence.length; i++) {
444
+ if (sequence[i].type === "stack") {
445
+ // This is always the last delimiter, so we just break the loop now.
446
+ break;
447
+ }
448
+
449
+ var metrics = getMetrics(delim, delimTypeToFont(sequence[i]));
450
+ var heightDepth = metrics.height + metrics.depth;
451
+
452
+ // Small delimiters are scaled down versions of the same font, so we
453
+ // account for the style change size.
454
+
455
+ if (sequence[i].type === "small") {
456
+ heightDepth *= sequence[i].style.sizeMultiplier;
457
+ }
458
+
459
+ // Check if the delimiter at this size works for the given height.
460
+ if (heightDepth > height) {
461
+ return sequence[i];
462
+ }
463
+ }
464
+
465
+ // If we reached the end of the sequence, return the last sequence element.
466
+ return sequence[sequence.length - 1];
467
+ };
468
+
469
+ /**
470
+ * Make a delimiter of a given height+depth, with optional centering. Here, we
471
+ * traverse the sequences, and create a delimiter that the sequence tells us to.
472
+ */
473
+ var makeCustomSizedDelim = function(delim, height, center, options, mode) {
474
+ if (delim === "<" || delim === "\\lt") {
475
+ delim = "\\langle";
476
+ } else if (delim === ">" || delim === "\\gt") {
477
+ delim = "\\rangle";
478
+ }
479
+
480
+ // Decide what sequence to use
481
+ var sequence;
482
+ if (utils.contains(stackNeverDelimiters, delim)) {
483
+ sequence = stackNeverDelimiterSequence;
484
+ } else if (utils.contains(stackLargeDelimiters, delim)) {
485
+ sequence = stackLargeDelimiterSequence;
486
+ } else {
487
+ sequence = stackAlwaysDelimiterSequence;
488
+ }
489
+
490
+ // Look through the sequence
491
+ var delimType = traverseSequence(delim, height, sequence, options);
492
+
493
+ // Depending on the sequence element we decided on, call the appropriate
494
+ // function.
495
+ if (delimType.type === "small") {
496
+ return makeSmallDelim(delim, delimType.style, center, options, mode);
497
+ } else if (delimType.type === "large") {
498
+ return makeLargeDelim(delim, delimType.size, center, options, mode);
499
+ } else if (delimType.type === "stack") {
500
+ return makeStackedDelim(delim, height, center, options, mode);
501
+ }
502
+ };
503
+
504
+ /**
505
+ * Make a delimiter for use with `\left` and `\right`, given a height and depth
506
+ * of an expression that the delimiters surround.
507
+ */
508
+ var makeLeftRightDelim = function(delim, height, depth, options, mode) {
509
+ // We always center \left/\right delimiters, so the axis is always shifted
510
+ var axisHeight =
511
+ fontMetrics.metrics.axisHeight * options.style.sizeMultiplier;
512
+
513
+ // Taken from TeX source, tex.web, function make_left_right
514
+ var delimiterFactor = 901;
515
+ var delimiterExtend = 5.0 / fontMetrics.metrics.ptPerEm;
516
+
517
+ var maxDistFromAxis = Math.max(
518
+ height - axisHeight, depth + axisHeight);
519
+
520
+ var totalHeight = Math.max(
521
+ // In real TeX, calculations are done using integral values which are
522
+ // 65536 per pt, or 655360 per em. So, the division here truncates in
523
+ // TeX but doesn't here, producing different results. If we wanted to
524
+ // exactly match TeX's calculation, we could do
525
+ // Math.floor(655360 * maxDistFromAxis / 500) *
526
+ // delimiterFactor / 655360
527
+ // (To see the difference, compare
528
+ // x^{x^{\left(\rule{0.1em}{0.68em}\right)}}
529
+ // in TeX and KaTeX)
530
+ maxDistFromAxis / 500 * delimiterFactor,
531
+ 2 * maxDistFromAxis - delimiterExtend);
532
+
533
+ // Finally, we defer to `makeCustomSizedDelim` with our calculated total
534
+ // height
535
+ return makeCustomSizedDelim(delim, totalHeight, true, options, mode);
536
+ };
537
+
538
+ module.exports = {
539
+ sizedDelim: makeSizedDelim,
540
+ customSizedDelim: makeCustomSizedDelim,
541
+ leftRightDelim: makeLeftRightDelim,
542
+ };
modules/tokenize_latex/third_party/katex/src/domTree.js ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * These objects store the data about the DOM nodes we create, as well as some
3
+ * extra data. They can then be transformed into real DOM nodes with the
4
+ * `toNode` function or HTML markup using `toMarkup`. They are useful for both
5
+ * storing extra properties on the nodes, as well as providing a way to easily
6
+ * work with the DOM.
7
+ *
8
+ * Similar functions for working with MathML nodes exist in mathMLTree.js.
9
+ */
10
+
11
+ var utils = require("./utils");
12
+
13
+ /**
14
+ * Create an HTML className based on a list of classes. In addition to joining
15
+ * with spaces, we also remove null or empty classes.
16
+ */
17
+ var createClass = function(classes) {
18
+ classes = classes.slice();
19
+ for (var i = classes.length - 1; i >= 0; i--) {
20
+ if (!classes[i]) {
21
+ classes.splice(i, 1);
22
+ }
23
+ }
24
+
25
+ return classes.join(" ");
26
+ };
27
+
28
+ /**
29
+ * This node represents a span node, with a className, a list of children, and
30
+ * an inline style. It also contains information about its height, depth, and
31
+ * maxFontSize.
32
+ */
33
+ function span(classes, children, height, depth, maxFontSize, style) {
34
+ this.classes = classes || [];
35
+ this.children = children || [];
36
+ this.height = height || 0;
37
+ this.depth = depth || 0;
38
+ this.maxFontSize = maxFontSize || 0;
39
+ this.style = style || {};
40
+ this.attributes = {};
41
+ }
42
+
43
+ /**
44
+ * Sets an arbitrary attribute on the span. Warning: use this wisely. Not all
45
+ * browsers support attributes the same, and having too many custom attributes
46
+ * is probably bad.
47
+ */
48
+ span.prototype.setAttribute = function(attribute, value) {
49
+ this.attributes[attribute] = value;
50
+ };
51
+
52
+ /**
53
+ * Convert the span into an HTML node
54
+ */
55
+ span.prototype.toNode = function() {
56
+ var span = document.createElement("span");
57
+
58
+ // Apply the class
59
+ span.className = createClass(this.classes);
60
+
61
+ // Apply inline styles
62
+ for (var style in this.style) {
63
+ if (Object.prototype.hasOwnProperty.call(this.style, style)) {
64
+ span.style[style] = this.style[style];
65
+ }
66
+ }
67
+
68
+ // Apply attributes
69
+ for (var attr in this.attributes) {
70
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
71
+ span.setAttribute(attr, this.attributes[attr]);
72
+ }
73
+ }
74
+
75
+ // Append the children, also as HTML nodes
76
+ for (var i = 0; i < this.children.length; i++) {
77
+ span.appendChild(this.children[i].toNode());
78
+ }
79
+
80
+ return span;
81
+ };
82
+
83
+ /**
84
+ * Convert the span into an HTML markup string
85
+ */
86
+ span.prototype.toMarkup = function() {
87
+ var markup = "<span";
88
+
89
+ // Add the class
90
+ if (this.classes.length) {
91
+ markup += " class=\"";
92
+ markup += utils.escape(createClass(this.classes));
93
+ markup += "\"";
94
+ }
95
+
96
+ var styles = "";
97
+
98
+ // Add the styles, after hyphenation
99
+ for (var style in this.style) {
100
+ if (this.style.hasOwnProperty(style)) {
101
+ styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
102
+ }
103
+ }
104
+
105
+ if (styles) {
106
+ markup += " style=\"" + utils.escape(styles) + "\"";
107
+ }
108
+
109
+ // Add the attributes
110
+ for (var attr in this.attributes) {
111
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
112
+ markup += " " + attr + "=\"";
113
+ markup += utils.escape(this.attributes[attr]);
114
+ markup += "\"";
115
+ }
116
+ }
117
+
118
+ markup += ">";
119
+
120
+ // Add the markup of the children, also as markup
121
+ for (var i = 0; i < this.children.length; i++) {
122
+ markup += this.children[i].toMarkup();
123
+ }
124
+
125
+ markup += "</span>";
126
+
127
+ return markup;
128
+ };
129
+
130
+ /**
131
+ * This node represents a document fragment, which contains elements, but when
132
+ * placed into the DOM doesn't have any representation itself. Thus, it only
133
+ * contains children and doesn't have any HTML properties. It also keeps track
134
+ * of a height, depth, and maxFontSize.
135
+ */
136
+ function documentFragment(children, height, depth, maxFontSize) {
137
+ this.children = children || [];
138
+ this.height = height || 0;
139
+ this.depth = depth || 0;
140
+ this.maxFontSize = maxFontSize || 0;
141
+ }
142
+
143
+ /**
144
+ * Convert the fragment into a node
145
+ */
146
+ documentFragment.prototype.toNode = function() {
147
+ // Create a fragment
148
+ var frag = document.createDocumentFragment();
149
+
150
+ // Append the children
151
+ for (var i = 0; i < this.children.length; i++) {
152
+ frag.appendChild(this.children[i].toNode());
153
+ }
154
+
155
+ return frag;
156
+ };
157
+
158
+ /**
159
+ * Convert the fragment into HTML markup
160
+ */
161
+ documentFragment.prototype.toMarkup = function() {
162
+ var markup = "";
163
+
164
+ // Simply concatenate the markup for the children together
165
+ for (var i = 0; i < this.children.length; i++) {
166
+ markup += this.children[i].toMarkup();
167
+ }
168
+
169
+ return markup;
170
+ };
171
+
172
+ /**
173
+ * A symbol node contains information about a single symbol. It either renders
174
+ * to a single text node, or a span with a single text node in it, depending on
175
+ * whether it has CSS classes, styles, or needs italic correction.
176
+ */
177
+ function symbolNode(value, height, depth, italic, skew, classes, style) {
178
+ this.value = value || "";
179
+ this.height = height || 0;
180
+ this.depth = depth || 0;
181
+ this.italic = italic || 0;
182
+ this.skew = skew || 0;
183
+ this.classes = classes || [];
184
+ this.style = style || {};
185
+ this.maxFontSize = 0;
186
+ }
187
+
188
+ /**
189
+ * Creates a text node or span from a symbol node. Note that a span is only
190
+ * created if it is needed.
191
+ */
192
+ symbolNode.prototype.toNode = function() {
193
+ var node = document.createTextNode(this.value);
194
+ var span = null;
195
+
196
+ if (this.italic > 0) {
197
+ span = document.createElement("span");
198
+ span.style.marginRight = this.italic + "em";
199
+ }
200
+
201
+ if (this.classes.length > 0) {
202
+ span = span || document.createElement("span");
203
+ span.className = createClass(this.classes);
204
+ }
205
+
206
+ for (var style in this.style) {
207
+ if (this.style.hasOwnProperty(style)) {
208
+ span = span || document.createElement("span");
209
+ span.style[style] = this.style[style];
210
+ }
211
+ }
212
+
213
+ if (span) {
214
+ span.appendChild(node);
215
+ return span;
216
+ } else {
217
+ return node;
218
+ }
219
+ };
220
+
221
+ /**
222
+ * Creates markup for a symbol node.
223
+ */
224
+ symbolNode.prototype.toMarkup = function() {
225
+ // TODO(alpert): More duplication than I'd like from
226
+ // span.prototype.toMarkup and symbolNode.prototype.toNode...
227
+ var needsSpan = false;
228
+
229
+ var markup = "<span";
230
+
231
+ if (this.classes.length) {
232
+ needsSpan = true;
233
+ markup += " class=\"";
234
+ markup += utils.escape(createClass(this.classes));
235
+ markup += "\"";
236
+ }
237
+
238
+ var styles = "";
239
+
240
+ if (this.italic > 0) {
241
+ styles += "margin-right:" + this.italic + "em;";
242
+ }
243
+ for (var style in this.style) {
244
+ if (this.style.hasOwnProperty(style)) {
245
+ styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
246
+ }
247
+ }
248
+
249
+ if (styles) {
250
+ needsSpan = true;
251
+ markup += " style=\"" + utils.escape(styles) + "\"";
252
+ }
253
+
254
+ var escaped = utils.escape(this.value);
255
+ if (needsSpan) {
256
+ markup += ">";
257
+ markup += escaped;
258
+ markup += "</span>";
259
+ return markup;
260
+ } else {
261
+ return escaped;
262
+ }
263
+ };
264
+
265
+ module.exports = {
266
+ span: span,
267
+ documentFragment: documentFragment,
268
+ symbolNode: symbolNode,
269
+ };
modules/tokenize_latex/third_party/katex/src/environments.js ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint no-constant-condition:0 */
2
+ var fontMetrics = require("./fontMetrics");
3
+ var parseData = require("./parseData");
4
+ var ParseError = require("./ParseError");
5
+
6
+ var ParseNode = parseData.ParseNode;
7
+
8
+ /**
9
+ * Parse the body of the environment, with rows delimited by \\ and
10
+ * columns delimited by &, and create a nested list in row-major order
11
+ * with one group per cell.
12
+ */
13
+ var q = 0 ;
14
+ function parseArray(parser, result) {
15
+ var row = [];
16
+ var body = [row];
17
+ var rowGaps = [];
18
+
19
+ while (true) {
20
+
21
+ // if (q == 1) console.error(parser.nextToken.text);
22
+ try {
23
+ var cell = parser.parseExpression(false, null);
24
+ } catch (e) {
25
+ // console.error(e);
26
+ exit();
27
+ }
28
+ // if (q == 1) exit();
29
+ row.push(new ParseNode("ordgroup", cell, parser.mode));
30
+ var next = parser.nextToken.text;
31
+ if (next === "&") {
32
+ parser.consume();
33
+ } else if (next === "\\end" || next == "}") {
34
+ break;
35
+ } else if (next === "\\\\" || next === "\\cr") {
36
+
37
+ var cr = parser.parseFunction();
38
+
39
+ rowGaps.push(cr.value.size);
40
+ row = [];
41
+ body.push(row);
42
+ } else {
43
+ // TODO: Clean up the following hack once #385 got merged
44
+ var pos = Math.min(parser.pos + 1, parser.lexer._input.length);
45
+ throw new ParseError("Expected & or \\\\ or \\end",
46
+ parser.lexer, pos);
47
+ }
48
+ }
49
+ result.body = body;
50
+ result.rowGaps = rowGaps;
51
+ // if (q == 1) exit();
52
+ var node = new ParseNode(result.type, result, parser.mode);
53
+ return node;
54
+ }
55
+
56
+ /*
57
+ * An environment definition is very similar to a function definition:
58
+ * it is declared with a name or a list of names, a set of properties
59
+ * and a handler containing the actual implementation.
60
+ *
61
+ * The properties include:
62
+ * - numArgs: The number of arguments after the \begin{name} function.
63
+ * - argTypes: (optional) Just like for a function
64
+ * - allowedInText: (optional) Whether or not the environment is allowed inside
65
+ * text mode (default false) (not enforced yet)
66
+ * - numOptionalArgs: (optional) Just like for a function
67
+ * A bare number instead of that object indicates the numArgs value.
68
+ *
69
+ * The handler function will receive two arguments
70
+ * - context: information and references provided by the parser
71
+ * - args: an array of arguments passed to \begin{name}
72
+ * The context contains the following properties:
73
+ * - envName: the name of the environment, one of the listed names.
74
+ * - parser: the parser object
75
+ * - lexer: the lexer object
76
+ * - positions: the positions associated with these arguments from args.
77
+ * The handler must return a ParseResult.
78
+ */
79
+
80
+ function defineEnvironment(names, props, handler) {
81
+ if (typeof names === "string") {
82
+ names = [names];
83
+ }
84
+ if (typeof props === "number") {
85
+ props = { numArgs: props };
86
+ }
87
+ // Set default values of environments
88
+ var data = {
89
+ numArgs: props.numArgs || 0,
90
+ argTypes: props.argTypes,
91
+ greediness: 1,
92
+ allowedInText: !!props.allowedInText,
93
+ numOptionalArgs: props.numOptionalArgs || 0,
94
+ handler: handler,
95
+ };
96
+ for (var i = 0; i < names.length; ++i) {
97
+ module.exports[names[i]] = data;
98
+ }
99
+ }
100
+
101
+ // Arrays are part of LaTeX, defined in lttab.dtx so its documentation
102
+ // is part of the source2e.pdf file of LaTeX2e source documentation.
103
+ defineEnvironment("array", {
104
+ numArgs: 1,
105
+ }, function(context, args) {
106
+ var colalign = args[0];
107
+ colalign = colalign.value.map ? colalign.value : [colalign];
108
+ var cols = colalign.map(function(node) {
109
+ var ca = node.value;
110
+ if ("lcr".indexOf(ca) !== -1) {
111
+ return {
112
+ type: "align",
113
+ align: ca,
114
+ };
115
+ } else if (ca === "|") {
116
+ return {
117
+ type: "separator",
118
+ separator: "|",
119
+ };
120
+ }
121
+ // throw new ParseError(
122
+ // "Unknown column alignment: " + node.value,
123
+ // context.lexer, context.positions[1]);
124
+ });
125
+ var res = {
126
+ type: "array",
127
+ cols: cols,
128
+ hskipBeforeAndAfter: true, // \@preamble in lttab.dtx
129
+ };
130
+ res = parseArray(context.parser, res);
131
+ return res;
132
+ });
133
+
134
+ defineEnvironment("tabular", {
135
+ numArgs: 1,
136
+ }, function(context, args) {
137
+ var colalign = args[0];
138
+ colalign = colalign.value.map ? colalign.value : [colalign];
139
+ var cols = colalign.map(function(node) {
140
+ var ca = node.value;
141
+ if ("lcr".indexOf(ca) !== -1) {
142
+ return {
143
+ type: "align",
144
+ align: ca,
145
+ };
146
+ } else if (ca === "|") {
147
+ return {
148
+ type: "separator",
149
+ separator: "|",
150
+ };
151
+ }
152
+ // throw new ParseError(
153
+ // "Unknown column alignment: " + node.value,
154
+ // context.lexer, context.positions[1]);
155
+ });
156
+ var res = {
157
+ type: "array",
158
+ style: "tabular",
159
+ cols: cols,
160
+ hskipBeforeAndAfter: true, // \@preamble in lttab.dtx
161
+ };
162
+ res = parseArray(context.parser, res);
163
+ return res;
164
+ });
165
+
166
+ // The matrix environments of amsmath builds on the array environment
167
+ // of LaTeX, which is discussed above.
168
+ defineEnvironment([
169
+ "matrix",
170
+ "pmatrix",
171
+ "bmatrix",
172
+ "Bmatrix",
173
+ "vmatrix",
174
+ "Vmatrix",
175
+ ], {
176
+ }, function(context) {
177
+ var delimiters = {
178
+ "matrix": null,
179
+ "pmatrix": ["(", ")"],
180
+ "bmatrix": ["[", "]"],
181
+ "Bmatrix": ["\\{", "\\}"],
182
+ "vmatrix": ["|", "|"],
183
+ "Vmatrix": ["\\Vert", "\\Vert"],
184
+ }[context.envName];
185
+ var res = {
186
+ type: "array",
187
+ hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath
188
+ };
189
+ q = 1;
190
+ res = parseArray(context.parser, res);
191
+
192
+ if (delimiters) {
193
+ res = new ParseNode("leftright", {
194
+ body: [res],
195
+ left: delimiters[0],
196
+ right: delimiters[1],
197
+ }, context.mode);
198
+ }
199
+ return res;
200
+ });
201
+
202
+ // A cases environment (in amsmath.sty) is almost equivalent to
203
+ // \def\arraystretch{1.2}%
204
+ // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
205
+ defineEnvironment("picture", {
206
+ }, function(context) {
207
+ var res = {
208
+ type: "array",
209
+ arraystretch: 1.2,
210
+ cols: [{
211
+ type: "align",
212
+ align: "l",
213
+ pregap: 0,
214
+ postgap: fontMetrics.metrics.quad,
215
+ }, {
216
+ type: "align",
217
+ align: "l",
218
+ pregap: 0,
219
+ postgap: 0,
220
+ }],
221
+ };
222
+ res = parseArray(context.parser, res);
223
+ res = new ParseNode("leftright", {
224
+ body: [res],
225
+ left: "\\{",
226
+ right: ".",
227
+ }, context.mode);
228
+ return res;
229
+ });
230
+
231
+ defineEnvironment("cases", {
232
+ }, function(context) {
233
+ var res = {
234
+ type: "array",
235
+ arraystretch: 1.2,
236
+ cols: [{
237
+ type: "align",
238
+ align: "l",
239
+ pregap: 0,
240
+ postgap: fontMetrics.metrics.quad,
241
+ }, {
242
+ type: "align",
243
+ align: "l",
244
+ pregap: 0,
245
+ postgap: 0,
246
+ }],
247
+ };
248
+ res = parseArray(context.parser, res);
249
+ res = new ParseNode("leftright", {
250
+ body: [res],
251
+ left: "\\{",
252
+ right: ".",
253
+ }, context.mode);
254
+ return res;
255
+ });
256
+
257
+ // An aligned environment is like the align* environment
258
+ // except it operates within math mode.
259
+ // Note that we assume \nomallineskiplimit to be zero,
260
+ // so that \strut@ is the same as \strut.
261
+ defineEnvironment("aligned", {
262
+ }, function(context) {
263
+ var res = {
264
+ type: "array",
265
+ cols: [],
266
+ };
267
+ res = parseArray(context.parser, res);
268
+ var emptyGroup = new ParseNode("ordgroup", [], context.mode);
269
+ var numCols = 0;
270
+ res.value.body.forEach(function(row) {
271
+ var i;
272
+ for (i = 1; i < row.length; i += 2) {
273
+ row[i].value.unshift(emptyGroup);
274
+ }
275
+ if (numCols < row.length) {
276
+ numCols = row.length;
277
+ }
278
+ });
279
+ for (var i = 0; i < numCols; ++i) {
280
+ var align = "r";
281
+ var pregap = 0;
282
+ if (i % 2 === 1) {
283
+ align = "l";
284
+ } else if (i > 0) {
285
+ pregap = 2; // one \qquad between columns
286
+ }
287
+ res.value.cols[i] = {
288
+ type: "align",
289
+ align: align,
290
+ pregap: pregap,
291
+ postgap: 0,
292
+ };
293
+ }
294
+ return res;
295
+ });
modules/tokenize_latex/third_party/katex/src/fontMetrics.js ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint no-unused-vars:0 */
2
+
3
+ var Style = require("./Style");
4
+
5
+ /**
6
+ * This file contains metrics regarding fonts and individual symbols. The sigma
7
+ * and xi variables, as well as the metricMap map contain data extracted from
8
+ * TeX, TeX font metrics, and the TTF files. These data are then exposed via the
9
+ * `metrics` variable and the getCharacterMetrics function.
10
+ */
11
+
12
+ // These font metrics are extracted from TeX by using
13
+ // \font\a=cmmi10
14
+ // \showthe\fontdimenX\a
15
+ // where X is the corresponding variable number. These correspond to the font
16
+ // parameters of the symbol fonts. In TeX, there are actually three sets of
17
+ // dimensions, one for each of textstyle, scriptstyle, and scriptscriptstyle,
18
+ // but we only use the textstyle ones, and scale certain dimensions accordingly.
19
+ // See the TeXbook, page 441.
20
+ var sigma1 = 0.025;
21
+ var sigma2 = 0;
22
+ var sigma3 = 0;
23
+ var sigma4 = 0;
24
+ var sigma5 = 0.431;
25
+ var sigma6 = 1;
26
+ var sigma7 = 0;
27
+ var sigma8 = 0.677;
28
+ var sigma9 = 0.394;
29
+ var sigma10 = 0.444;
30
+ var sigma11 = 0.686;
31
+ var sigma12 = 0.345;
32
+ var sigma13 = 0.413;
33
+ var sigma14 = 0.363;
34
+ var sigma15 = 0.289;
35
+ var sigma16 = 0.150;
36
+ var sigma17 = 0.247;
37
+ var sigma18 = 0.386;
38
+ var sigma19 = 0.050;
39
+ var sigma20 = 2.390;
40
+ var sigma21 = 1.01;
41
+ var sigma21Script = 0.81;
42
+ var sigma21ScriptScript = 0.71;
43
+ var sigma22 = 0.250;
44
+
45
+ // These font metrics are extracted from TeX by using
46
+ // \font\a=cmex10
47
+ // \showthe\fontdimenX\a
48
+ // where X is the corresponding variable number. These correspond to the font
49
+ // parameters of the extension fonts (family 3). See the TeXbook, page 441.
50
+ var xi1 = 0;
51
+ var xi2 = 0;
52
+ var xi3 = 0;
53
+ var xi4 = 0;
54
+ var xi5 = 0.431;
55
+ var xi6 = 1;
56
+ var xi7 = 0;
57
+ var xi8 = 0.04;
58
+ var xi9 = 0.111;
59
+ var xi10 = 0.166;
60
+ var xi11 = 0.2;
61
+ var xi12 = 0.6;
62
+ var xi13 = 0.1;
63
+
64
+ // This value determines how large a pt is, for metrics which are defined in
65
+ // terms of pts.
66
+ // This value is also used in katex.less; if you change it make sure the values
67
+ // match.
68
+ var ptPerEm = 10.0;
69
+
70
+ // The space between adjacent `|` columns in an array definition. From
71
+ // `\showthe\doublerulesep` in LaTeX.
72
+ var doubleRuleSep = 2.0 / ptPerEm;
73
+
74
+ /**
75
+ * This is just a mapping from common names to real metrics
76
+ */
77
+ var metrics = {
78
+ xHeight: sigma5,
79
+ quad: sigma6,
80
+ num1: sigma8,
81
+ num2: sigma9,
82
+ num3: sigma10,
83
+ denom1: sigma11,
84
+ denom2: sigma12,
85
+ sup1: sigma13,
86
+ sup2: sigma14,
87
+ sup3: sigma15,
88
+ sub1: sigma16,
89
+ sub2: sigma17,
90
+ supDrop: sigma18,
91
+ subDrop: sigma19,
92
+ axisHeight: sigma22,
93
+ defaultRuleThickness: xi8,
94
+ bigOpSpacing1: xi9,
95
+ bigOpSpacing2: xi10,
96
+ bigOpSpacing3: xi11,
97
+ bigOpSpacing4: xi12,
98
+ bigOpSpacing5: xi13,
99
+ ptPerEm: ptPerEm,
100
+ emPerEx: sigma5 / sigma6,
101
+ doubleRuleSep: doubleRuleSep,
102
+
103
+ // TODO(alpert): Missing parallel structure here. We should probably add
104
+ // style-specific metrics for all of these.
105
+ delim1: sigma20,
106
+ getDelim2: function(style) {
107
+ if (style.size === Style.TEXT.size) {
108
+ return sigma21;
109
+ } else if (style.size === Style.SCRIPT.size) {
110
+ return sigma21Script;
111
+ } else if (style.size === Style.SCRIPTSCRIPT.size) {
112
+ return sigma21ScriptScript;
113
+ }
114
+ throw new Error("Unexpected style size: " + style.size);
115
+ },
116
+ };
117
+
118
+ // This map contains a mapping from font name and character code to character
119
+ // metrics, including height, depth, italic correction, and skew (kern from the
120
+ // character to the corresponding \skewchar)
121
+ // This map is generated via `make metrics`. It should not be changed manually.
122
+ var metricMap = require("./fontMetricsData");
123
+
124
+ /**
125
+ * This function is a convenience function for looking up information in the
126
+ * metricMap table. It takes a character as a string, and a style.
127
+ *
128
+ * Note: the `width` property may be undefined if fontMetricsData.js wasn't
129
+ * built using `Make extended_metrics`.
130
+ */
131
+ var getCharacterMetrics = function(character, style) {
132
+ var metrics = metricMap[style][character.charCodeAt(0)];
133
+ if (metrics) {
134
+ return {
135
+ depth: metrics[0],
136
+ height: metrics[1],
137
+ italic: metrics[2],
138
+ skew: metrics[3],
139
+ width: metrics[4],
140
+ };
141
+ }
142
+ };
143
+
144
+ module.exports = {
145
+ metrics: metrics,
146
+ getCharacterMetrics: getCharacterMetrics,
147
+ };
modules/tokenize_latex/third_party/katex/src/fontMetricsData.js ADDED
@@ -0,0 +1,1752 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ "AMS-Regular": {
3
+ "65": [0, 0.68889, 0, 0],
4
+ "66": [0, 0.68889, 0, 0],
5
+ "67": [0, 0.68889, 0, 0],
6
+ "68": [0, 0.68889, 0, 0],
7
+ "69": [0, 0.68889, 0, 0],
8
+ "70": [0, 0.68889, 0, 0],
9
+ "71": [0, 0.68889, 0, 0],
10
+ "72": [0, 0.68889, 0, 0],
11
+ "73": [0, 0.68889, 0, 0],
12
+ "74": [0.16667, 0.68889, 0, 0],
13
+ "75": [0, 0.68889, 0, 0],
14
+ "76": [0, 0.68889, 0, 0],
15
+ "77": [0, 0.68889, 0, 0],
16
+ "78": [0, 0.68889, 0, 0],
17
+ "79": [0.16667, 0.68889, 0, 0],
18
+ "80": [0, 0.68889, 0, 0],
19
+ "81": [0.16667, 0.68889, 0, 0],
20
+ "82": [0, 0.68889, 0, 0],
21
+ "83": [0, 0.68889, 0, 0],
22
+ "84": [0, 0.68889, 0, 0],
23
+ "85": [0, 0.68889, 0, 0],
24
+ "86": [0, 0.68889, 0, 0],
25
+ "87": [0, 0.68889, 0, 0],
26
+ "88": [0, 0.68889, 0, 0],
27
+ "89": [0, 0.68889, 0, 0],
28
+ "90": [0, 0.68889, 0, 0],
29
+ "107": [0, 0.68889, 0, 0],
30
+ "165": [0, 0.675, 0.025, 0],
31
+ "174": [0.15559, 0.69224, 0, 0],
32
+ "240": [0, 0.68889, 0, 0],
33
+ "295": [0, 0.68889, 0, 0],
34
+ "710": [0, 0.825, 0, 0],
35
+ "732": [0, 0.9, 0, 0],
36
+ "770": [0, 0.825, 0, 0],
37
+ "771": [0, 0.9, 0, 0],
38
+ "989": [0.08167, 0.58167, 0, 0],
39
+ "1008": [0, 0.43056, 0.04028, 0],
40
+ "8245": [0, 0.54986, 0, 0],
41
+ "8463": [0, 0.68889, 0, 0],
42
+ "8487": [0, 0.68889, 0, 0],
43
+ "8498": [0, 0.68889, 0, 0],
44
+ "8502": [0, 0.68889, 0, 0],
45
+ "8503": [0, 0.68889, 0, 0],
46
+ "8504": [0, 0.68889, 0, 0],
47
+ "8513": [0, 0.68889, 0, 0],
48
+ "8592": [-0.03598, 0.46402, 0, 0],
49
+ "8594": [-0.03598, 0.46402, 0, 0],
50
+ "8602": [-0.13313, 0.36687, 0, 0],
51
+ "8603": [-0.13313, 0.36687, 0, 0],
52
+ "8606": [0.01354, 0.52239, 0, 0],
53
+ "8608": [0.01354, 0.52239, 0, 0],
54
+ "8610": [0.01354, 0.52239, 0, 0],
55
+ "8611": [0.01354, 0.52239, 0, 0],
56
+ "8619": [0, 0.54986, 0, 0],
57
+ "8620": [0, 0.54986, 0, 0],
58
+ "8621": [-0.13313, 0.37788, 0, 0],
59
+ "8622": [-0.13313, 0.36687, 0, 0],
60
+ "8624": [0, 0.69224, 0, 0],
61
+ "8625": [0, 0.69224, 0, 0],
62
+ "8630": [0, 0.43056, 0, 0],
63
+ "8631": [0, 0.43056, 0, 0],
64
+ "8634": [0.08198, 0.58198, 0, 0],
65
+ "8635": [0.08198, 0.58198, 0, 0],
66
+ "8638": [0.19444, 0.69224, 0, 0],
67
+ "8639": [0.19444, 0.69224, 0, 0],
68
+ "8642": [0.19444, 0.69224, 0, 0],
69
+ "8643": [0.19444, 0.69224, 0, 0],
70
+ "8644": [0.1808, 0.675, 0, 0],
71
+ "8646": [0.1808, 0.675, 0, 0],
72
+ "8647": [0.1808, 0.675, 0, 0],
73
+ "8648": [0.19444, 0.69224, 0, 0],
74
+ "8649": [0.1808, 0.675, 0, 0],
75
+ "8650": [0.19444, 0.69224, 0, 0],
76
+ "8651": [0.01354, 0.52239, 0, 0],
77
+ "8652": [0.01354, 0.52239, 0, 0],
78
+ "8653": [-0.13313, 0.36687, 0, 0],
79
+ "8654": [-0.13313, 0.36687, 0, 0],
80
+ "8655": [-0.13313, 0.36687, 0, 0],
81
+ "8666": [0.13667, 0.63667, 0, 0],
82
+ "8667": [0.13667, 0.63667, 0, 0],
83
+ "8669": [-0.13313, 0.37788, 0, 0],
84
+ "8672": [-0.064, 0.437, 0, 0],
85
+ "8674": [-0.064, 0.437, 0, 0],
86
+ "8705": [0, 0.825, 0, 0],
87
+ "8708": [0, 0.68889, 0, 0],
88
+ "8709": [0.08167, 0.58167, 0, 0],
89
+ "8717": [0, 0.43056, 0, 0],
90
+ "8722": [-0.03598, 0.46402, 0, 0],
91
+ "8724": [0.08198, 0.69224, 0, 0],
92
+ "8726": [0.08167, 0.58167, 0, 0],
93
+ "8733": [0, 0.69224, 0, 0],
94
+ "8736": [0, 0.69224, 0, 0],
95
+ "8737": [0, 0.69224, 0, 0],
96
+ "8738": [0.03517, 0.52239, 0, 0],
97
+ "8739": [0.08167, 0.58167, 0, 0],
98
+ "8740": [0.25142, 0.74111, 0, 0],
99
+ "8741": [0.08167, 0.58167, 0, 0],
100
+ "8742": [0.25142, 0.74111, 0, 0],
101
+ "8756": [0, 0.69224, 0, 0],
102
+ "8757": [0, 0.69224, 0, 0],
103
+ "8764": [-0.13313, 0.36687, 0, 0],
104
+ "8765": [-0.13313, 0.37788, 0, 0],
105
+ "8769": [-0.13313, 0.36687, 0, 0],
106
+ "8770": [-0.03625, 0.46375, 0, 0],
107
+ "8774": [0.30274, 0.79383, 0, 0],
108
+ "8776": [-0.01688, 0.48312, 0, 0],
109
+ "8778": [0.08167, 0.58167, 0, 0],
110
+ "8782": [0.06062, 0.54986, 0, 0],
111
+ "8783": [0.06062, 0.54986, 0, 0],
112
+ "8785": [0.08198, 0.58198, 0, 0],
113
+ "8786": [0.08198, 0.58198, 0, 0],
114
+ "8787": [0.08198, 0.58198, 0, 0],
115
+ "8790": [0, 0.69224, 0, 0],
116
+ "8791": [0.22958, 0.72958, 0, 0],
117
+ "8796": [0.08198, 0.91667, 0, 0],
118
+ "8806": [0.25583, 0.75583, 0, 0],
119
+ "8807": [0.25583, 0.75583, 0, 0],
120
+ "8808": [0.25142, 0.75726, 0, 0],
121
+ "8809": [0.25142, 0.75726, 0, 0],
122
+ "8812": [0.25583, 0.75583, 0, 0],
123
+ "8814": [0.20576, 0.70576, 0, 0],
124
+ "8815": [0.20576, 0.70576, 0, 0],
125
+ "8816": [0.30274, 0.79383, 0, 0],
126
+ "8817": [0.30274, 0.79383, 0, 0],
127
+ "8818": [0.22958, 0.72958, 0, 0],
128
+ "8819": [0.22958, 0.72958, 0, 0],
129
+ "8822": [0.1808, 0.675, 0, 0],
130
+ "8823": [0.1808, 0.675, 0, 0],
131
+ "8828": [0.13667, 0.63667, 0, 0],
132
+ "8829": [0.13667, 0.63667, 0, 0],
133
+ "8830": [0.22958, 0.72958, 0, 0],
134
+ "8831": [0.22958, 0.72958, 0, 0],
135
+ "8832": [0.20576, 0.70576, 0, 0],
136
+ "8833": [0.20576, 0.70576, 0, 0],
137
+ "8840": [0.30274, 0.79383, 0, 0],
138
+ "8841": [0.30274, 0.79383, 0, 0],
139
+ "8842": [0.13597, 0.63597, 0, 0],
140
+ "8843": [0.13597, 0.63597, 0, 0],
141
+ "8847": [0.03517, 0.54986, 0, 0],
142
+ "8848": [0.03517, 0.54986, 0, 0],
143
+ "8858": [0.08198, 0.58198, 0, 0],
144
+ "8859": [0.08198, 0.58198, 0, 0],
145
+ "8861": [0.08198, 0.58198, 0, 0],
146
+ "8862": [0, 0.675, 0, 0],
147
+ "8863": [0, 0.675, 0, 0],
148
+ "8864": [0, 0.675, 0, 0],
149
+ "8865": [0, 0.675, 0, 0],
150
+ "8872": [0, 0.69224, 0, 0],
151
+ "8873": [0, 0.69224, 0, 0],
152
+ "8874": [0, 0.69224, 0, 0],
153
+ "8876": [0, 0.68889, 0, 0],
154
+ "8877": [0, 0.68889, 0, 0],
155
+ "8878": [0, 0.68889, 0, 0],
156
+ "8879": [0, 0.68889, 0, 0],
157
+ "8882": [0.03517, 0.54986, 0, 0],
158
+ "8883": [0.03517, 0.54986, 0, 0],
159
+ "8884": [0.13667, 0.63667, 0, 0],
160
+ "8885": [0.13667, 0.63667, 0, 0],
161
+ "8888": [0, 0.54986, 0, 0],
162
+ "8890": [0.19444, 0.43056, 0, 0],
163
+ "8891": [0.19444, 0.69224, 0, 0],
164
+ "8892": [0.19444, 0.69224, 0, 0],
165
+ "8901": [0, 0.54986, 0, 0],
166
+ "8903": [0.08167, 0.58167, 0, 0],
167
+ "8905": [0.08167, 0.58167, 0, 0],
168
+ "8906": [0.08167, 0.58167, 0, 0],
169
+ "8907": [0, 0.69224, 0, 0],
170
+ "8908": [0, 0.69224, 0, 0],
171
+ "8909": [-0.03598, 0.46402, 0, 0],
172
+ "8910": [0, 0.54986, 0, 0],
173
+ "8911": [0, 0.54986, 0, 0],
174
+ "8912": [0.03517, 0.54986, 0, 0],
175
+ "8913": [0.03517, 0.54986, 0, 0],
176
+ "8914": [0, 0.54986, 0, 0],
177
+ "8915": [0, 0.54986, 0, 0],
178
+ "8916": [0, 0.69224, 0, 0],
179
+ "8918": [0.0391, 0.5391, 0, 0],
180
+ "8919": [0.0391, 0.5391, 0, 0],
181
+ "8920": [0.03517, 0.54986, 0, 0],
182
+ "8921": [0.03517, 0.54986, 0, 0],
183
+ "8922": [0.38569, 0.88569, 0, 0],
184
+ "8923": [0.38569, 0.88569, 0, 0],
185
+ "8926": [0.13667, 0.63667, 0, 0],
186
+ "8927": [0.13667, 0.63667, 0, 0],
187
+ "8928": [0.30274, 0.79383, 0, 0],
188
+ "8929": [0.30274, 0.79383, 0, 0],
189
+ "8934": [0.23222, 0.74111, 0, 0],
190
+ "8935": [0.23222, 0.74111, 0, 0],
191
+ "8936": [0.23222, 0.74111, 0, 0],
192
+ "8937": [0.23222, 0.74111, 0, 0],
193
+ "8938": [0.20576, 0.70576, 0, 0],
194
+ "8939": [0.20576, 0.70576, 0, 0],
195
+ "8940": [0.30274, 0.79383, 0, 0],
196
+ "8941": [0.30274, 0.79383, 0, 0],
197
+ "8994": [0.19444, 0.69224, 0, 0],
198
+ "8995": [0.19444, 0.69224, 0, 0],
199
+ "9416": [0.15559, 0.69224, 0, 0],
200
+ "9484": [0, 0.69224, 0, 0],
201
+ "9488": [0, 0.69224, 0, 0],
202
+ "9492": [0, 0.37788, 0, 0],
203
+ "9496": [0, 0.37788, 0, 0],
204
+ "9585": [0.19444, 0.68889, 0, 0],
205
+ "9586": [0.19444, 0.74111, 0, 0],
206
+ "9632": [0, 0.675, 0, 0],
207
+ "9633": [0, 0.675, 0, 0],
208
+ "9650": [0, 0.54986, 0, 0],
209
+ "9651": [0, 0.54986, 0, 0],
210
+ "9654": [0.03517, 0.54986, 0, 0],
211
+ "9660": [0, 0.54986, 0, 0],
212
+ "9661": [0, 0.54986, 0, 0],
213
+ "9664": [0.03517, 0.54986, 0, 0],
214
+ "9674": [0.11111, 0.69224, 0, 0],
215
+ "9733": [0.19444, 0.69224, 0, 0],
216
+ "10003": [0, 0.69224, 0, 0],
217
+ "10016": [0, 0.69224, 0, 0],
218
+ "10731": [0.11111, 0.69224, 0, 0],
219
+ "10846": [0.19444, 0.75583, 0, 0],
220
+ "10877": [0.13667, 0.63667, 0, 0],
221
+ "10878": [0.13667, 0.63667, 0, 0],
222
+ "10885": [0.25583, 0.75583, 0, 0],
223
+ "10886": [0.25583, 0.75583, 0, 0],
224
+ "10887": [0.13597, 0.63597, 0, 0],
225
+ "10888": [0.13597, 0.63597, 0, 0],
226
+ "10889": [0.26167, 0.75726, 0, 0],
227
+ "10890": [0.26167, 0.75726, 0, 0],
228
+ "10891": [0.48256, 0.98256, 0, 0],
229
+ "10892": [0.48256, 0.98256, 0, 0],
230
+ "10901": [0.13667, 0.63667, 0, 0],
231
+ "10902": [0.13667, 0.63667, 0, 0],
232
+ "10933": [0.25142, 0.75726, 0, 0],
233
+ "10934": [0.25142, 0.75726, 0, 0],
234
+ "10935": [0.26167, 0.75726, 0, 0],
235
+ "10936": [0.26167, 0.75726, 0, 0],
236
+ "10937": [0.26167, 0.75726, 0, 0],
237
+ "10938": [0.26167, 0.75726, 0, 0],
238
+ "10949": [0.25583, 0.75583, 0, 0],
239
+ "10950": [0.25583, 0.75583, 0, 0],
240
+ "10955": [0.28481, 0.79383, 0, 0],
241
+ "10956": [0.28481, 0.79383, 0, 0],
242
+ "57350": [0.08167, 0.58167, 0, 0],
243
+ "57351": [0.08167, 0.58167, 0, 0],
244
+ "57352": [0.08167, 0.58167, 0, 0],
245
+ "57353": [0, 0.43056, 0.04028, 0],
246
+ "57356": [0.25142, 0.75726, 0, 0],
247
+ "57357": [0.25142, 0.75726, 0, 0],
248
+ "57358": [0.41951, 0.91951, 0, 0],
249
+ "57359": [0.30274, 0.79383, 0, 0],
250
+ "57360": [0.30274, 0.79383, 0, 0],
251
+ "57361": [0.41951, 0.91951, 0, 0],
252
+ "57366": [0.25142, 0.75726, 0, 0],
253
+ "57367": [0.25142, 0.75726, 0, 0],
254
+ "57368": [0.25142, 0.75726, 0, 0],
255
+ "57369": [0.25142, 0.75726, 0, 0],
256
+ "57370": [0.13597, 0.63597, 0, 0],
257
+ "57371": [0.13597, 0.63597, 0, 0],
258
+ },
259
+ "Caligraphic-Regular": {
260
+ "48": [0, 0.43056, 0, 0],
261
+ "49": [0, 0.43056, 0, 0],
262
+ "50": [0, 0.43056, 0, 0],
263
+ "51": [0.19444, 0.43056, 0, 0],
264
+ "52": [0.19444, 0.43056, 0, 0],
265
+ "53": [0.19444, 0.43056, 0, 0],
266
+ "54": [0, 0.64444, 0, 0],
267
+ "55": [0.19444, 0.43056, 0, 0],
268
+ "56": [0, 0.64444, 0, 0],
269
+ "57": [0.19444, 0.43056, 0, 0],
270
+ "65": [0, 0.68333, 0, 0.19445],
271
+ "66": [0, 0.68333, 0.03041, 0.13889],
272
+ "67": [0, 0.68333, 0.05834, 0.13889],
273
+ "68": [0, 0.68333, 0.02778, 0.08334],
274
+ "69": [0, 0.68333, 0.08944, 0.11111],
275
+ "70": [0, 0.68333, 0.09931, 0.11111],
276
+ "71": [0.09722, 0.68333, 0.0593, 0.11111],
277
+ "72": [0, 0.68333, 0.00965, 0.11111],
278
+ "73": [0, 0.68333, 0.07382, 0],
279
+ "74": [0.09722, 0.68333, 0.18472, 0.16667],
280
+ "75": [0, 0.68333, 0.01445, 0.05556],
281
+ "76": [0, 0.68333, 0, 0.13889],
282
+ "77": [0, 0.68333, 0, 0.13889],
283
+ "78": [0, 0.68333, 0.14736, 0.08334],
284
+ "79": [0, 0.68333, 0.02778, 0.11111],
285
+ "80": [0, 0.68333, 0.08222, 0.08334],
286
+ "81": [0.09722, 0.68333, 0, 0.11111],
287
+ "82": [0, 0.68333, 0, 0.08334],
288
+ "83": [0, 0.68333, 0.075, 0.13889],
289
+ "84": [0, 0.68333, 0.25417, 0],
290
+ "85": [0, 0.68333, 0.09931, 0.08334],
291
+ "86": [0, 0.68333, 0.08222, 0],
292
+ "87": [0, 0.68333, 0.08222, 0.08334],
293
+ "88": [0, 0.68333, 0.14643, 0.13889],
294
+ "89": [0.09722, 0.68333, 0.08222, 0.08334],
295
+ "90": [0, 0.68333, 0.07944, 0.13889],
296
+ },
297
+ "Fraktur-Regular": {
298
+ "33": [0, 0.69141, 0, 0],
299
+ "34": [0, 0.69141, 0, 0],
300
+ "38": [0, 0.69141, 0, 0],
301
+ "39": [0, 0.69141, 0, 0],
302
+ "40": [0.24982, 0.74947, 0, 0],
303
+ "41": [0.24982, 0.74947, 0, 0],
304
+ "42": [0, 0.62119, 0, 0],
305
+ "43": [0.08319, 0.58283, 0, 0],
306
+ "44": [0, 0.10803, 0, 0],
307
+ "45": [0.08319, 0.58283, 0, 0],
308
+ "46": [0, 0.10803, 0, 0],
309
+ "47": [0.24982, 0.74947, 0, 0],
310
+ "48": [0, 0.47534, 0, 0],
311
+ "49": [0, 0.47534, 0, 0],
312
+ "50": [0, 0.47534, 0, 0],
313
+ "51": [0.18906, 0.47534, 0, 0],
314
+ "52": [0.18906, 0.47534, 0, 0],
315
+ "53": [0.18906, 0.47534, 0, 0],
316
+ "54": [0, 0.69141, 0, 0],
317
+ "55": [0.18906, 0.47534, 0, 0],
318
+ "56": [0, 0.69141, 0, 0],
319
+ "57": [0.18906, 0.47534, 0, 0],
320
+ "58": [0, 0.47534, 0, 0],
321
+ "59": [0.12604, 0.47534, 0, 0],
322
+ "61": [-0.13099, 0.36866, 0, 0],
323
+ "63": [0, 0.69141, 0, 0],
324
+ "65": [0, 0.69141, 0, 0],
325
+ "66": [0, 0.69141, 0, 0],
326
+ "67": [0, 0.69141, 0, 0],
327
+ "68": [0, 0.69141, 0, 0],
328
+ "69": [0, 0.69141, 0, 0],
329
+ "70": [0.12604, 0.69141, 0, 0],
330
+ "71": [0, 0.69141, 0, 0],
331
+ "72": [0.06302, 0.69141, 0, 0],
332
+ "73": [0, 0.69141, 0, 0],
333
+ "74": [0.12604, 0.69141, 0, 0],
334
+ "75": [0, 0.69141, 0, 0],
335
+ "76": [0, 0.69141, 0, 0],
336
+ "77": [0, 0.69141, 0, 0],
337
+ "78": [0, 0.69141, 0, 0],
338
+ "79": [0, 0.69141, 0, 0],
339
+ "80": [0.18906, 0.69141, 0, 0],
340
+ "81": [0.03781, 0.69141, 0, 0],
341
+ "82": [0, 0.69141, 0, 0],
342
+ "83": [0, 0.69141, 0, 0],
343
+ "84": [0, 0.69141, 0, 0],
344
+ "85": [0, 0.69141, 0, 0],
345
+ "86": [0, 0.69141, 0, 0],
346
+ "87": [0, 0.69141, 0, 0],
347
+ "88": [0, 0.69141, 0, 0],
348
+ "89": [0.18906, 0.69141, 0, 0],
349
+ "90": [0.12604, 0.69141, 0, 0],
350
+ "91": [0.24982, 0.74947, 0, 0],
351
+ "93": [0.24982, 0.74947, 0, 0],
352
+ "94": [0, 0.69141, 0, 0],
353
+ "97": [0, 0.47534, 0, 0],
354
+ "98": [0, 0.69141, 0, 0],
355
+ "99": [0, 0.47534, 0, 0],
356
+ "100": [0, 0.62119, 0, 0],
357
+ "101": [0, 0.47534, 0, 0],
358
+ "102": [0.18906, 0.69141, 0, 0],
359
+ "103": [0.18906, 0.47534, 0, 0],
360
+ "104": [0.18906, 0.69141, 0, 0],
361
+ "105": [0, 0.69141, 0, 0],
362
+ "106": [0, 0.69141, 0, 0],
363
+ "107": [0, 0.69141, 0, 0],
364
+ "108": [0, 0.69141, 0, 0],
365
+ "109": [0, 0.47534, 0, 0],
366
+ "110": [0, 0.47534, 0, 0],
367
+ "111": [0, 0.47534, 0, 0],
368
+ "112": [0.18906, 0.52396, 0, 0],
369
+ "113": [0.18906, 0.47534, 0, 0],
370
+ "114": [0, 0.47534, 0, 0],
371
+ "115": [0, 0.47534, 0, 0],
372
+ "116": [0, 0.62119, 0, 0],
373
+ "117": [0, 0.47534, 0, 0],
374
+ "118": [0, 0.52396, 0, 0],
375
+ "119": [0, 0.52396, 0, 0],
376
+ "120": [0.18906, 0.47534, 0, 0],
377
+ "121": [0.18906, 0.47534, 0, 0],
378
+ "122": [0.18906, 0.47534, 0, 0],
379
+ "8216": [0, 0.69141, 0, 0],
380
+ "8217": [0, 0.69141, 0, 0],
381
+ "58112": [0, 0.62119, 0, 0],
382
+ "58113": [0, 0.62119, 0, 0],
383
+ "58114": [0.18906, 0.69141, 0, 0],
384
+ "58115": [0.18906, 0.69141, 0, 0],
385
+ "58116": [0.18906, 0.47534, 0, 0],
386
+ "58117": [0, 0.69141, 0, 0],
387
+ "58118": [0, 0.62119, 0, 0],
388
+ "58119": [0, 0.47534, 0, 0],
389
+ },
390
+ "Main-Bold": {
391
+ "33": [0, 0.69444, 0, 0],
392
+ "34": [0, 0.69444, 0, 0],
393
+ "35": [0.19444, 0.69444, 0, 0],
394
+ "36": [0.05556, 0.75, 0, 0],
395
+ "37": [0.05556, 0.75, 0, 0],
396
+ "38": [0, 0.69444, 0, 0],
397
+ "39": [0, 0.69444, 0, 0],
398
+ "40": [0.25, 0.75, 0, 0],
399
+ "41": [0.25, 0.75, 0, 0],
400
+ "42": [0, 0.75, 0, 0],
401
+ "43": [0.13333, 0.63333, 0, 0],
402
+ "44": [0.19444, 0.15556, 0, 0],
403
+ "45": [0, 0.44444, 0, 0],
404
+ "46": [0, 0.15556, 0, 0],
405
+ "47": [0.25, 0.75, 0, 0],
406
+ "48": [0, 0.64444, 0, 0],
407
+ "49": [0, 0.64444, 0, 0],
408
+ "50": [0, 0.64444, 0, 0],
409
+ "51": [0, 0.64444, 0, 0],
410
+ "52": [0, 0.64444, 0, 0],
411
+ "53": [0, 0.64444, 0, 0],
412
+ "54": [0, 0.64444, 0, 0],
413
+ "55": [0, 0.64444, 0, 0],
414
+ "56": [0, 0.64444, 0, 0],
415
+ "57": [0, 0.64444, 0, 0],
416
+ "58": [0, 0.44444, 0, 0],
417
+ "59": [0.19444, 0.44444, 0, 0],
418
+ "60": [0.08556, 0.58556, 0, 0],
419
+ "61": [-0.10889, 0.39111, 0, 0],
420
+ "62": [0.08556, 0.58556, 0, 0],
421
+ "63": [0, 0.69444, 0, 0],
422
+ "64": [0, 0.69444, 0, 0],
423
+ "65": [0, 0.68611, 0, 0],
424
+ "66": [0, 0.68611, 0, 0],
425
+ "67": [0, 0.68611, 0, 0],
426
+ "68": [0, 0.68611, 0, 0],
427
+ "69": [0, 0.68611, 0, 0],
428
+ "70": [0, 0.68611, 0, 0],
429
+ "71": [0, 0.68611, 0, 0],
430
+ "72": [0, 0.68611, 0, 0],
431
+ "73": [0, 0.68611, 0, 0],
432
+ "74": [0, 0.68611, 0, 0],
433
+ "75": [0, 0.68611, 0, 0],
434
+ "76": [0, 0.68611, 0, 0],
435
+ "77": [0, 0.68611, 0, 0],
436
+ "78": [0, 0.68611, 0, 0],
437
+ "79": [0, 0.68611, 0, 0],
438
+ "80": [0, 0.68611, 0, 0],
439
+ "81": [0.19444, 0.68611, 0, 0],
440
+ "82": [0, 0.68611, 0, 0],
441
+ "83": [0, 0.68611, 0, 0],
442
+ "84": [0, 0.68611, 0, 0],
443
+ "85": [0, 0.68611, 0, 0],
444
+ "86": [0, 0.68611, 0.01597, 0],
445
+ "87": [0, 0.68611, 0.01597, 0],
446
+ "88": [0, 0.68611, 0, 0],
447
+ "89": [0, 0.68611, 0.02875, 0],
448
+ "90": [0, 0.68611, 0, 0],
449
+ "91": [0.25, 0.75, 0, 0],
450
+ "92": [0.25, 0.75, 0, 0],
451
+ "93": [0.25, 0.75, 0, 0],
452
+ "94": [0, 0.69444, 0, 0],
453
+ "95": [0.31, 0.13444, 0.03194, 0],
454
+ "96": [0, 0.69444, 0, 0],
455
+ "97": [0, 0.44444, 0, 0],
456
+ "98": [0, 0.69444, 0, 0],
457
+ "99": [0, 0.44444, 0, 0],
458
+ "100": [0, 0.69444, 0, 0],
459
+ "101": [0, 0.44444, 0, 0],
460
+ "102": [0, 0.69444, 0.10903, 0],
461
+ "103": [0.19444, 0.44444, 0.01597, 0],
462
+ "104": [0, 0.69444, 0, 0],
463
+ "105": [0, 0.69444, 0, 0],
464
+ "106": [0.19444, 0.69444, 0, 0],
465
+ "107": [0, 0.69444, 0, 0],
466
+ "108": [0, 0.69444, 0, 0],
467
+ "109": [0, 0.44444, 0, 0],
468
+ "110": [0, 0.44444, 0, 0],
469
+ "111": [0, 0.44444, 0, 0],
470
+ "112": [0.19444, 0.44444, 0, 0],
471
+ "113": [0.19444, 0.44444, 0, 0],
472
+ "114": [0, 0.44444, 0, 0],
473
+ "115": [0, 0.44444, 0, 0],
474
+ "116": [0, 0.63492, 0, 0],
475
+ "117": [0, 0.44444, 0, 0],
476
+ "118": [0, 0.44444, 0.01597, 0],
477
+ "119": [0, 0.44444, 0.01597, 0],
478
+ "120": [0, 0.44444, 0, 0],
479
+ "121": [0.19444, 0.44444, 0.01597, 0],
480
+ "122": [0, 0.44444, 0, 0],
481
+ "123": [0.25, 0.75, 0, 0],
482
+ "124": [0.25, 0.75, 0, 0],
483
+ "125": [0.25, 0.75, 0, 0],
484
+ "126": [0.35, 0.34444, 0, 0],
485
+ "168": [0, 0.69444, 0, 0],
486
+ "172": [0, 0.44444, 0, 0],
487
+ "175": [0, 0.59611, 0, 0],
488
+ "176": [0, 0.69444, 0, 0],
489
+ "177": [0.13333, 0.63333, 0, 0],
490
+ "180": [0, 0.69444, 0, 0],
491
+ "215": [0.13333, 0.63333, 0, 0],
492
+ "247": [0.13333, 0.63333, 0, 0],
493
+ "305": [0, 0.44444, 0, 0],
494
+ "567": [0.19444, 0.44444, 0, 0],
495
+ "710": [0, 0.69444, 0, 0],
496
+ "711": [0, 0.63194, 0, 0],
497
+ "713": [0, 0.59611, 0, 0],
498
+ "714": [0, 0.69444, 0, 0],
499
+ "715": [0, 0.69444, 0, 0],
500
+ "728": [0, 0.69444, 0, 0],
501
+ "729": [0, 0.69444, 0, 0],
502
+ "730": [0, 0.69444, 0, 0],
503
+ "732": [0, 0.69444, 0, 0],
504
+ "768": [0, 0.69444, 0, 0],
505
+ "769": [0, 0.69444, 0, 0],
506
+ "770": [0, 0.69444, 0, 0],
507
+ "771": [0, 0.69444, 0, 0],
508
+ "772": [0, 0.59611, 0, 0],
509
+ "774": [0, 0.69444, 0, 0],
510
+ "775": [0, 0.69444, 0, 0],
511
+ "776": [0, 0.69444, 0, 0],
512
+ "778": [0, 0.69444, 0, 0],
513
+ "779": [0, 0.69444, 0, 0],
514
+ "780": [0, 0.63194, 0, 0],
515
+ "824": [0.19444, 0.69444, 0, 0],
516
+ "915": [0, 0.68611, 0, 0],
517
+ "916": [0, 0.68611, 0, 0],
518
+ "920": [0, 0.68611, 0, 0],
519
+ "923": [0, 0.68611, 0, 0],
520
+ "926": [0, 0.68611, 0, 0],
521
+ "928": [0, 0.68611, 0, 0],
522
+ "931": [0, 0.68611, 0, 0],
523
+ "933": [0, 0.68611, 0, 0],
524
+ "934": [0, 0.68611, 0, 0],
525
+ "936": [0, 0.68611, 0, 0],
526
+ "937": [0, 0.68611, 0, 0],
527
+ "8211": [0, 0.44444, 0.03194, 0],
528
+ "8212": [0, 0.44444, 0.03194, 0],
529
+ "8216": [0, 0.69444, 0, 0],
530
+ "8217": [0, 0.69444, 0, 0],
531
+ "8220": [0, 0.69444, 0, 0],
532
+ "8221": [0, 0.69444, 0, 0],
533
+ "8224": [0.19444, 0.69444, 0, 0],
534
+ "8225": [0.19444, 0.69444, 0, 0],
535
+ "8242": [0, 0.55556, 0, 0],
536
+ "8407": [0, 0.72444, 0.15486, 0],
537
+ "8463": [0, 0.69444, 0, 0],
538
+ "8465": [0, 0.69444, 0, 0],
539
+ "8467": [0, 0.69444, 0, 0],
540
+ "8472": [0.19444, 0.44444, 0, 0],
541
+ "8476": [0, 0.69444, 0, 0],
542
+ "8501": [0, 0.69444, 0, 0],
543
+ "8592": [-0.10889, 0.39111, 0, 0],
544
+ "8593": [0.19444, 0.69444, 0, 0],
545
+ "8594": [-0.10889, 0.39111, 0, 0],
546
+ "8595": [0.19444, 0.69444, 0, 0],
547
+ "8596": [-0.10889, 0.39111, 0, 0],
548
+ "8597": [0.25, 0.75, 0, 0],
549
+ "8598": [0.19444, 0.69444, 0, 0],
550
+ "8599": [0.19444, 0.69444, 0, 0],
551
+ "8600": [0.19444, 0.69444, 0, 0],
552
+ "8601": [0.19444, 0.69444, 0, 0],
553
+ "8636": [-0.10889, 0.39111, 0, 0],
554
+ "8637": [-0.10889, 0.39111, 0, 0],
555
+ "8640": [-0.10889, 0.39111, 0, 0],
556
+ "8641": [-0.10889, 0.39111, 0, 0],
557
+ "8656": [-0.10889, 0.39111, 0, 0],
558
+ "8657": [0.19444, 0.69444, 0, 0],
559
+ "8658": [-0.10889, 0.39111, 0, 0],
560
+ "8659": [0.19444, 0.69444, 0, 0],
561
+ "8660": [-0.10889, 0.39111, 0, 0],
562
+ "8661": [0.25, 0.75, 0, 0],
563
+ "8704": [0, 0.69444, 0, 0],
564
+ "8706": [0, 0.69444, 0.06389, 0],
565
+ "8707": [0, 0.69444, 0, 0],
566
+ "8709": [0.05556, 0.75, 0, 0],
567
+ "8711": [0, 0.68611, 0, 0],
568
+ "8712": [0.08556, 0.58556, 0, 0],
569
+ "8715": [0.08556, 0.58556, 0, 0],
570
+ "8722": [0.13333, 0.63333, 0, 0],
571
+ "8723": [0.13333, 0.63333, 0, 0],
572
+ "8725": [0.25, 0.75, 0, 0],
573
+ "8726": [0.25, 0.75, 0, 0],
574
+ "8727": [-0.02778, 0.47222, 0, 0],
575
+ "8728": [-0.02639, 0.47361, 0, 0],
576
+ "8729": [-0.02639, 0.47361, 0, 0],
577
+ "8730": [0.18, 0.82, 0, 0],
578
+ "8733": [0, 0.44444, 0, 0],
579
+ "8734": [0, 0.44444, 0, 0],
580
+ "8736": [0, 0.69224, 0, 0],
581
+ "8739": [0.25, 0.75, 0, 0],
582
+ "8741": [0.25, 0.75, 0, 0],
583
+ "8743": [0, 0.55556, 0, 0],
584
+ "8744": [0, 0.55556, 0, 0],
585
+ "8745": [0, 0.55556, 0, 0],
586
+ "8746": [0, 0.55556, 0, 0],
587
+ "8747": [0.19444, 0.69444, 0.12778, 0],
588
+ "8764": [-0.10889, 0.39111, 0, 0],
589
+ "8768": [0.19444, 0.69444, 0, 0],
590
+ "8771": [0.00222, 0.50222, 0, 0],
591
+ "8776": [0.02444, 0.52444, 0, 0],
592
+ "8781": [0.00222, 0.50222, 0, 0],
593
+ "8801": [0.00222, 0.50222, 0, 0],
594
+ "8804": [0.19667, 0.69667, 0, 0],
595
+ "8805": [0.19667, 0.69667, 0, 0],
596
+ "8810": [0.08556, 0.58556, 0, 0],
597
+ "8811": [0.08556, 0.58556, 0, 0],
598
+ "8826": [0.08556, 0.58556, 0, 0],
599
+ "8827": [0.08556, 0.58556, 0, 0],
600
+ "8834": [0.08556, 0.58556, 0, 0],
601
+ "8835": [0.08556, 0.58556, 0, 0],
602
+ "8838": [0.19667, 0.69667, 0, 0],
603
+ "8839": [0.19667, 0.69667, 0, 0],
604
+ "8846": [0, 0.55556, 0, 0],
605
+ "8849": [0.19667, 0.69667, 0, 0],
606
+ "8850": [0.19667, 0.69667, 0, 0],
607
+ "8851": [0, 0.55556, 0, 0],
608
+ "8852": [0, 0.55556, 0, 0],
609
+ "8853": [0.13333, 0.63333, 0, 0],
610
+ "8854": [0.13333, 0.63333, 0, 0],
611
+ "8855": [0.13333, 0.63333, 0, 0],
612
+ "8856": [0.13333, 0.63333, 0, 0],
613
+ "8857": [0.13333, 0.63333, 0, 0],
614
+ "8866": [0, 0.69444, 0, 0],
615
+ "8867": [0, 0.69444, 0, 0],
616
+ "8868": [0, 0.69444, 0, 0],
617
+ "8869": [0, 0.69444, 0, 0],
618
+ "8900": [-0.02639, 0.47361, 0, 0],
619
+ "8901": [-0.02639, 0.47361, 0, 0],
620
+ "8902": [-0.02778, 0.47222, 0, 0],
621
+ "8968": [0.25, 0.75, 0, 0],
622
+ "8969": [0.25, 0.75, 0, 0],
623
+ "8970": [0.25, 0.75, 0, 0],
624
+ "8971": [0.25, 0.75, 0, 0],
625
+ "8994": [-0.13889, 0.36111, 0, 0],
626
+ "8995": [-0.13889, 0.36111, 0, 0],
627
+ "9651": [0.19444, 0.69444, 0, 0],
628
+ "9657": [-0.02778, 0.47222, 0, 0],
629
+ "9661": [0.19444, 0.69444, 0, 0],
630
+ "9667": [-0.02778, 0.47222, 0, 0],
631
+ "9711": [0.19444, 0.69444, 0, 0],
632
+ "9824": [0.12963, 0.69444, 0, 0],
633
+ "9825": [0.12963, 0.69444, 0, 0],
634
+ "9826": [0.12963, 0.69444, 0, 0],
635
+ "9827": [0.12963, 0.69444, 0, 0],
636
+ "9837": [0, 0.75, 0, 0],
637
+ "9838": [0.19444, 0.69444, 0, 0],
638
+ "9839": [0.19444, 0.69444, 0, 0],
639
+ "10216": [0.25, 0.75, 0, 0],
640
+ "10217": [0.25, 0.75, 0, 0],
641
+ "10815": [0, 0.68611, 0, 0],
642
+ "10927": [0.19667, 0.69667, 0, 0],
643
+ "10928": [0.19667, 0.69667, 0, 0],
644
+ },
645
+ "Main-Italic": {
646
+ "33": [0, 0.69444, 0.12417, 0],
647
+ "34": [0, 0.69444, 0.06961, 0],
648
+ "35": [0.19444, 0.69444, 0.06616, 0],
649
+ "37": [0.05556, 0.75, 0.13639, 0],
650
+ "38": [0, 0.69444, 0.09694, 0],
651
+ "39": [0, 0.69444, 0.12417, 0],
652
+ "40": [0.25, 0.75, 0.16194, 0],
653
+ "41": [0.25, 0.75, 0.03694, 0],
654
+ "42": [0, 0.75, 0.14917, 0],
655
+ "43": [0.05667, 0.56167, 0.03694, 0],
656
+ "44": [0.19444, 0.10556, 0, 0],
657
+ "45": [0, 0.43056, 0.02826, 0],
658
+ "46": [0, 0.10556, 0, 0],
659
+ "47": [0.25, 0.75, 0.16194, 0],
660
+ "48": [0, 0.64444, 0.13556, 0],
661
+ "49": [0, 0.64444, 0.13556, 0],
662
+ "50": [0, 0.64444, 0.13556, 0],
663
+ "51": [0, 0.64444, 0.13556, 0],
664
+ "52": [0.19444, 0.64444, 0.13556, 0],
665
+ "53": [0, 0.64444, 0.13556, 0],
666
+ "54": [0, 0.64444, 0.13556, 0],
667
+ "55": [0.19444, 0.64444, 0.13556, 0],
668
+ "56": [0, 0.64444, 0.13556, 0],
669
+ "57": [0, 0.64444, 0.13556, 0],
670
+ "58": [0, 0.43056, 0.0582, 0],
671
+ "59": [0.19444, 0.43056, 0.0582, 0],
672
+ "61": [-0.13313, 0.36687, 0.06616, 0],
673
+ "63": [0, 0.69444, 0.1225, 0],
674
+ "64": [0, 0.69444, 0.09597, 0],
675
+ "65": [0, 0.68333, 0, 0],
676
+ "66": [0, 0.68333, 0.10257, 0],
677
+ "67": [0, 0.68333, 0.14528, 0],
678
+ "68": [0, 0.68333, 0.09403, 0],
679
+ "69": [0, 0.68333, 0.12028, 0],
680
+ "70": [0, 0.68333, 0.13305, 0],
681
+ "71": [0, 0.68333, 0.08722, 0],
682
+ "72": [0, 0.68333, 0.16389, 0],
683
+ "73": [0, 0.68333, 0.15806, 0],
684
+ "74": [0, 0.68333, 0.14028, 0],
685
+ "75": [0, 0.68333, 0.14528, 0],
686
+ "76": [0, 0.68333, 0, 0],
687
+ "77": [0, 0.68333, 0.16389, 0],
688
+ "78": [0, 0.68333, 0.16389, 0],
689
+ "79": [0, 0.68333, 0.09403, 0],
690
+ "80": [0, 0.68333, 0.10257, 0],
691
+ "81": [0.19444, 0.68333, 0.09403, 0],
692
+ "82": [0, 0.68333, 0.03868, 0],
693
+ "83": [0, 0.68333, 0.11972, 0],
694
+ "84": [0, 0.68333, 0.13305, 0],
695
+ "85": [0, 0.68333, 0.16389, 0],
696
+ "86": [0, 0.68333, 0.18361, 0],
697
+ "87": [0, 0.68333, 0.18361, 0],
698
+ "88": [0, 0.68333, 0.15806, 0],
699
+ "89": [0, 0.68333, 0.19383, 0],
700
+ "90": [0, 0.68333, 0.14528, 0],
701
+ "91": [0.25, 0.75, 0.1875, 0],
702
+ "93": [0.25, 0.75, 0.10528, 0],
703
+ "94": [0, 0.69444, 0.06646, 0],
704
+ "95": [0.31, 0.12056, 0.09208, 0],
705
+ "97": [0, 0.43056, 0.07671, 0],
706
+ "98": [0, 0.69444, 0.06312, 0],
707
+ "99": [0, 0.43056, 0.05653, 0],
708
+ "100": [0, 0.69444, 0.10333, 0],
709
+ "101": [0, 0.43056, 0.07514, 0],
710
+ "102": [0.19444, 0.69444, 0.21194, 0],
711
+ "103": [0.19444, 0.43056, 0.08847, 0],
712
+ "104": [0, 0.69444, 0.07671, 0],
713
+ "105": [0, 0.65536, 0.1019, 0],
714
+ "106": [0.19444, 0.65536, 0.14467, 0],
715
+ "107": [0, 0.69444, 0.10764, 0],
716
+ "108": [0, 0.69444, 0.10333, 0],
717
+ "109": [0, 0.43056, 0.07671, 0],
718
+ "110": [0, 0.43056, 0.07671, 0],
719
+ "111": [0, 0.43056, 0.06312, 0],
720
+ "112": [0.19444, 0.43056, 0.06312, 0],
721
+ "113": [0.19444, 0.43056, 0.08847, 0],
722
+ "114": [0, 0.43056, 0.10764, 0],
723
+ "115": [0, 0.43056, 0.08208, 0],
724
+ "116": [0, 0.61508, 0.09486, 0],
725
+ "117": [0, 0.43056, 0.07671, 0],
726
+ "118": [0, 0.43056, 0.10764, 0],
727
+ "119": [0, 0.43056, 0.10764, 0],
728
+ "120": [0, 0.43056, 0.12042, 0],
729
+ "121": [0.19444, 0.43056, 0.08847, 0],
730
+ "122": [0, 0.43056, 0.12292, 0],
731
+ "126": [0.35, 0.31786, 0.11585, 0],
732
+ "163": [0, 0.69444, 0, 0],
733
+ "305": [0, 0.43056, 0, 0.02778],
734
+ "567": [0.19444, 0.43056, 0, 0.08334],
735
+ "768": [0, 0.69444, 0, 0],
736
+ "769": [0, 0.69444, 0.09694, 0],
737
+ "770": [0, 0.69444, 0.06646, 0],
738
+ "771": [0, 0.66786, 0.11585, 0],
739
+ "772": [0, 0.56167, 0.10333, 0],
740
+ "774": [0, 0.69444, 0.10806, 0],
741
+ "775": [0, 0.66786, 0.11752, 0],
742
+ "776": [0, 0.66786, 0.10474, 0],
743
+ "778": [0, 0.69444, 0, 0],
744
+ "779": [0, 0.69444, 0.1225, 0],
745
+ "780": [0, 0.62847, 0.08295, 0],
746
+ "915": [0, 0.68333, 0.13305, 0],
747
+ "916": [0, 0.68333, 0, 0],
748
+ "920": [0, 0.68333, 0.09403, 0],
749
+ "923": [0, 0.68333, 0, 0],
750
+ "926": [0, 0.68333, 0.15294, 0],
751
+ "928": [0, 0.68333, 0.16389, 0],
752
+ "931": [0, 0.68333, 0.12028, 0],
753
+ "933": [0, 0.68333, 0.11111, 0],
754
+ "934": [0, 0.68333, 0.05986, 0],
755
+ "936": [0, 0.68333, 0.11111, 0],
756
+ "937": [0, 0.68333, 0.10257, 0],
757
+ "8211": [0, 0.43056, 0.09208, 0],
758
+ "8212": [0, 0.43056, 0.09208, 0],
759
+ "8216": [0, 0.69444, 0.12417, 0],
760
+ "8217": [0, 0.69444, 0.12417, 0],
761
+ "8220": [0, 0.69444, 0.1685, 0],
762
+ "8221": [0, 0.69444, 0.06961, 0],
763
+ "8463": [0, 0.68889, 0, 0],
764
+ },
765
+ "Main-Regular": {
766
+ "32": [0, 0, 0, 0],
767
+ "33": [0, 0.69444, 0, 0],
768
+ "34": [0, 0.69444, 0, 0],
769
+ "35": [0.19444, 0.69444, 0, 0],
770
+ "36": [0.05556, 0.75, 0, 0],
771
+ "37": [0.05556, 0.75, 0, 0],
772
+ "38": [0, 0.69444, 0, 0],
773
+ "39": [0, 0.69444, 0, 0],
774
+ "40": [0.25, 0.75, 0, 0],
775
+ "41": [0.25, 0.75, 0, 0],
776
+ "42": [0, 0.75, 0, 0],
777
+ "43": [0.08333, 0.58333, 0, 0],
778
+ "44": [0.19444, 0.10556, 0, 0],
779
+ "45": [0, 0.43056, 0, 0],
780
+ "46": [0, 0.10556, 0, 0],
781
+ "47": [0.25, 0.75, 0, 0],
782
+ "48": [0, 0.64444, 0, 0],
783
+ "49": [0, 0.64444, 0, 0],
784
+ "50": [0, 0.64444, 0, 0],
785
+ "51": [0, 0.64444, 0, 0],
786
+ "52": [0, 0.64444, 0, 0],
787
+ "53": [0, 0.64444, 0, 0],
788
+ "54": [0, 0.64444, 0, 0],
789
+ "55": [0, 0.64444, 0, 0],
790
+ "56": [0, 0.64444, 0, 0],
791
+ "57": [0, 0.64444, 0, 0],
792
+ "58": [0, 0.43056, 0, 0],
793
+ "59": [0.19444, 0.43056, 0, 0],
794
+ "60": [0.0391, 0.5391, 0, 0],
795
+ "61": [-0.13313, 0.36687, 0, 0],
796
+ "62": [0.0391, 0.5391, 0, 0],
797
+ "63": [0, 0.69444, 0, 0],
798
+ "64": [0, 0.69444, 0, 0],
799
+ "65": [0, 0.68333, 0, 0],
800
+ "66": [0, 0.68333, 0, 0],
801
+ "67": [0, 0.68333, 0, 0],
802
+ "68": [0, 0.68333, 0, 0],
803
+ "69": [0, 0.68333, 0, 0],
804
+ "70": [0, 0.68333, 0, 0],
805
+ "71": [0, 0.68333, 0, 0],
806
+ "72": [0, 0.68333, 0, 0],
807
+ "73": [0, 0.68333, 0, 0],
808
+ "74": [0, 0.68333, 0, 0],
809
+ "75": [0, 0.68333, 0, 0],
810
+ "76": [0, 0.68333, 0, 0],
811
+ "77": [0, 0.68333, 0, 0],
812
+ "78": [0, 0.68333, 0, 0],
813
+ "79": [0, 0.68333, 0, 0],
814
+ "80": [0, 0.68333, 0, 0],
815
+ "81": [0.19444, 0.68333, 0, 0],
816
+ "82": [0, 0.68333, 0, 0],
817
+ "83": [0, 0.68333, 0, 0],
818
+ "84": [0, 0.68333, 0, 0],
819
+ "85": [0, 0.68333, 0, 0],
820
+ "86": [0, 0.68333, 0.01389, 0],
821
+ "87": [0, 0.68333, 0.01389, 0],
822
+ "88": [0, 0.68333, 0, 0],
823
+ "89": [0, 0.68333, 0.025, 0],
824
+ "90": [0, 0.68333, 0, 0],
825
+ "91": [0.25, 0.75, 0, 0],
826
+ "92": [0.25, 0.75, 0, 0],
827
+ "93": [0.25, 0.75, 0, 0],
828
+ "94": [0, 0.69444, 0, 0],
829
+ "95": [0.31, 0.12056, 0.02778, 0],
830
+ "96": [0, 0.69444, 0, 0],
831
+ "97": [0, 0.43056, 0, 0],
832
+ "98": [0, 0.69444, 0, 0],
833
+ "99": [0, 0.43056, 0, 0],
834
+ "100": [0, 0.69444, 0, 0],
835
+ "101": [0, 0.43056, 0, 0],
836
+ "102": [0, 0.69444, 0.07778, 0],
837
+ "103": [0.19444, 0.43056, 0.01389, 0],
838
+ "104": [0, 0.69444, 0, 0],
839
+ "105": [0, 0.66786, 0, 0],
840
+ "106": [0.19444, 0.66786, 0, 0],
841
+ "107": [0, 0.69444, 0, 0],
842
+ "108": [0, 0.69444, 0, 0],
843
+ "109": [0, 0.43056, 0, 0],
844
+ "110": [0, 0.43056, 0, 0],
845
+ "111": [0, 0.43056, 0, 0],
846
+ "112": [0.19444, 0.43056, 0, 0],
847
+ "113": [0.19444, 0.43056, 0, 0],
848
+ "114": [0, 0.43056, 0, 0],
849
+ "115": [0, 0.43056, 0, 0],
850
+ "116": [0, 0.61508, 0, 0],
851
+ "117": [0, 0.43056, 0, 0],
852
+ "118": [0, 0.43056, 0.01389, 0],
853
+ "119": [0, 0.43056, 0.01389, 0],
854
+ "120": [0, 0.43056, 0, 0],
855
+ "121": [0.19444, 0.43056, 0.01389, 0],
856
+ "122": [0, 0.43056, 0, 0],
857
+ "123": [0.25, 0.75, 0, 0],
858
+ "124": [0.25, 0.75, 0, 0],
859
+ "125": [0.25, 0.75, 0, 0],
860
+ "126": [0.35, 0.31786, 0, 0],
861
+ "160": [0, 0, 0, 0],
862
+ "168": [0, 0.66786, 0, 0],
863
+ "172": [0, 0.43056, 0, 0],
864
+ "175": [0, 0.56778, 0, 0],
865
+ "176": [0, 0.69444, 0, 0],
866
+ "177": [0.08333, 0.58333, 0, 0],
867
+ "180": [0, 0.69444, 0, 0],
868
+ "215": [0.08333, 0.58333, 0, 0],
869
+ "247": [0.08333, 0.58333, 0, 0],
870
+ "305": [0, 0.43056, 0, 0],
871
+ "567": [0.19444, 0.43056, 0, 0],
872
+ "710": [0, 0.69444, 0, 0],
873
+ "711": [0, 0.62847, 0, 0],
874
+ "713": [0, 0.56778, 0, 0],
875
+ "714": [0, 0.69444, 0, 0],
876
+ "715": [0, 0.69444, 0, 0],
877
+ "728": [0, 0.69444, 0, 0],
878
+ "729": [0, 0.66786, 0, 0],
879
+ "730": [0, 0.69444, 0, 0],
880
+ "732": [0, 0.66786, 0, 0],
881
+ "768": [0, 0.69444, 0, 0],
882
+ "769": [0, 0.69444, 0, 0],
883
+ "770": [0, 0.69444, 0, 0],
884
+ "771": [0, 0.66786, 0, 0],
885
+ "772": [0, 0.56778, 0, 0],
886
+ "774": [0, 0.69444, 0, 0],
887
+ "775": [0, 0.66786, 0, 0],
888
+ "776": [0, 0.66786, 0, 0],
889
+ "778": [0, 0.69444, 0, 0],
890
+ "779": [0, 0.69444, 0, 0],
891
+ "780": [0, 0.62847, 0, 0],
892
+ "824": [0.19444, 0.69444, 0, 0],
893
+ "915": [0, 0.68333, 0, 0],
894
+ "916": [0, 0.68333, 0, 0],
895
+ "920": [0, 0.68333, 0, 0],
896
+ "923": [0, 0.68333, 0, 0],
897
+ "926": [0, 0.68333, 0, 0],
898
+ "928": [0, 0.68333, 0, 0],
899
+ "931": [0, 0.68333, 0, 0],
900
+ "933": [0, 0.68333, 0, 0],
901
+ "934": [0, 0.68333, 0, 0],
902
+ "936": [0, 0.68333, 0, 0],
903
+ "937": [0, 0.68333, 0, 0],
904
+ "8211": [0, 0.43056, 0.02778, 0],
905
+ "8212": [0, 0.43056, 0.02778, 0],
906
+ "8216": [0, 0.69444, 0, 0],
907
+ "8217": [0, 0.69444, 0, 0],
908
+ "8220": [0, 0.69444, 0, 0],
909
+ "8221": [0, 0.69444, 0, 0],
910
+ "8224": [0.19444, 0.69444, 0, 0],
911
+ "8225": [0.19444, 0.69444, 0, 0],
912
+ "8230": [0, 0.12, 0, 0],
913
+ "8242": [0, 0.55556, 0, 0],
914
+ "8407": [0, 0.71444, 0.15382, 0],
915
+ "8463": [0, 0.68889, 0, 0],
916
+ "8465": [0, 0.69444, 0, 0],
917
+ "8467": [0, 0.69444, 0, 0.11111],
918
+ "8472": [0.19444, 0.43056, 0, 0.11111],
919
+ "8476": [0, 0.69444, 0, 0],
920
+ "8501": [0, 0.69444, 0, 0],
921
+ "8592": [-0.13313, 0.36687, 0, 0],
922
+ "8593": [0.19444, 0.69444, 0, 0],
923
+ "8594": [-0.13313, 0.36687, 0, 0],
924
+ "8595": [0.19444, 0.69444, 0, 0],
925
+ "8596": [-0.13313, 0.36687, 0, 0],
926
+ "8597": [0.25, 0.75, 0, 0],
927
+ "8598": [0.19444, 0.69444, 0, 0],
928
+ "8599": [0.19444, 0.69444, 0, 0],
929
+ "8600": [0.19444, 0.69444, 0, 0],
930
+ "8601": [0.19444, 0.69444, 0, 0],
931
+ "8614": [0.011, 0.511, 0, 0],
932
+ "8617": [0.011, 0.511, 0, 0],
933
+ "8618": [0.011, 0.511, 0, 0],
934
+ "8636": [-0.13313, 0.36687, 0, 0],
935
+ "8637": [-0.13313, 0.36687, 0, 0],
936
+ "8640": [-0.13313, 0.36687, 0, 0],
937
+ "8641": [-0.13313, 0.36687, 0, 0],
938
+ "8652": [0.011, 0.671, 0, 0],
939
+ "8656": [-0.13313, 0.36687, 0, 0],
940
+ "8657": [0.19444, 0.69444, 0, 0],
941
+ "8658": [-0.13313, 0.36687, 0, 0],
942
+ "8659": [0.19444, 0.69444, 0, 0],
943
+ "8660": [-0.13313, 0.36687, 0, 0],
944
+ "8661": [0.25, 0.75, 0, 0],
945
+ "8704": [0, 0.69444, 0, 0],
946
+ "8706": [0, 0.69444, 0.05556, 0.08334],
947
+ "8707": [0, 0.69444, 0, 0],
948
+ "8709": [0.05556, 0.75, 0, 0],
949
+ "8711": [0, 0.68333, 0, 0],
950
+ "8712": [0.0391, 0.5391, 0, 0],
951
+ "8715": [0.0391, 0.5391, 0, 0],
952
+ "8722": [0.08333, 0.58333, 0, 0],
953
+ "8723": [0.08333, 0.58333, 0, 0],
954
+ "8725": [0.25, 0.75, 0, 0],
955
+ "8726": [0.25, 0.75, 0, 0],
956
+ "8727": [-0.03472, 0.46528, 0, 0],
957
+ "8728": [-0.05555, 0.44445, 0, 0],
958
+ "8729": [-0.05555, 0.44445, 0, 0],
959
+ "8730": [0.2, 0.8, 0, 0],
960
+ "8733": [0, 0.43056, 0, 0],
961
+ "8734": [0, 0.43056, 0, 0],
962
+ "8736": [0, 0.69224, 0, 0],
963
+ "8739": [0.25, 0.75, 0, 0],
964
+ "8741": [0.25, 0.75, 0, 0],
965
+ "8743": [0, 0.55556, 0, 0],
966
+ "8744": [0, 0.55556, 0, 0],
967
+ "8745": [0, 0.55556, 0, 0],
968
+ "8746": [0, 0.55556, 0, 0],
969
+ "8747": [0.19444, 0.69444, 0.11111, 0],
970
+ "8764": [-0.13313, 0.36687, 0, 0],
971
+ "8768": [0.19444, 0.69444, 0, 0],
972
+ "8771": [-0.03625, 0.46375, 0, 0],
973
+ "8773": [-0.022, 0.589, 0, 0],
974
+ "8776": [-0.01688, 0.48312, 0, 0],
975
+ "8781": [-0.03625, 0.46375, 0, 0],
976
+ "8784": [-0.133, 0.67, 0, 0],
977
+ "8800": [0.215, 0.716, 0, 0],
978
+ "8801": [-0.03625, 0.46375, 0, 0],
979
+ "8804": [0.13597, 0.63597, 0, 0],
980
+ "8805": [0.13597, 0.63597, 0, 0],
981
+ "8810": [0.0391, 0.5391, 0, 0],
982
+ "8811": [0.0391, 0.5391, 0, 0],
983
+ "8826": [0.0391, 0.5391, 0, 0],
984
+ "8827": [0.0391, 0.5391, 0, 0],
985
+ "8834": [0.0391, 0.5391, 0, 0],
986
+ "8835": [0.0391, 0.5391, 0, 0],
987
+ "8838": [0.13597, 0.63597, 0, 0],
988
+ "8839": [0.13597, 0.63597, 0, 0],
989
+ "8846": [0, 0.55556, 0, 0],
990
+ "8849": [0.13597, 0.63597, 0, 0],
991
+ "8850": [0.13597, 0.63597, 0, 0],
992
+ "8851": [0, 0.55556, 0, 0],
993
+ "8852": [0, 0.55556, 0, 0],
994
+ "8853": [0.08333, 0.58333, 0, 0],
995
+ "8854": [0.08333, 0.58333, 0, 0],
996
+ "8855": [0.08333, 0.58333, 0, 0],
997
+ "8856": [0.08333, 0.58333, 0, 0],
998
+ "8857": [0.08333, 0.58333, 0, 0],
999
+ "8866": [0, 0.69444, 0, 0],
1000
+ "8867": [0, 0.69444, 0, 0],
1001
+ "8868": [0, 0.69444, 0, 0],
1002
+ "8869": [0, 0.69444, 0, 0],
1003
+ "8872": [0.249, 0.75, 0, 0],
1004
+ "8900": [-0.05555, 0.44445, 0, 0],
1005
+ "8901": [-0.05555, 0.44445, 0, 0],
1006
+ "8902": [-0.03472, 0.46528, 0, 0],
1007
+ "8904": [0.005, 0.505, 0, 0],
1008
+ "8942": [0.03, 0.9, 0, 0],
1009
+ "8943": [-0.19, 0.31, 0, 0],
1010
+ "8945": [-0.1, 0.82, 0, 0],
1011
+ "8968": [0.25, 0.75, 0, 0],
1012
+ "8969": [0.25, 0.75, 0, 0],
1013
+ "8970": [0.25, 0.75, 0, 0],
1014
+ "8971": [0.25, 0.75, 0, 0],
1015
+ "8994": [-0.14236, 0.35764, 0, 0],
1016
+ "8995": [-0.14236, 0.35764, 0, 0],
1017
+ "9136": [0.244, 0.744, 0, 0],
1018
+ "9137": [0.244, 0.744, 0, 0],
1019
+ "9651": [0.19444, 0.69444, 0, 0],
1020
+ "9657": [-0.03472, 0.46528, 0, 0],
1021
+ "9661": [0.19444, 0.69444, 0, 0],
1022
+ "9667": [-0.03472, 0.46528, 0, 0],
1023
+ "9711": [0.19444, 0.69444, 0, 0],
1024
+ "9824": [0.12963, 0.69444, 0, 0],
1025
+ "9825": [0.12963, 0.69444, 0, 0],
1026
+ "9826": [0.12963, 0.69444, 0, 0],
1027
+ "9827": [0.12963, 0.69444, 0, 0],
1028
+ "9837": [0, 0.75, 0, 0],
1029
+ "9838": [0.19444, 0.69444, 0, 0],
1030
+ "9839": [0.19444, 0.69444, 0, 0],
1031
+ "10216": [0.25, 0.75, 0, 0],
1032
+ "10217": [0.25, 0.75, 0, 0],
1033
+ "10222": [0.244, 0.744, 0, 0],
1034
+ "10223": [0.244, 0.744, 0, 0],
1035
+ "10229": [0.011, 0.511, 0, 0],
1036
+ "10230": [0.011, 0.511, 0, 0],
1037
+ "10231": [0.011, 0.511, 0, 0],
1038
+ "10232": [0.024, 0.525, 0, 0],
1039
+ "10233": [0.024, 0.525, 0, 0],
1040
+ "10234": [0.024, 0.525, 0, 0],
1041
+ "10236": [0.011, 0.511, 0, 0],
1042
+ "10815": [0, 0.68333, 0, 0],
1043
+ "10927": [0.13597, 0.63597, 0, 0],
1044
+ "10928": [0.13597, 0.63597, 0, 0],
1045
+ },
1046
+ "Math-BoldItalic": {
1047
+ "47": [0.19444, 0.69444, 0, 0],
1048
+ "65": [0, 0.68611, 0, 0],
1049
+ "66": [0, 0.68611, 0.04835, 0],
1050
+ "67": [0, 0.68611, 0.06979, 0],
1051
+ "68": [0, 0.68611, 0.03194, 0],
1052
+ "69": [0, 0.68611, 0.05451, 0],
1053
+ "70": [0, 0.68611, 0.15972, 0],
1054
+ "71": [0, 0.68611, 0, 0],
1055
+ "72": [0, 0.68611, 0.08229, 0],
1056
+ "73": [0, 0.68611, 0.07778, 0],
1057
+ "74": [0, 0.68611, 0.10069, 0],
1058
+ "75": [0, 0.68611, 0.06979, 0],
1059
+ "76": [0, 0.68611, 0, 0],
1060
+ "77": [0, 0.68611, 0.11424, 0],
1061
+ "78": [0, 0.68611, 0.11424, 0],
1062
+ "79": [0, 0.68611, 0.03194, 0],
1063
+ "80": [0, 0.68611, 0.15972, 0],
1064
+ "81": [0.19444, 0.68611, 0, 0],
1065
+ "82": [0, 0.68611, 0.00421, 0],
1066
+ "83": [0, 0.68611, 0.05382, 0],
1067
+ "84": [0, 0.68611, 0.15972, 0],
1068
+ "85": [0, 0.68611, 0.11424, 0],
1069
+ "86": [0, 0.68611, 0.25555, 0],
1070
+ "87": [0, 0.68611, 0.15972, 0],
1071
+ "88": [0, 0.68611, 0.07778, 0],
1072
+ "89": [0, 0.68611, 0.25555, 0],
1073
+ "90": [0, 0.68611, 0.06979, 0],
1074
+ "97": [0, 0.44444, 0, 0],
1075
+ "98": [0, 0.69444, 0, 0],
1076
+ "99": [0, 0.44444, 0, 0],
1077
+ "100": [0, 0.69444, 0, 0],
1078
+ "101": [0, 0.44444, 0, 0],
1079
+ "102": [0.19444, 0.69444, 0.11042, 0],
1080
+ "103": [0.19444, 0.44444, 0.03704, 0],
1081
+ "104": [0, 0.69444, 0, 0],
1082
+ "105": [0, 0.69326, 0, 0],
1083
+ "106": [0.19444, 0.69326, 0.0622, 0],
1084
+ "107": [0, 0.69444, 0.01852, 0],
1085
+ "108": [0, 0.69444, 0.0088, 0],
1086
+ "109": [0, 0.44444, 0, 0],
1087
+ "110": [0, 0.44444, 0, 0],
1088
+ "111": [0, 0.44444, 0, 0],
1089
+ "112": [0.19444, 0.44444, 0, 0],
1090
+ "113": [0.19444, 0.44444, 0.03704, 0],
1091
+ "114": [0, 0.44444, 0.03194, 0],
1092
+ "115": [0, 0.44444, 0, 0],
1093
+ "116": [0, 0.63492, 0, 0],
1094
+ "117": [0, 0.44444, 0, 0],
1095
+ "118": [0, 0.44444, 0.03704, 0],
1096
+ "119": [0, 0.44444, 0.02778, 0],
1097
+ "120": [0, 0.44444, 0, 0],
1098
+ "121": [0.19444, 0.44444, 0.03704, 0],
1099
+ "122": [0, 0.44444, 0.04213, 0],
1100
+ "915": [0, 0.68611, 0.15972, 0],
1101
+ "916": [0, 0.68611, 0, 0],
1102
+ "920": [0, 0.68611, 0.03194, 0],
1103
+ "923": [0, 0.68611, 0, 0],
1104
+ "926": [0, 0.68611, 0.07458, 0],
1105
+ "928": [0, 0.68611, 0.08229, 0],
1106
+ "931": [0, 0.68611, 0.05451, 0],
1107
+ "933": [0, 0.68611, 0.15972, 0],
1108
+ "934": [0, 0.68611, 0, 0],
1109
+ "936": [0, 0.68611, 0.11653, 0],
1110
+ "937": [0, 0.68611, 0.04835, 0],
1111
+ "945": [0, 0.44444, 0, 0],
1112
+ "946": [0.19444, 0.69444, 0.03403, 0],
1113
+ "947": [0.19444, 0.44444, 0.06389, 0],
1114
+ "948": [0, 0.69444, 0.03819, 0],
1115
+ "949": [0, 0.44444, 0, 0],
1116
+ "950": [0.19444, 0.69444, 0.06215, 0],
1117
+ "951": [0.19444, 0.44444, 0.03704, 0],
1118
+ "952": [0, 0.69444, 0.03194, 0],
1119
+ "953": [0, 0.44444, 0, 0],
1120
+ "954": [0, 0.44444, 0, 0],
1121
+ "955": [0, 0.69444, 0, 0],
1122
+ "956": [0.19444, 0.44444, 0, 0],
1123
+ "957": [0, 0.44444, 0.06898, 0],
1124
+ "958": [0.19444, 0.69444, 0.03021, 0],
1125
+ "959": [0, 0.44444, 0, 0],
1126
+ "960": [0, 0.44444, 0.03704, 0],
1127
+ "961": [0.19444, 0.44444, 0, 0],
1128
+ "962": [0.09722, 0.44444, 0.07917, 0],
1129
+ "963": [0, 0.44444, 0.03704, 0],
1130
+ "964": [0, 0.44444, 0.13472, 0],
1131
+ "965": [0, 0.44444, 0.03704, 0],
1132
+ "966": [0.19444, 0.44444, 0, 0],
1133
+ "967": [0.19444, 0.44444, 0, 0],
1134
+ "968": [0.19444, 0.69444, 0.03704, 0],
1135
+ "969": [0, 0.44444, 0.03704, 0],
1136
+ "977": [0, 0.69444, 0, 0],
1137
+ "981": [0.19444, 0.69444, 0, 0],
1138
+ "982": [0, 0.44444, 0.03194, 0],
1139
+ "1009": [0.19444, 0.44444, 0, 0],
1140
+ "1013": [0, 0.44444, 0, 0],
1141
+ },
1142
+ "Math-Italic": {
1143
+ "47": [0.19444, 0.69444, 0, 0],
1144
+ "65": [0, 0.68333, 0, 0.13889],
1145
+ "66": [0, 0.68333, 0.05017, 0.08334],
1146
+ "67": [0, 0.68333, 0.07153, 0.08334],
1147
+ "68": [0, 0.68333, 0.02778, 0.05556],
1148
+ "69": [0, 0.68333, 0.05764, 0.08334],
1149
+ "70": [0, 0.68333, 0.13889, 0.08334],
1150
+ "71": [0, 0.68333, 0, 0.08334],
1151
+ "72": [0, 0.68333, 0.08125, 0.05556],
1152
+ "73": [0, 0.68333, 0.07847, 0.11111],
1153
+ "74": [0, 0.68333, 0.09618, 0.16667],
1154
+ "75": [0, 0.68333, 0.07153, 0.05556],
1155
+ "76": [0, 0.68333, 0, 0.02778],
1156
+ "77": [0, 0.68333, 0.10903, 0.08334],
1157
+ "78": [0, 0.68333, 0.10903, 0.08334],
1158
+ "79": [0, 0.68333, 0.02778, 0.08334],
1159
+ "80": [0, 0.68333, 0.13889, 0.08334],
1160
+ "81": [0.19444, 0.68333, 0, 0.08334],
1161
+ "82": [0, 0.68333, 0.00773, 0.08334],
1162
+ "83": [0, 0.68333, 0.05764, 0.08334],
1163
+ "84": [0, 0.68333, 0.13889, 0.08334],
1164
+ "85": [0, 0.68333, 0.10903, 0.02778],
1165
+ "86": [0, 0.68333, 0.22222, 0],
1166
+ "87": [0, 0.68333, 0.13889, 0],
1167
+ "88": [0, 0.68333, 0.07847, 0.08334],
1168
+ "89": [0, 0.68333, 0.22222, 0],
1169
+ "90": [0, 0.68333, 0.07153, 0.08334],
1170
+ "97": [0, 0.43056, 0, 0],
1171
+ "98": [0, 0.69444, 0, 0],
1172
+ "99": [0, 0.43056, 0, 0.05556],
1173
+ "100": [0, 0.69444, 0, 0.16667],
1174
+ "101": [0, 0.43056, 0, 0.05556],
1175
+ "102": [0.19444, 0.69444, 0.10764, 0.16667],
1176
+ "103": [0.19444, 0.43056, 0.03588, 0.02778],
1177
+ "104": [0, 0.69444, 0, 0],
1178
+ "105": [0, 0.65952, 0, 0],
1179
+ "106": [0.19444, 0.65952, 0.05724, 0],
1180
+ "107": [0, 0.69444, 0.03148, 0],
1181
+ "108": [0, 0.69444, 0.01968, 0.08334],
1182
+ "109": [0, 0.43056, 0, 0],
1183
+ "110": [0, 0.43056, 0, 0],
1184
+ "111": [0, 0.43056, 0, 0.05556],
1185
+ "112": [0.19444, 0.43056, 0, 0.08334],
1186
+ "113": [0.19444, 0.43056, 0.03588, 0.08334],
1187
+ "114": [0, 0.43056, 0.02778, 0.05556],
1188
+ "115": [0, 0.43056, 0, 0.05556],
1189
+ "116": [0, 0.61508, 0, 0.08334],
1190
+ "117": [0, 0.43056, 0, 0.02778],
1191
+ "118": [0, 0.43056, 0.03588, 0.02778],
1192
+ "119": [0, 0.43056, 0.02691, 0.08334],
1193
+ "120": [0, 0.43056, 0, 0.02778],
1194
+ "121": [0.19444, 0.43056, 0.03588, 0.05556],
1195
+ "122": [0, 0.43056, 0.04398, 0.05556],
1196
+ "915": [0, 0.68333, 0.13889, 0.08334],
1197
+ "916": [0, 0.68333, 0, 0.16667],
1198
+ "920": [0, 0.68333, 0.02778, 0.08334],
1199
+ "923": [0, 0.68333, 0, 0.16667],
1200
+ "926": [0, 0.68333, 0.07569, 0.08334],
1201
+ "928": [0, 0.68333, 0.08125, 0.05556],
1202
+ "931": [0, 0.68333, 0.05764, 0.08334],
1203
+ "933": [0, 0.68333, 0.13889, 0.05556],
1204
+ "934": [0, 0.68333, 0, 0.08334],
1205
+ "936": [0, 0.68333, 0.11, 0.05556],
1206
+ "937": [0, 0.68333, 0.05017, 0.08334],
1207
+ "945": [0, 0.43056, 0.0037, 0.02778],
1208
+ "946": [0.19444, 0.69444, 0.05278, 0.08334],
1209
+ "947": [0.19444, 0.43056, 0.05556, 0],
1210
+ "948": [0, 0.69444, 0.03785, 0.05556],
1211
+ "949": [0, 0.43056, 0, 0.08334],
1212
+ "950": [0.19444, 0.69444, 0.07378, 0.08334],
1213
+ "951": [0.19444, 0.43056, 0.03588, 0.05556],
1214
+ "952": [0, 0.69444, 0.02778, 0.08334],
1215
+ "953": [0, 0.43056, 0, 0.05556],
1216
+ "954": [0, 0.43056, 0, 0],
1217
+ "955": [0, 0.69444, 0, 0],
1218
+ "956": [0.19444, 0.43056, 0, 0.02778],
1219
+ "957": [0, 0.43056, 0.06366, 0.02778],
1220
+ "958": [0.19444, 0.69444, 0.04601, 0.11111],
1221
+ "959": [0, 0.43056, 0, 0.05556],
1222
+ "960": [0, 0.43056, 0.03588, 0],
1223
+ "961": [0.19444, 0.43056, 0, 0.08334],
1224
+ "962": [0.09722, 0.43056, 0.07986, 0.08334],
1225
+ "963": [0, 0.43056, 0.03588, 0],
1226
+ "964": [0, 0.43056, 0.1132, 0.02778],
1227
+ "965": [0, 0.43056, 0.03588, 0.02778],
1228
+ "966": [0.19444, 0.43056, 0, 0.08334],
1229
+ "967": [0.19444, 0.43056, 0, 0.05556],
1230
+ "968": [0.19444, 0.69444, 0.03588, 0.11111],
1231
+ "969": [0, 0.43056, 0.03588, 0],
1232
+ "977": [0, 0.69444, 0, 0.08334],
1233
+ "981": [0.19444, 0.69444, 0, 0.08334],
1234
+ "982": [0, 0.43056, 0.02778, 0],
1235
+ "1009": [0.19444, 0.43056, 0, 0.08334],
1236
+ "1013": [0, 0.43056, 0, 0.05556],
1237
+ },
1238
+ "Math-Regular": {
1239
+ "65": [0, 0.68333, 0, 0.13889],
1240
+ "66": [0, 0.68333, 0.05017, 0.08334],
1241
+ "67": [0, 0.68333, 0.07153, 0.08334],
1242
+ "68": [0, 0.68333, 0.02778, 0.05556],
1243
+ "69": [0, 0.68333, 0.05764, 0.08334],
1244
+ "70": [0, 0.68333, 0.13889, 0.08334],
1245
+ "71": [0, 0.68333, 0, 0.08334],
1246
+ "72": [0, 0.68333, 0.08125, 0.05556],
1247
+ "73": [0, 0.68333, 0.07847, 0.11111],
1248
+ "74": [0, 0.68333, 0.09618, 0.16667],
1249
+ "75": [0, 0.68333, 0.07153, 0.05556],
1250
+ "76": [0, 0.68333, 0, 0.02778],
1251
+ "77": [0, 0.68333, 0.10903, 0.08334],
1252
+ "78": [0, 0.68333, 0.10903, 0.08334],
1253
+ "79": [0, 0.68333, 0.02778, 0.08334],
1254
+ "80": [0, 0.68333, 0.13889, 0.08334],
1255
+ "81": [0.19444, 0.68333, 0, 0.08334],
1256
+ "82": [0, 0.68333, 0.00773, 0.08334],
1257
+ "83": [0, 0.68333, 0.05764, 0.08334],
1258
+ "84": [0, 0.68333, 0.13889, 0.08334],
1259
+ "85": [0, 0.68333, 0.10903, 0.02778],
1260
+ "86": [0, 0.68333, 0.22222, 0],
1261
+ "87": [0, 0.68333, 0.13889, 0],
1262
+ "88": [0, 0.68333, 0.07847, 0.08334],
1263
+ "89": [0, 0.68333, 0.22222, 0],
1264
+ "90": [0, 0.68333, 0.07153, 0.08334],
1265
+ "97": [0, 0.43056, 0, 0],
1266
+ "98": [0, 0.69444, 0, 0],
1267
+ "99": [0, 0.43056, 0, 0.05556],
1268
+ "100": [0, 0.69444, 0, 0.16667],
1269
+ "101": [0, 0.43056, 0, 0.05556],
1270
+ "102": [0.19444, 0.69444, 0.10764, 0.16667],
1271
+ "103": [0.19444, 0.43056, 0.03588, 0.02778],
1272
+ "104": [0, 0.69444, 0, 0],
1273
+ "105": [0, 0.65952, 0, 0],
1274
+ "106": [0.19444, 0.65952, 0.05724, 0],
1275
+ "107": [0, 0.69444, 0.03148, 0],
1276
+ "108": [0, 0.69444, 0.01968, 0.08334],
1277
+ "109": [0, 0.43056, 0, 0],
1278
+ "110": [0, 0.43056, 0, 0],
1279
+ "111": [0, 0.43056, 0, 0.05556],
1280
+ "112": [0.19444, 0.43056, 0, 0.08334],
1281
+ "113": [0.19444, 0.43056, 0.03588, 0.08334],
1282
+ "114": [0, 0.43056, 0.02778, 0.05556],
1283
+ "115": [0, 0.43056, 0, 0.05556],
1284
+ "116": [0, 0.61508, 0, 0.08334],
1285
+ "117": [0, 0.43056, 0, 0.02778],
1286
+ "118": [0, 0.43056, 0.03588, 0.02778],
1287
+ "119": [0, 0.43056, 0.02691, 0.08334],
1288
+ "120": [0, 0.43056, 0, 0.02778],
1289
+ "121": [0.19444, 0.43056, 0.03588, 0.05556],
1290
+ "122": [0, 0.43056, 0.04398, 0.05556],
1291
+ "915": [0, 0.68333, 0.13889, 0.08334],
1292
+ "916": [0, 0.68333, 0, 0.16667],
1293
+ "920": [0, 0.68333, 0.02778, 0.08334],
1294
+ "923": [0, 0.68333, 0, 0.16667],
1295
+ "926": [0, 0.68333, 0.07569, 0.08334],
1296
+ "928": [0, 0.68333, 0.08125, 0.05556],
1297
+ "931": [0, 0.68333, 0.05764, 0.08334],
1298
+ "933": [0, 0.68333, 0.13889, 0.05556],
1299
+ "934": [0, 0.68333, 0, 0.08334],
1300
+ "936": [0, 0.68333, 0.11, 0.05556],
1301
+ "937": [0, 0.68333, 0.05017, 0.08334],
1302
+ "945": [0, 0.43056, 0.0037, 0.02778],
1303
+ "946": [0.19444, 0.69444, 0.05278, 0.08334],
1304
+ "947": [0.19444, 0.43056, 0.05556, 0],
1305
+ "948": [0, 0.69444, 0.03785, 0.05556],
1306
+ "949": [0, 0.43056, 0, 0.08334],
1307
+ "950": [0.19444, 0.69444, 0.07378, 0.08334],
1308
+ "951": [0.19444, 0.43056, 0.03588, 0.05556],
1309
+ "952": [0, 0.69444, 0.02778, 0.08334],
1310
+ "953": [0, 0.43056, 0, 0.05556],
1311
+ "954": [0, 0.43056, 0, 0],
1312
+ "955": [0, 0.69444, 0, 0],
1313
+ "956": [0.19444, 0.43056, 0, 0.02778],
1314
+ "957": [0, 0.43056, 0.06366, 0.02778],
1315
+ "958": [0.19444, 0.69444, 0.04601, 0.11111],
1316
+ "959": [0, 0.43056, 0, 0.05556],
1317
+ "960": [0, 0.43056, 0.03588, 0],
1318
+ "961": [0.19444, 0.43056, 0, 0.08334],
1319
+ "962": [0.09722, 0.43056, 0.07986, 0.08334],
1320
+ "963": [0, 0.43056, 0.03588, 0],
1321
+ "964": [0, 0.43056, 0.1132, 0.02778],
1322
+ "965": [0, 0.43056, 0.03588, 0.02778],
1323
+ "966": [0.19444, 0.43056, 0, 0.08334],
1324
+ "967": [0.19444, 0.43056, 0, 0.05556],
1325
+ "968": [0.19444, 0.69444, 0.03588, 0.11111],
1326
+ "969": [0, 0.43056, 0.03588, 0],
1327
+ "977": [0, 0.69444, 0, 0.08334],
1328
+ "981": [0.19444, 0.69444, 0, 0.08334],
1329
+ "982": [0, 0.43056, 0.02778, 0],
1330
+ "1009": [0.19444, 0.43056, 0, 0.08334],
1331
+ "1013": [0, 0.43056, 0, 0.05556],
1332
+ },
1333
+ "SansSerif-Regular": {
1334
+ "33": [0, 0.69444, 0, 0],
1335
+ "34": [0, 0.69444, 0, 0],
1336
+ "35": [0.19444, 0.69444, 0, 0],
1337
+ "36": [0.05556, 0.75, 0, 0],
1338
+ "37": [0.05556, 0.75, 0, 0],
1339
+ "38": [0, 0.69444, 0, 0],
1340
+ "39": [0, 0.69444, 0, 0],
1341
+ "40": [0.25, 0.75, 0, 0],
1342
+ "41": [0.25, 0.75, 0, 0],
1343
+ "42": [0, 0.75, 0, 0],
1344
+ "43": [0.08333, 0.58333, 0, 0],
1345
+ "44": [0.125, 0.08333, 0, 0],
1346
+ "45": [0, 0.44444, 0, 0],
1347
+ "46": [0, 0.08333, 0, 0],
1348
+ "47": [0.25, 0.75, 0, 0],
1349
+ "48": [0, 0.65556, 0, 0],
1350
+ "49": [0, 0.65556, 0, 0],
1351
+ "50": [0, 0.65556, 0, 0],
1352
+ "51": [0, 0.65556, 0, 0],
1353
+ "52": [0, 0.65556, 0, 0],
1354
+ "53": [0, 0.65556, 0, 0],
1355
+ "54": [0, 0.65556, 0, 0],
1356
+ "55": [0, 0.65556, 0, 0],
1357
+ "56": [0, 0.65556, 0, 0],
1358
+ "57": [0, 0.65556, 0, 0],
1359
+ "58": [0, 0.44444, 0, 0],
1360
+ "59": [0.125, 0.44444, 0, 0],
1361
+ "61": [-0.13, 0.37, 0, 0],
1362
+ "63": [0, 0.69444, 0, 0],
1363
+ "64": [0, 0.69444, 0, 0],
1364
+ "65": [0, 0.69444, 0, 0],
1365
+ "66": [0, 0.69444, 0, 0],
1366
+ "67": [0, 0.69444, 0, 0],
1367
+ "68": [0, 0.69444, 0, 0],
1368
+ "69": [0, 0.69444, 0, 0],
1369
+ "70": [0, 0.69444, 0, 0],
1370
+ "71": [0, 0.69444, 0, 0],
1371
+ "72": [0, 0.69444, 0, 0],
1372
+ "73": [0, 0.69444, 0, 0],
1373
+ "74": [0, 0.69444, 0, 0],
1374
+ "75": [0, 0.69444, 0, 0],
1375
+ "76": [0, 0.69444, 0, 0],
1376
+ "77": [0, 0.69444, 0, 0],
1377
+ "78": [0, 0.69444, 0, 0],
1378
+ "79": [0, 0.69444, 0, 0],
1379
+ "80": [0, 0.69444, 0, 0],
1380
+ "81": [0.125, 0.69444, 0, 0],
1381
+ "82": [0, 0.69444, 0, 0],
1382
+ "83": [0, 0.69444, 0, 0],
1383
+ "84": [0, 0.69444, 0, 0],
1384
+ "85": [0, 0.69444, 0, 0],
1385
+ "86": [0, 0.69444, 0.01389, 0],
1386
+ "87": [0, 0.69444, 0.01389, 0],
1387
+ "88": [0, 0.69444, 0, 0],
1388
+ "89": [0, 0.69444, 0.025, 0],
1389
+ "90": [0, 0.69444, 0, 0],
1390
+ "91": [0.25, 0.75, 0, 0],
1391
+ "93": [0.25, 0.75, 0, 0],
1392
+ "94": [0, 0.69444, 0, 0],
1393
+ "95": [0.35, 0.09444, 0.02778, 0],
1394
+ "97": [0, 0.44444, 0, 0],
1395
+ "98": [0, 0.69444, 0, 0],
1396
+ "99": [0, 0.44444, 0, 0],
1397
+ "100": [0, 0.69444, 0, 0],
1398
+ "101": [0, 0.44444, 0, 0],
1399
+ "102": [0, 0.69444, 0.06944, 0],
1400
+ "103": [0.19444, 0.44444, 0.01389, 0],
1401
+ "104": [0, 0.69444, 0, 0],
1402
+ "105": [0, 0.67937, 0, 0],
1403
+ "106": [0.19444, 0.67937, 0, 0],
1404
+ "107": [0, 0.69444, 0, 0],
1405
+ "108": [0, 0.69444, 0, 0],
1406
+ "109": [0, 0.44444, 0, 0],
1407
+ "110": [0, 0.44444, 0, 0],
1408
+ "111": [0, 0.44444, 0, 0],
1409
+ "112": [0.19444, 0.44444, 0, 0],
1410
+ "113": [0.19444, 0.44444, 0, 0],
1411
+ "114": [0, 0.44444, 0.01389, 0],
1412
+ "115": [0, 0.44444, 0, 0],
1413
+ "116": [0, 0.57143, 0, 0],
1414
+ "117": [0, 0.44444, 0, 0],
1415
+ "118": [0, 0.44444, 0.01389, 0],
1416
+ "119": [0, 0.44444, 0.01389, 0],
1417
+ "120": [0, 0.44444, 0, 0],
1418
+ "121": [0.19444, 0.44444, 0.01389, 0],
1419
+ "122": [0, 0.44444, 0, 0],
1420
+ "126": [0.35, 0.32659, 0, 0],
1421
+ "305": [0, 0.44444, 0, 0],
1422
+ "567": [0.19444, 0.44444, 0, 0],
1423
+ "768": [0, 0.69444, 0, 0],
1424
+ "769": [0, 0.69444, 0, 0],
1425
+ "770": [0, 0.69444, 0, 0],
1426
+ "771": [0, 0.67659, 0, 0],
1427
+ "772": [0, 0.60889, 0, 0],
1428
+ "774": [0, 0.69444, 0, 0],
1429
+ "775": [0, 0.67937, 0, 0],
1430
+ "776": [0, 0.67937, 0, 0],
1431
+ "778": [0, 0.69444, 0, 0],
1432
+ "779": [0, 0.69444, 0, 0],
1433
+ "780": [0, 0.63194, 0, 0],
1434
+ "915": [0, 0.69444, 0, 0],
1435
+ "916": [0, 0.69444, 0, 0],
1436
+ "920": [0, 0.69444, 0, 0],
1437
+ "923": [0, 0.69444, 0, 0],
1438
+ "926": [0, 0.69444, 0, 0],
1439
+ "928": [0, 0.69444, 0, 0],
1440
+ "931": [0, 0.69444, 0, 0],
1441
+ "933": [0, 0.69444, 0, 0],
1442
+ "934": [0, 0.69444, 0, 0],
1443
+ "936": [0, 0.69444, 0, 0],
1444
+ "937": [0, 0.69444, 0, 0],
1445
+ "8211": [0, 0.44444, 0.02778, 0],
1446
+ "8212": [0, 0.44444, 0.02778, 0],
1447
+ "8216": [0, 0.69444, 0, 0],
1448
+ "8217": [0, 0.69444, 0, 0],
1449
+ "8220": [0, 0.69444, 0, 0],
1450
+ "8221": [0, 0.69444, 0, 0],
1451
+ },
1452
+ "Script-Regular": {
1453
+ "65": [0, 0.7, 0.22925, 0],
1454
+ "66": [0, 0.7, 0.04087, 0],
1455
+ "67": [0, 0.7, 0.1689, 0],
1456
+ "68": [0, 0.7, 0.09371, 0],
1457
+ "69": [0, 0.7, 0.18583, 0],
1458
+ "70": [0, 0.7, 0.13634, 0],
1459
+ "71": [0, 0.7, 0.17322, 0],
1460
+ "72": [0, 0.7, 0.29694, 0],
1461
+ "73": [0, 0.7, 0.19189, 0],
1462
+ "74": [0.27778, 0.7, 0.19189, 0],
1463
+ "75": [0, 0.7, 0.31259, 0],
1464
+ "76": [0, 0.7, 0.19189, 0],
1465
+ "77": [0, 0.7, 0.15981, 0],
1466
+ "78": [0, 0.7, 0.3525, 0],
1467
+ "79": [0, 0.7, 0.08078, 0],
1468
+ "80": [0, 0.7, 0.08078, 0],
1469
+ "81": [0, 0.7, 0.03305, 0],
1470
+ "82": [0, 0.7, 0.06259, 0],
1471
+ "83": [0, 0.7, 0.19189, 0],
1472
+ "84": [0, 0.7, 0.29087, 0],
1473
+ "85": [0, 0.7, 0.25815, 0],
1474
+ "86": [0, 0.7, 0.27523, 0],
1475
+ "87": [0, 0.7, 0.27523, 0],
1476
+ "88": [0, 0.7, 0.26006, 0],
1477
+ "89": [0, 0.7, 0.2939, 0],
1478
+ "90": [0, 0.7, 0.24037, 0],
1479
+ },
1480
+ "Size1-Regular": {
1481
+ "40": [0.35001, 0.85, 0, 0],
1482
+ "41": [0.35001, 0.85, 0, 0],
1483
+ "47": [0.35001, 0.85, 0, 0],
1484
+ "91": [0.35001, 0.85, 0, 0],
1485
+ "92": [0.35001, 0.85, 0, 0],
1486
+ "93": [0.35001, 0.85, 0, 0],
1487
+ "123": [0.35001, 0.85, 0, 0],
1488
+ "125": [0.35001, 0.85, 0, 0],
1489
+ "710": [0, 0.72222, 0, 0],
1490
+ "732": [0, 0.72222, 0, 0],
1491
+ "770": [0, 0.72222, 0, 0],
1492
+ "771": [0, 0.72222, 0, 0],
1493
+ "8214": [-0.00099, 0.601, 0, 0],
1494
+ "8593": [1e-05, 0.6, 0, 0],
1495
+ "8595": [1e-05, 0.6, 0, 0],
1496
+ "8657": [1e-05, 0.6, 0, 0],
1497
+ "8659": [1e-05, 0.6, 0, 0],
1498
+ "8719": [0.25001, 0.75, 0, 0],
1499
+ "8720": [0.25001, 0.75, 0, 0],
1500
+ "8721": [0.25001, 0.75, 0, 0],
1501
+ "8730": [0.35001, 0.85, 0, 0],
1502
+ "8739": [-0.00599, 0.606, 0, 0],
1503
+ "8741": [-0.00599, 0.606, 0, 0],
1504
+ "8747": [0.30612, 0.805, 0.19445, 0],
1505
+ "8748": [0.306, 0.805, 0.19445, 0],
1506
+ "8749": [0.306, 0.805, 0.19445, 0],
1507
+ "8750": [0.30612, 0.805, 0.19445, 0],
1508
+ "8896": [0.25001, 0.75, 0, 0],
1509
+ "8897": [0.25001, 0.75, 0, 0],
1510
+ "8898": [0.25001, 0.75, 0, 0],
1511
+ "8899": [0.25001, 0.75, 0, 0],
1512
+ "8968": [0.35001, 0.85, 0, 0],
1513
+ "8969": [0.35001, 0.85, 0, 0],
1514
+ "8970": [0.35001, 0.85, 0, 0],
1515
+ "8971": [0.35001, 0.85, 0, 0],
1516
+ "9168": [-0.00099, 0.601, 0, 0],
1517
+ "10216": [0.35001, 0.85, 0, 0],
1518
+ "10217": [0.35001, 0.85, 0, 0],
1519
+ "10752": [0.25001, 0.75, 0, 0],
1520
+ "10753": [0.25001, 0.75, 0, 0],
1521
+ "10754": [0.25001, 0.75, 0, 0],
1522
+ "10756": [0.25001, 0.75, 0, 0],
1523
+ "10758": [0.25001, 0.75, 0, 0],
1524
+ },
1525
+ "Size2-Regular": {
1526
+ "40": [0.65002, 1.15, 0, 0],
1527
+ "41": [0.65002, 1.15, 0, 0],
1528
+ "47": [0.65002, 1.15, 0, 0],
1529
+ "91": [0.65002, 1.15, 0, 0],
1530
+ "92": [0.65002, 1.15, 0, 0],
1531
+ "93": [0.65002, 1.15, 0, 0],
1532
+ "123": [0.65002, 1.15, 0, 0],
1533
+ "125": [0.65002, 1.15, 0, 0],
1534
+ "710": [0, 0.75, 0, 0],
1535
+ "732": [0, 0.75, 0, 0],
1536
+ "770": [0, 0.75, 0, 0],
1537
+ "771": [0, 0.75, 0, 0],
1538
+ "8719": [0.55001, 1.05, 0, 0],
1539
+ "8720": [0.55001, 1.05, 0, 0],
1540
+ "8721": [0.55001, 1.05, 0, 0],
1541
+ "8730": [0.65002, 1.15, 0, 0],
1542
+ "8747": [0.86225, 1.36, 0.44445, 0],
1543
+ "8748": [0.862, 1.36, 0.44445, 0],
1544
+ "8749": [0.862, 1.36, 0.44445, 0],
1545
+ "8750": [0.86225, 1.36, 0.44445, 0],
1546
+ "8896": [0.55001, 1.05, 0, 0],
1547
+ "8897": [0.55001, 1.05, 0, 0],
1548
+ "8898": [0.55001, 1.05, 0, 0],
1549
+ "8899": [0.55001, 1.05, 0, 0],
1550
+ "8968": [0.65002, 1.15, 0, 0],
1551
+ "8969": [0.65002, 1.15, 0, 0],
1552
+ "8970": [0.65002, 1.15, 0, 0],
1553
+ "8971": [0.65002, 1.15, 0, 0],
1554
+ "10216": [0.65002, 1.15, 0, 0],
1555
+ "10217": [0.65002, 1.15, 0, 0],
1556
+ "10752": [0.55001, 1.05, 0, 0],
1557
+ "10753": [0.55001, 1.05, 0, 0],
1558
+ "10754": [0.55001, 1.05, 0, 0],
1559
+ "10756": [0.55001, 1.05, 0, 0],
1560
+ "10758": [0.55001, 1.05, 0, 0],
1561
+ },
1562
+ "Size3-Regular": {
1563
+ "40": [0.95003, 1.45, 0, 0],
1564
+ "41": [0.95003, 1.45, 0, 0],
1565
+ "47": [0.95003, 1.45, 0, 0],
1566
+ "91": [0.95003, 1.45, 0, 0],
1567
+ "92": [0.95003, 1.45, 0, 0],
1568
+ "93": [0.95003, 1.45, 0, 0],
1569
+ "123": [0.95003, 1.45, 0, 0],
1570
+ "125": [0.95003, 1.45, 0, 0],
1571
+ "710": [0, 0.75, 0, 0],
1572
+ "732": [0, 0.75, 0, 0],
1573
+ "770": [0, 0.75, 0, 0],
1574
+ "771": [0, 0.75, 0, 0],
1575
+ "8730": [0.95003, 1.45, 0, 0],
1576
+ "8968": [0.95003, 1.45, 0, 0],
1577
+ "8969": [0.95003, 1.45, 0, 0],
1578
+ "8970": [0.95003, 1.45, 0, 0],
1579
+ "8971": [0.95003, 1.45, 0, 0],
1580
+ "10216": [0.95003, 1.45, 0, 0],
1581
+ "10217": [0.95003, 1.45, 0, 0],
1582
+ },
1583
+ "Size4-Regular": {
1584
+ "40": [1.25003, 1.75, 0, 0],
1585
+ "41": [1.25003, 1.75, 0, 0],
1586
+ "47": [1.25003, 1.75, 0, 0],
1587
+ "91": [1.25003, 1.75, 0, 0],
1588
+ "92": [1.25003, 1.75, 0, 0],
1589
+ "93": [1.25003, 1.75, 0, 0],
1590
+ "123": [1.25003, 1.75, 0, 0],
1591
+ "125": [1.25003, 1.75, 0, 0],
1592
+ "710": [0, 0.825, 0, 0],
1593
+ "732": [0, 0.825, 0, 0],
1594
+ "770": [0, 0.825, 0, 0],
1595
+ "771": [0, 0.825, 0, 0],
1596
+ "8730": [1.25003, 1.75, 0, 0],
1597
+ "8968": [1.25003, 1.75, 0, 0],
1598
+ "8969": [1.25003, 1.75, 0, 0],
1599
+ "8970": [1.25003, 1.75, 0, 0],
1600
+ "8971": [1.25003, 1.75, 0, 0],
1601
+ "9115": [0.64502, 1.155, 0, 0],
1602
+ "9116": [1e-05, 0.6, 0, 0],
1603
+ "9117": [0.64502, 1.155, 0, 0],
1604
+ "9118": [0.64502, 1.155, 0, 0],
1605
+ "9119": [1e-05, 0.6, 0, 0],
1606
+ "9120": [0.64502, 1.155, 0, 0],
1607
+ "9121": [0.64502, 1.155, 0, 0],
1608
+ "9122": [-0.00099, 0.601, 0, 0],
1609
+ "9123": [0.64502, 1.155, 0, 0],
1610
+ "9124": [0.64502, 1.155, 0, 0],
1611
+ "9125": [-0.00099, 0.601, 0, 0],
1612
+ "9126": [0.64502, 1.155, 0, 0],
1613
+ "9127": [1e-05, 0.9, 0, 0],
1614
+ "9128": [0.65002, 1.15, 0, 0],
1615
+ "9129": [0.90001, 0, 0, 0],
1616
+ "9130": [0, 0.3, 0, 0],
1617
+ "9131": [1e-05, 0.9, 0, 0],
1618
+ "9132": [0.65002, 1.15, 0, 0],
1619
+ "9133": [0.90001, 0, 0, 0],
1620
+ "9143": [0.88502, 0.915, 0, 0],
1621
+ "10216": [1.25003, 1.75, 0, 0],
1622
+ "10217": [1.25003, 1.75, 0, 0],
1623
+ "57344": [-0.00499, 0.605, 0, 0],
1624
+ "57345": [-0.00499, 0.605, 0, 0],
1625
+ "57680": [0, 0.12, 0, 0],
1626
+ "57681": [0, 0.12, 0, 0],
1627
+ "57682": [0, 0.12, 0, 0],
1628
+ "57683": [0, 0.12, 0, 0],
1629
+ },
1630
+ "Typewriter-Regular": {
1631
+ "33": [0, 0.61111, 0, 0],
1632
+ "34": [0, 0.61111, 0, 0],
1633
+ "35": [0, 0.61111, 0, 0],
1634
+ "36": [0.08333, 0.69444, 0, 0],
1635
+ "37": [0.08333, 0.69444, 0, 0],
1636
+ "38": [0, 0.61111, 0, 0],
1637
+ "39": [0, 0.61111, 0, 0],
1638
+ "40": [0.08333, 0.69444, 0, 0],
1639
+ "41": [0.08333, 0.69444, 0, 0],
1640
+ "42": [0, 0.52083, 0, 0],
1641
+ "43": [-0.08056, 0.53055, 0, 0],
1642
+ "44": [0.13889, 0.125, 0, 0],
1643
+ "45": [-0.08056, 0.53055, 0, 0],
1644
+ "46": [0, 0.125, 0, 0],
1645
+ "47": [0.08333, 0.69444, 0, 0],
1646
+ "48": [0, 0.61111, 0, 0],
1647
+ "49": [0, 0.61111, 0, 0],
1648
+ "50": [0, 0.61111, 0, 0],
1649
+ "51": [0, 0.61111, 0, 0],
1650
+ "52": [0, 0.61111, 0, 0],
1651
+ "53": [0, 0.61111, 0, 0],
1652
+ "54": [0, 0.61111, 0, 0],
1653
+ "55": [0, 0.61111, 0, 0],
1654
+ "56": [0, 0.61111, 0, 0],
1655
+ "57": [0, 0.61111, 0, 0],
1656
+ "58": [0, 0.43056, 0, 0],
1657
+ "59": [0.13889, 0.43056, 0, 0],
1658
+ "60": [-0.05556, 0.55556, 0, 0],
1659
+ "61": [-0.19549, 0.41562, 0, 0],
1660
+ "62": [-0.05556, 0.55556, 0, 0],
1661
+ "63": [0, 0.61111, 0, 0],
1662
+ "64": [0, 0.61111, 0, 0],
1663
+ "65": [0, 0.61111, 0, 0],
1664
+ "66": [0, 0.61111, 0, 0],
1665
+ "67": [0, 0.61111, 0, 0],
1666
+ "68": [0, 0.61111, 0, 0],
1667
+ "69": [0, 0.61111, 0, 0],
1668
+ "70": [0, 0.61111, 0, 0],
1669
+ "71": [0, 0.61111, 0, 0],
1670
+ "72": [0, 0.61111, 0, 0],
1671
+ "73": [0, 0.61111, 0, 0],
1672
+ "74": [0, 0.61111, 0, 0],
1673
+ "75": [0, 0.61111, 0, 0],
1674
+ "76": [0, 0.61111, 0, 0],
1675
+ "77": [0, 0.61111, 0, 0],
1676
+ "78": [0, 0.61111, 0, 0],
1677
+ "79": [0, 0.61111, 0, 0],
1678
+ "80": [0, 0.61111, 0, 0],
1679
+ "81": [0.13889, 0.61111, 0, 0],
1680
+ "82": [0, 0.61111, 0, 0],
1681
+ "83": [0, 0.61111, 0, 0],
1682
+ "84": [0, 0.61111, 0, 0],
1683
+ "85": [0, 0.61111, 0, 0],
1684
+ "86": [0, 0.61111, 0, 0],
1685
+ "87": [0, 0.61111, 0, 0],
1686
+ "88": [0, 0.61111, 0, 0],
1687
+ "89": [0, 0.61111, 0, 0],
1688
+ "90": [0, 0.61111, 0, 0],
1689
+ "91": [0.08333, 0.69444, 0, 0],
1690
+ "92": [0.08333, 0.69444, 0, 0],
1691
+ "93": [0.08333, 0.69444, 0, 0],
1692
+ "94": [0, 0.61111, 0, 0],
1693
+ "95": [0.09514, 0, 0, 0],
1694
+ "96": [0, 0.61111, 0, 0],
1695
+ "97": [0, 0.43056, 0, 0],
1696
+ "98": [0, 0.61111, 0, 0],
1697
+ "99": [0, 0.43056, 0, 0],
1698
+ "100": [0, 0.61111, 0, 0],
1699
+ "101": [0, 0.43056, 0, 0],
1700
+ "102": [0, 0.61111, 0, 0],
1701
+ "103": [0.22222, 0.43056, 0, 0],
1702
+ "104": [0, 0.61111, 0, 0],
1703
+ "105": [0, 0.61111, 0, 0],
1704
+ "106": [0.22222, 0.61111, 0, 0],
1705
+ "107": [0, 0.61111, 0, 0],
1706
+ "108": [0, 0.61111, 0, 0],
1707
+ "109": [0, 0.43056, 0, 0],
1708
+ "110": [0, 0.43056, 0, 0],
1709
+ "111": [0, 0.43056, 0, 0],
1710
+ "112": [0.22222, 0.43056, 0, 0],
1711
+ "113": [0.22222, 0.43056, 0, 0],
1712
+ "114": [0, 0.43056, 0, 0],
1713
+ "115": [0, 0.43056, 0, 0],
1714
+ "116": [0, 0.55358, 0, 0],
1715
+ "117": [0, 0.43056, 0, 0],
1716
+ "118": [0, 0.43056, 0, 0],
1717
+ "119": [0, 0.43056, 0, 0],
1718
+ "120": [0, 0.43056, 0, 0],
1719
+ "121": [0.22222, 0.43056, 0, 0],
1720
+ "122": [0, 0.43056, 0, 0],
1721
+ "123": [0.08333, 0.69444, 0, 0],
1722
+ "124": [0.08333, 0.69444, 0, 0],
1723
+ "125": [0.08333, 0.69444, 0, 0],
1724
+ "126": [0, 0.61111, 0, 0],
1725
+ "127": [0, 0.61111, 0, 0],
1726
+ "305": [0, 0.43056, 0, 0],
1727
+ "567": [0.22222, 0.43056, 0, 0],
1728
+ "768": [0, 0.61111, 0, 0],
1729
+ "769": [0, 0.61111, 0, 0],
1730
+ "770": [0, 0.61111, 0, 0],
1731
+ "771": [0, 0.61111, 0, 0],
1732
+ "772": [0, 0.56555, 0, 0],
1733
+ "774": [0, 0.61111, 0, 0],
1734
+ "776": [0, 0.61111, 0, 0],
1735
+ "778": [0, 0.61111, 0, 0],
1736
+ "780": [0, 0.56597, 0, 0],
1737
+ "915": [0, 0.61111, 0, 0],
1738
+ "916": [0, 0.61111, 0, 0],
1739
+ "920": [0, 0.61111, 0, 0],
1740
+ "923": [0, 0.61111, 0, 0],
1741
+ "926": [0, 0.61111, 0, 0],
1742
+ "928": [0, 0.61111, 0, 0],
1743
+ "931": [0, 0.61111, 0, 0],
1744
+ "933": [0, 0.61111, 0, 0],
1745
+ "934": [0, 0.61111, 0, 0],
1746
+ "936": [0, 0.61111, 0, 0],
1747
+ "937": [0, 0.61111, 0, 0],
1748
+ "2018": [0, 0.61111, 0, 0],
1749
+ "2019": [0, 0.61111, 0, 0],
1750
+ "8242": [0, 0.61111, 0, 0],
1751
+ },
1752
+ };
modules/tokenize_latex/third_party/katex/src/functions.js ADDED
@@ -0,0 +1,585 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var utils = require("./utils");
2
+ var ParseError = require("./ParseError");
3
+
4
+ /* This file contains a list of functions that we parse, identified by
5
+ * the calls to defineFunction.
6
+ *
7
+ * The first argument to defineFunction is a single name or a list of names.
8
+ * All functions named in such a list will share a single implementation.
9
+ *
10
+ * Each declared function can have associated properties, which
11
+ * include the following:
12
+ *
13
+ * - numArgs: The number of arguments the function takes.
14
+ * If this is the only property, it can be passed as a number
15
+ * instead of an element of a properties object.
16
+ * - argTypes: (optional) An array corresponding to each argument of the
17
+ * function, giving the type of argument that should be parsed. Its
18
+ * length should be equal to `numArgs + numOptionalArgs`. Valid
19
+ * types:
20
+ * - "size": A size-like thing, such as "1em" or "5ex"
21
+ * - "color": An html color, like "#abc" or "blue"
22
+ * - "original": The same type as the environment that the
23
+ * function being parsed is in (e.g. used for the
24
+ * bodies of functions like \color where the first
25
+ * argument is special and the second argument is
26
+ * parsed normally)
27
+ * Other possible types (probably shouldn't be used)
28
+ * - "text": Text-like (e.g. \text)
29
+ * - "math": Normal math
30
+ * If undefined, this will be treated as an appropriate length
31
+ * array of "original" strings
32
+ * - greediness: (optional) The greediness of the function to use ungrouped
33
+ * arguments.
34
+ *
35
+ * E.g. if you have an expression
36
+ * \sqrt \frac 1 2
37
+ * since \frac has greediness=2 vs \sqrt's greediness=1, \frac
38
+ * will use the two arguments '1' and '2' as its two arguments,
39
+ * then that whole function will be used as the argument to
40
+ * \sqrt. On the other hand, the expressions
41
+ * \frac \frac 1 2 3
42
+ * and
43
+ * \frac \sqrt 1 2
44
+ * will fail because \frac and \frac have equal greediness
45
+ * and \sqrt has a lower greediness than \frac respectively. To
46
+ * make these parse, we would have to change them to:
47
+ * \frac {\frac 1 2} 3
48
+ * and
49
+ * \frac {\sqrt 1} 2
50
+ *
51
+ * The default value is `1`
52
+ * - allowedInText: (optional) Whether or not the function is allowed inside
53
+ * text mode (default false)
54
+ * - numOptionalArgs: (optional) The number of optional arguments the function
55
+ * should parse. If the optional arguments aren't found,
56
+ * `null` will be passed to the handler in their place.
57
+ * (default 0)
58
+ *
59
+ * The last argument is that implementation, the handler for the function(s).
60
+ * It is called to handle these functions and their arguments.
61
+ * It receives two arguments:
62
+ * - context contains information and references provided by the parser
63
+ * - args is an array of arguments obtained from TeX input
64
+ * The context contains the following properties:
65
+ * - funcName: the text (i.e. name) of the function, including \
66
+ * - parser: the parser object
67
+ * - lexer: the lexer object
68
+ * - positions: the positions in the overall string of the function
69
+ * and the arguments.
70
+ * The latter three should only be used to produce error messages.
71
+ *
72
+ * The function should return an object with the following keys:
73
+ * - type: The type of element that this is. This is then used in
74
+ * buildHTML/buildMathML to determine which function
75
+ * should be called to build this node into a DOM node
76
+ * Any other data can be added to the object, which will be passed
77
+ * in to the function in buildHTML/buildMathML as `group.value`.
78
+ */
79
+
80
+ function defineFunction(names, props, handler) {
81
+ if (typeof names === "string") {
82
+ names = [names];
83
+ }
84
+ if (typeof props === "number") {
85
+ props = { numArgs: props };
86
+ }
87
+ // Set default values of functions
88
+ var data = {
89
+ numArgs: props.numArgs,
90
+ argTypes: props.argTypes,
91
+ greediness: (props.greediness === undefined) ? 1 : props.greediness,
92
+ allowedInText: !!props.allowedInText,
93
+ numOptionalArgs: props.numOptionalArgs || 0,
94
+ handler: handler,
95
+ };
96
+ for (var i = 0; i < names.length; ++i) {
97
+ module.exports[names[i]] = data;
98
+ }
99
+ }
100
+
101
+ // A normal square root
102
+ defineFunction("\\sqrt", {
103
+ numArgs: 1,
104
+ numOptionalArgs: 1,
105
+ }, function(context, args) {
106
+ var index = args[0];
107
+ var body = args[1];
108
+ return {
109
+ type: "sqrt",
110
+ body: body,
111
+ index: index,
112
+ };
113
+ });
114
+
115
+ // Some non-mathy text
116
+ defineFunction(["\\text", "\\mbox", "\\hbox", "\\vbox"], {
117
+ numArgs: 1,
118
+ argTypes: ["text"],
119
+ greediness: 2,
120
+ }, function(context, args) {
121
+ var body = args[0];
122
+ // Since the corresponding buildHTML/buildMathML function expects a
123
+ // list of elements, we normalize for different kinds of arguments
124
+ // TODO(emily): maybe this should be done somewhere else
125
+ var inner;
126
+ if (body.type === "ordgroup") {
127
+ inner = body.value;
128
+ } else {
129
+ inner = [body];
130
+ }
131
+
132
+ return {
133
+ type: "text",
134
+ body: inner,
135
+ };
136
+ });
137
+
138
+ // A two-argument custom color
139
+ defineFunction("\\color", {
140
+ numArgs: 2,
141
+ allowedInText: true,
142
+ greediness: 3,
143
+ argTypes: ["color", "original"],
144
+ }, function(context, args) {
145
+ var color = args[0];
146
+ var body = args[1];
147
+ // Normalize the different kinds of bodies (see \text above)
148
+ var inner;
149
+ if (body.type === "ordgroup") {
150
+ inner = body.value;
151
+ } else {
152
+ inner = [body];
153
+ }
154
+
155
+ return {
156
+ type: "color",
157
+ color: color.value,
158
+ value: inner,
159
+ };
160
+ });
161
+
162
+ // An overline
163
+ defineFunction("\\overline", {
164
+ numArgs: 1,
165
+ }, function(context, args) {
166
+ var body = args[0];
167
+ return {
168
+ type: "overline",
169
+ body: body,
170
+ };
171
+ });
172
+
173
+ // An underline
174
+ defineFunction("\\underline", {
175
+ numArgs: 1,
176
+ }, function(context, args) {
177
+ var body = args[0];
178
+ return {
179
+ type: "underline",
180
+ body: body,
181
+ };
182
+ });
183
+
184
+ // A box of the width and height
185
+ defineFunction("\\rule", {
186
+ numArgs: 2,
187
+ numOptionalArgs: 1,
188
+ argTypes: ["size", "size", "size"],
189
+ }, function(context, args) {
190
+ var shift = args[0];
191
+ var width = args[1];
192
+ var height = args[2];
193
+ return {
194
+ type: "rule",
195
+ shift: shift && shift.value,
196
+ width: width.value,
197
+ height: height.value,
198
+ };
199
+ });
200
+
201
+ // A KaTeX logo
202
+ defineFunction("\\KaTeX", {
203
+ numArgs: 0,
204
+ }, function(context) {
205
+ return {
206
+ type: "katex",
207
+ };
208
+ });
209
+
210
+ defineFunction("\\phantom", {
211
+ numArgs: 1,
212
+ }, function(context, args) {
213
+ var body = args[0];
214
+ var inner;
215
+ if (body.type === "ordgroup") {
216
+ inner = body.value;
217
+ } else {
218
+ inner = [body];
219
+ }
220
+
221
+ return {
222
+ type: "phantom",
223
+ value: inner,
224
+ };
225
+ });
226
+
227
+ // Extra data needed for the delimiter handler down below
228
+ var delimiterSizes = {
229
+ "\\bigl" : {type: "open", size: 1},
230
+ "\\Bigl" : {type: "open", size: 2},
231
+ "\\biggl": {type: "open", size: 3},
232
+ "\\Biggl": {type: "open", size: 4},
233
+ "\\bigr" : {type: "close", size: 1},
234
+ "\\Bigr" : {type: "close", size: 2},
235
+ "\\biggr": {type: "close", size: 3},
236
+ "\\Biggr": {type: "close", size: 4},
237
+ "\\bigm" : {type: "rel", size: 1},
238
+ "\\Bigm" : {type: "rel", size: 2},
239
+ "\\biggm": {type: "rel", size: 3},
240
+ "\\Biggm": {type: "rel", size: 4},
241
+ "\\big" : {type: "textord", size: 1},
242
+ "\\Big" : {type: "textord", size: 2},
243
+ "\\bigg" : {type: "textord", size: 3},
244
+ "\\Bigg" : {type: "textord", size: 4},
245
+ };
246
+
247
+ var delimiters = [
248
+ "(", ")", "[", "\\lbrack", "]", "\\rbrack",
249
+ "\\{", "\\lbrace", "\\}", "\\rbrace",
250
+ "\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
251
+ "<", ">", "\\langle", "\\rangle", "\\lt", "\\gt",
252
+ "\\lvert", "\\rvert", "\\lVert", "\\rVert",
253
+ "\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache",
254
+ "/", "\\backslash",
255
+ "|", "\\vert", "\\|", "\\Vert",
256
+ "\\uparrow", "\\Uparrow",
257
+ "\\downarrow", "\\Downarrow",
258
+ "\\updownarrow", "\\Updownarrow",
259
+ ".",
260
+ ];
261
+
262
+ var fontAliases = {
263
+ "\\Bbb": "\\mathbb",
264
+ "\\bold": "\\mathbf",
265
+ "\\frak": "\\mathfrak",
266
+ };
267
+
268
+ // Single-argument color functions
269
+ defineFunction([
270
+ "\\blue", "\\orange", "\\pink", "\\red",
271
+ "\\green", "\\gray", "\\purple",
272
+ "\\blueA", "\\blueB", "\\blueC", "\\blueD", "\\blueE",
273
+ "\\tealA", "\\tealB", "\\tealC", "\\tealD", "\\tealE",
274
+ "\\greenA", "\\greenB", "\\greenC", "\\greenD", "\\greenE",
275
+ "\\goldA", "\\goldB", "\\goldC", "\\goldD", "\\goldE",
276
+ "\\redA", "\\redB", "\\redC", "\\redD", "\\redE",
277
+ "\\maroonA", "\\maroonB", "\\maroonC", "\\maroonD", "\\maroonE",
278
+ "\\purpleA", "\\purpleB", "\\purpleC", "\\purpleD", "\\purpleE",
279
+ "\\mintA", "\\mintB", "\\mintC",
280
+ "\\grayA", "\\grayB", "\\grayC", "\\grayD", "\\grayE",
281
+ "\\grayF", "\\grayG", "\\grayH", "\\grayI",
282
+ "\\kaBlue", "\\kaGreen",
283
+ ], {
284
+ numArgs: 1,
285
+ allowedInText: true,
286
+ greediness: 3,
287
+ }, function(context, args) {
288
+ var body = args[0];
289
+ var atoms;
290
+ if (body.type === "ordgroup") {
291
+ atoms = body.value;
292
+ } else {
293
+ atoms = [body];
294
+ }
295
+
296
+ return {
297
+ type: "color",
298
+ color: "katex-" + context.funcName.slice(1),
299
+ value: atoms,
300
+ };
301
+ });
302
+
303
+ // There are 2 flags for operators; whether they produce limits in
304
+ // displaystyle, and whether they are symbols and should grow in
305
+ // displaystyle. These four groups cover the four possible choices.
306
+
307
+ // No limits, not symbols
308
+ defineFunction([
309
+ "\\arcsin", "\\arccos", "\\arctan", "\\arg", "\\cos", "\\cosh",
310
+ "\\cot", "\\coth", "\\csc", "\\deg", "\\dim", "\\exp", "\\hom",
311
+ "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin", "\\sinh",
312
+ "\\tan", "\\tanh",
313
+ ], {
314
+ numArgs: 0,
315
+ }, function(context) {
316
+ return {
317
+ type: "op",
318
+ limits: false,
319
+ symbol: false,
320
+ body: context.funcName,
321
+ };
322
+ });
323
+
324
+ // Limits, not symbols
325
+ defineFunction([
326
+ "\\det", "\\gcd", "\\inf", "\\lim", "\\liminf", "\\limsup", "\\max",
327
+ "\\min", "\\Pr", "\\sup",
328
+ ], {
329
+ numArgs: 0,
330
+ }, function(context) {
331
+ return {
332
+ type: "op",
333
+ limits: true,
334
+ symbol: false,
335
+ body: context.funcName,
336
+ };
337
+ });
338
+
339
+ // No limits, symbols
340
+ defineFunction([
341
+ "\\int", "\\iint", "\\iiint", "\\oint",
342
+ ], {
343
+ numArgs: 0,
344
+ }, function(context) {
345
+ return {
346
+ type: "op",
347
+ limits: false,
348
+ symbol: true,
349
+ body: context.funcName,
350
+ };
351
+ });
352
+
353
+ // Limits, symbols
354
+ defineFunction([
355
+ "\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap",
356
+ "\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes",
357
+ "\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint",
358
+ ], {
359
+ numArgs: 0,
360
+ }, function(context) {
361
+ return {
362
+ type: "op",
363
+ limits: true,
364
+ symbol: true,
365
+ body: context.funcName,
366
+ };
367
+ });
368
+
369
+ // Fractions
370
+ defineFunction([
371
+ "\\dfrac", "\\frac", "\\tfrac",
372
+ "\\dbinom", "\\binom", "\\tbinom",
373
+ ], {
374
+ numArgs: 2,
375
+ greediness: 2,
376
+ }, function(context, args) {
377
+ var numer = args[0];
378
+ var denom = args[1];
379
+ var hasBarLine;
380
+ var leftDelim = null;
381
+ var rightDelim = null;
382
+ var size = "auto";
383
+
384
+ switch (context.funcName) {
385
+ case "\\dfrac":
386
+ case "\\frac":
387
+ case "\\tfrac":
388
+ hasBarLine = true;
389
+ break;
390
+ case "\\dbinom":
391
+ case "\\binom":
392
+ case "\\tbinom":
393
+ hasBarLine = false;
394
+ leftDelim = "(";
395
+ rightDelim = ")";
396
+ break;
397
+ default:
398
+ throw new Error("Unrecognized genfrac command");
399
+ }
400
+
401
+ switch (context.funcName) {
402
+ case "\\dfrac":
403
+ case "\\dbinom":
404
+ size = "display";
405
+ break;
406
+ case "\\tfrac":
407
+ case "\\tbinom":
408
+ size = "text";
409
+ break;
410
+ }
411
+
412
+ return {
413
+ type: "genfrac",
414
+ numer: numer,
415
+ denom: denom,
416
+ hasBarLine: hasBarLine,
417
+ leftDelim: leftDelim,
418
+ rightDelim: rightDelim,
419
+ size: size,
420
+ };
421
+ });
422
+
423
+ // Left and right overlap functions
424
+ defineFunction(["\\llap", "\\rlap"], {
425
+ numArgs: 1,
426
+ allowedInText: true,
427
+ }, function(context, args) {
428
+ var body = args[0];
429
+ return {
430
+ type: context.funcName.slice(1),
431
+ body: body,
432
+ };
433
+ });
434
+
435
+ // Delimiter functions
436
+ defineFunction([
437
+ "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
438
+ "\\bigr", "\\Bigr", "\\biggr", "\\Biggr",
439
+ "\\bigm", "\\Bigm", "\\biggm", "\\Biggm",
440
+ "\\big", "\\Big", "\\bigg", "\\Bigg",
441
+ "\\left", "\\right"
442
+ ], {
443
+ numArgs: 1,
444
+ }, function(context, args) {
445
+ var delim = args[0];
446
+ if (!utils.contains(delimiters, delim.value)) {
447
+ throw new ParseError(
448
+ "Invalid delimiter: '" + delim.value + "' after '" +
449
+ context.funcName + "'",
450
+ context.lexer, context.positions[1]);
451
+ }
452
+
453
+ // \left and \right are caught somewhere in Parser.js, which is
454
+ // why this data doesn't match what is in buildHTML.
455
+ if (context.funcName === "\\left" || context.funcName === "\\right") {
456
+ return {
457
+ type: "leftright",
458
+ value: delim.value,
459
+ funcName: context.funcName
460
+ };
461
+ } else {
462
+ return {
463
+ type: "delimsizing",
464
+ size: delimiterSizes[context.funcName].size,
465
+ delimType: delimiterSizes[context.funcName].type,
466
+ value: delim.value,
467
+ funcName: context.funcName
468
+ };
469
+ }
470
+ });
471
+
472
+ // Sizing functions (handled in Parser.js explicitly, hence no handler)
473
+ defineFunction([
474
+ "\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
475
+ "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", "\\textrm", "\\rm", "\\cal", "\\bf", "\\siptstyle", "\\boldmath", "\\it"
476
+ ], 0, null);
477
+
478
+ // Style changing functions (handled in Parser.js explicitly, hence no
479
+ // handler)
480
+ defineFunction([
481
+ "\\displaystyle", "\\textstyle", "\\scriptstyle",
482
+ "\\scriptscriptstyle",
483
+ ], 0, null);
484
+
485
+ defineFunction([
486
+ // styles
487
+ "\\mathrm", "\\mathit", "\\mathbf","\\mathop","\\stackrel",
488
+
489
+ // families
490
+ "\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
491
+ "\\mathtt",
492
+
493
+ "\\label", "\\comment", "\\hspace", "\\vspace", "\\atop", "\\fbox", "\\tag", "\\makebox",
494
+ "\\raisebox", "\\framebox", "\\circle", "\\line", "\\put", "\\vphantom", "\\textup", "\\noalign",
495
+
496
+ // aliases
497
+ "\\Bbb", "\\bold", "\\frak",
498
+ ], {
499
+ numArgs: 1,
500
+ greediness: 2,
501
+ }, function(context, args) {
502
+ var body = args[0];
503
+ var func = context.funcName;
504
+ if (func in fontAliases) {
505
+ func = fontAliases[func];
506
+ }
507
+ return {
508
+ type: "font",
509
+ font: func.slice(1),
510
+ body: body,
511
+ };
512
+ });
513
+
514
+ // Accents
515
+ defineFunction([
516
+ "\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
517
+ "\\check", "\\hat", "\\vec", "\\dot",
518
+ // We don't support expanding accents yet
519
+ // "\\widetilde", "\\widehat"
520
+ ], {
521
+ numArgs: 1,
522
+ }, function(context, args) {
523
+ var base = args[0];
524
+ return {
525
+ type: "accent",
526
+ accent: context.funcName,
527
+ base: base,
528
+ };
529
+ });
530
+
531
+ // Infix generalized fractions
532
+ defineFunction(["\\over", "\\choose"], {
533
+ numArgs: 0,
534
+ }, function(context) {
535
+ var replaceWith;
536
+ switch (context.funcName) {
537
+ case "\\over":
538
+ replaceWith = "\\frac";
539
+ break;
540
+ case "\\choose":
541
+ replaceWith = "\\binom";
542
+ break;
543
+ default:
544
+ throw new Error("Unrecognized infix genfrac command");
545
+ }
546
+ return {
547
+ type: "infix",
548
+ replaceWith: replaceWith,
549
+ };
550
+ });
551
+
552
+ // Row breaks for aligned data
553
+ defineFunction(["\\\\", "\\cr"], {
554
+ numArgs: 0,
555
+ numOptionalArgs: 1,
556
+ argTypes: ["size"],
557
+ }, function(context, args) {
558
+ var size = args[0];
559
+ return {
560
+ type: "cr",
561
+ size: size,
562
+ };
563
+ });
564
+
565
+ // Environment delimiters
566
+ defineFunction(["\\begin", "\\end"], {
567
+ numArgs: 1,
568
+ argTypes: ["text"],
569
+ }, function(context, args) {
570
+ var nameGroup = args[0];
571
+ if (nameGroup.type !== "ordgroup") {
572
+ throw new ParseError(
573
+ "Invalid environment name",
574
+ context.lexer, context.positions[1]);
575
+ }
576
+ var name = "";
577
+ for (var i = 0; i < nameGroup.value.length; ++i) {
578
+ name += nameGroup.value[i].value;
579
+ }
580
+ return {
581
+ type: "environment",
582
+ name: name,
583
+ namepos: context.positions[1],
584
+ };
585
+ });
modules/tokenize_latex/third_party/katex/src/mathMLTree.js ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * These objects store data about MathML nodes. This is the MathML equivalent
3
+ * of the types in domTree.js. Since MathML handles its own rendering, and
4
+ * since we're mainly using MathML to improve accessibility, we don't manage
5
+ * any of the styling state that the plain DOM nodes do.
6
+ *
7
+ * The `toNode` and `toMarkup` functions work simlarly to how they do in
8
+ * domTree.js, creating namespaced DOM nodes and HTML text markup respectively.
9
+ */
10
+
11
+ var utils = require("./utils");
12
+
13
+ /**
14
+ * This node represents a general purpose MathML node of any type. The
15
+ * constructor requires the type of node to create (for example, `"mo"` or
16
+ * `"mspace"`, corresponding to `<mo>` and `<mspace>` tags).
17
+ */
18
+ function MathNode(type, children) {
19
+ this.type = type;
20
+ this.attributes = {};
21
+ this.children = children || [];
22
+ }
23
+
24
+ /**
25
+ * Sets an attribute on a MathML node. MathML depends on attributes to convey a
26
+ * semantic content, so this is used heavily.
27
+ */
28
+ MathNode.prototype.setAttribute = function(name, value) {
29
+ this.attributes[name] = value;
30
+ };
31
+
32
+ /**
33
+ * Converts the math node into a MathML-namespaced DOM element.
34
+ */
35
+ MathNode.prototype.toNode = function() {
36
+ var node = document.createElementNS(
37
+ "http://www.w3.org/1998/Math/MathML", this.type);
38
+
39
+ for (var attr in this.attributes) {
40
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
41
+ node.setAttribute(attr, this.attributes[attr]);
42
+ }
43
+ }
44
+
45
+ for (var i = 0; i < this.children.length; i++) {
46
+ node.appendChild(this.children[i].toNode());
47
+ }
48
+
49
+ return node;
50
+ };
51
+
52
+ /**
53
+ * Converts the math node into an HTML markup string.
54
+ */
55
+ MathNode.prototype.toMarkup = function() {
56
+ var markup = "<" + this.type;
57
+
58
+ // Add the attributes
59
+ for (var attr in this.attributes) {
60
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
61
+ markup += " " + attr + "=\"";
62
+ markup += utils.escape(this.attributes[attr]);
63
+ markup += "\"";
64
+ }
65
+ }
66
+
67
+ markup += ">";
68
+
69
+ for (var i = 0; i < this.children.length; i++) {
70
+ markup += this.children[i].toMarkup();
71
+ }
72
+
73
+ markup += "</" + this.type + ">";
74
+
75
+ return markup;
76
+ };
77
+
78
+ /**
79
+ * This node represents a piece of text.
80
+ */
81
+ function TextNode(text) {
82
+ this.text = text;
83
+ }
84
+
85
+ /**
86
+ * Converts the text node into a DOM text node.
87
+ */
88
+ TextNode.prototype.toNode = function() {
89
+ return document.createTextNode(this.text);
90
+ };
91
+
92
+ /**
93
+ * Converts the text node into HTML markup (which is just the text itself).
94
+ */
95
+ TextNode.prototype.toMarkup = function() {
96
+ return utils.escape(this.text);
97
+ };
98
+
99
+ module.exports = {
100
+ MathNode: MathNode,
101
+ TextNode: TextNode,
102
+ };
modules/tokenize_latex/third_party/katex/src/parseData.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * The resulting parse tree nodes of the parse tree.
3
+ */
4
+ function ParseNode(type, value, mode) {
5
+ this.type = type;
6
+ this.value = value;
7
+ this.mode = mode;
8
+ }
9
+
10
+ module.exports = {
11
+ ParseNode: ParseNode,
12
+ };
13
+
modules/tokenize_latex/third_party/katex/src/parseTree.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Provides a single function for parsing an expression using a Parser
3
+ * TODO(emily): Remove this
4
+ */
5
+
6
+ var Parser = require("./Parser");
7
+
8
+ /**
9
+ * Parses an expression using a Parser, then returns the parsed result.
10
+ */
11
+ var parseTree = function(toParse, settings) {
12
+ var parser = new Parser(toParse, settings);
13
+
14
+ return parser.parse();
15
+ };
16
+
17
+ module.exports = parseTree;
modules/tokenize_latex/third_party/katex/src/symbols.js ADDED
@@ -0,0 +1,687 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file holds a list of all no-argument functions and single-character
3
+ * symbols (like 'a' or ';').
4
+ *
5
+ * For each of the symbols, there are three properties they can have:
6
+ * - font (required): the font to be used for this symbol. Either "main" (the
7
+ normal font), or "ams" (the ams fonts).
8
+ * - group (required): the ParseNode group type the symbol should have (i.e.
9
+ "textord", "mathord", etc).
10
+ See https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
11
+ * - replace: the character that this symbol or function should be
12
+ * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi
13
+ * character in the main font).
14
+ *
15
+ * The outermost map in the table indicates what mode the symbols should be
16
+ * accepted in (e.g. "math" or "text").
17
+ */
18
+
19
+ module.exports = {
20
+ math: {},
21
+ text: {},
22
+ };
23
+
24
+ function defineSymbol(mode, font, group, replace, name) {
25
+ module.exports[mode][name] = {
26
+ font: font,
27
+ group: group,
28
+ replace: replace,
29
+ };
30
+ }
31
+
32
+ // Some abbreviations for commonly used strings.
33
+ // This helps minify the code, and also spotting typos using jshint.
34
+
35
+ // modes:
36
+ var math = "math";
37
+ var text = "text";
38
+
39
+ // fonts:
40
+ var main = "main";
41
+ var ams = "ams";
42
+
43
+ // groups:
44
+ var accent = "accent";
45
+ var bin = "bin";
46
+ var close = "close";
47
+ var inner = "inner";
48
+ var mathord = "mathord";
49
+ var op = "op";
50
+ var open = "open";
51
+ var punct = "punct";
52
+ var rel = "rel";
53
+ var spacing = "spacing";
54
+ var textord = "textord";
55
+
56
+ // Now comes the symbol table
57
+
58
+ // Relation Symbols
59
+ defineSymbol(math, main, rel, "\u2261", "\\equiv");
60
+ defineSymbol(math, main, rel, "\u227a", "\\prec");
61
+ defineSymbol(math, main, rel, "\u227b", "\\succ");
62
+ defineSymbol(math, main, rel, "\u223c", "\\sim");
63
+ defineSymbol(math, main, rel, "\u22a5", "\\perp");
64
+ defineSymbol(math, main, rel, "\u2aaf", "\\preceq");
65
+ defineSymbol(math, main, rel, "\u2ab0", "\\succeq");
66
+ defineSymbol(math, main, rel, "\u2243", "\\simeq");
67
+ defineSymbol(math, main, rel, "\u2223", "\\mid");
68
+ defineSymbol(math, main, rel, "\u226a", "\\ll");
69
+ defineSymbol(math, main, rel, "\u226b", "\\gg");
70
+ defineSymbol(math, main, rel, "\u224d", "\\asymp");
71
+ defineSymbol(math, main, rel, "\u2225", "\\parallel");
72
+ defineSymbol(math, main, rel, "\u22c8", "\\bowtie");
73
+ defineSymbol(math, main, rel, "\u2323", "\\smile");
74
+ defineSymbol(math, main, rel, "\u2291", "\\sqsubseteq");
75
+ defineSymbol(math, main, rel, "\u2292", "\\sqsupseteq");
76
+ defineSymbol(math, main, rel, "\u2250", "\\doteq");
77
+ defineSymbol(math, main, rel, "\u2322", "\\frown");
78
+ defineSymbol(math, main, rel, "\u220b", "\\ni");
79
+ defineSymbol(math, main, rel, "\u221d", "\\propto");
80
+ defineSymbol(math, main, rel, "\u22a2", "\\vdash");
81
+ defineSymbol(math, main, rel, "\u22a3", "\\dashv");
82
+ defineSymbol(math, main, rel, "\u220b", "\\owns");
83
+
84
+ defineSymbol(math, main, rel, "\u220b", "\\widehat");
85
+ defineSymbol(math, main, rel, "\u220b", "\\widetilde");
86
+ defineSymbol(math, main, rel, "\u220b", "\\sp");
87
+ defineSymbol(math, main, rel, "\u220b", "\\quad");
88
+ // defineSymbol(math, main, rel, "\u220b", "\\cr");
89
+ defineSymbol(math, main, rel, "\u220b", "\\\\sim");
90
+ defineSymbol(math, main, rel, "\u220b", "\\nonumber");
91
+ defineSymbol(math, main, rel, "\u220b", "\\dots");
92
+ defineSymbol(math, main, rel, "\u220b", "\\cases");
93
+ defineSymbol(math, main, rel, "\u220b", "\\mit");
94
+ defineSymbol(math, main, rel, "\u220b", "\\smallskip");
95
+ defineSymbol(math, main, rel, "\u220b", "\\slash");
96
+ defineSymbol(math, main, rel, "\u220b", "\\d");
97
+ defineSymbol(math, main, rel, "\u220b", "\\c");
98
+ defineSymbol(math, main, rel, "\u220b", "\\b");
99
+ defineSymbol(math, main, rel, "\u220b", "\\M");
100
+ defineSymbol(math, main, rel, "\u220b", "\\S");
101
+ defineSymbol(math, main, rel, "\u220b", "\\(");
102
+ defineSymbol(math, main, rel, "\u220b", "\\)");
103
+ // defineSymbol(math, main, rel, "\u220b", "\\Comp");
104
+ defineSymbol(math, main, rel, "\u220b", "\\thinspace");
105
+ defineSymbol(math, main, rel, "\u220b", "\\hskip");
106
+ defineSymbol(math, main, rel, "\u220b", "\\tt");
107
+ defineSymbol(math, main, rel, "\u220b", "\\not");
108
+ defineSymbol(math, main, rel, "\u220b", "\\boldmathr");
109
+ defineSymbol(math, main, rel, "\u220b", "\\overleftarrow");
110
+ defineSymbol(math, main, rel, "\u220b", "\\overrightarrow");
111
+ defineSymbol(math, main, rel, "\u220b", "\\intf");
112
+ defineSymbol(math, main, rel, "\u220b", "\\sf");
113
+ defineSymbol(math, main, rel, "\u220b", "\\textbf");
114
+ defineSymbol(math, main, rel, "\u220b", "\\L");
115
+ defineSymbol(math, main, rel, "\u220b", "\\pii");
116
+ defineSymbol(math, main, rel, "\u220b", "\\unitlength");
117
+ defineSymbol(math, main, rel, "\u220b", "\\arowtor5linv");
118
+ defineSymbol(math, main, rel, "\u220b", "\\hline");
119
+ defineSymbol(math, main, rel, "\u220b", "\\mathbin");
120
+ defineSymbol(math, main, rel, "\u220b", "\\nc");
121
+ defineSymbol(math, main, rel, "\u220b", "\\underbrace");
122
+ defineSymbol(math, main, rel, "\u220b", "\\o");
123
+ defineSymbol(math, main, rel, "\u220b", "\\a");
124
+ defineSymbol(math, main, rel, "\u220b", "\\b");
125
+ defineSymbol(math, main, rel, "\u220b", "\\c");
126
+ defineSymbol(math, main, rel, "\u220b", "\\d");
127
+ defineSymbol(math, main, rel, "\u220b", "\\e");
128
+ defineSymbol(math, main, rel, "\u220b", "\\f");
129
+ defineSymbol(math, main, rel, "\u220b", "\\g");
130
+ defineSymbol(math, main, rel, "\u220b", "\\h");
131
+ defineSymbol(math, main, rel, "\u220b", "\\i");
132
+ defineSymbol(math, main, rel, "\u220b", "\\j");
133
+ defineSymbol(math, main, rel, "\u220b", "\\k");
134
+ defineSymbol(math, main, rel, "\u220b", "\\l");
135
+ defineSymbol(math, main, rel, "\u220b", "\\m");
136
+ defineSymbol(math, main, rel, "\u220b", "\\n");
137
+ defineSymbol(math, main, rel, "\u220b", "\\o");
138
+ // defineSymbol(math, main, rel, "\u220b", "\\wedgee");
139
+ defineSymbol(math, main, rel, "\u220b", "\\sb");
140
+ defineSymbol(math, main, rel, "\u220b", "\\do");
141
+ defineSymbol(math, main, rel, "\u220b", "\\em");
142
+ // defineSymbol(math, main, rel, "\u220b", "\\diamonda");
143
+
144
+
145
+ defineSymbol(math, main, rel, "\u220b", "\\dint");
146
+ defineSymbol(math, main, rel, "\u220b", "\\intd");
147
+
148
+
149
+ // Punctuation
150
+ defineSymbol(math, main, punct, "\u002e", "\\ldotp");
151
+ defineSymbol(math, main, punct, "\u22c5", "\\cdotp");
152
+
153
+ // Misc Symbols
154
+ defineSymbol(math, main, textord, "\u0023", "\\#");
155
+ defineSymbol(math, main, textord, "\u0026", "\\&");
156
+ defineSymbol(math, main, textord, "\u2135", "\\aleph");
157
+ defineSymbol(math, main, textord, "\u2200", "\\forall");
158
+ defineSymbol(math, main, textord, "\u210f", "\\hbar");
159
+ defineSymbol(math, main, textord, "\u2203", "\\eixsts");
160
+ defineSymbol(math, main, textord, "\u2207", "\\nabla");
161
+ defineSymbol(math, main, textord, "\u266d", "\\flat");
162
+ defineSymbol(math, main, textord, "\u2113", "\\ell");
163
+ defineSymbol(math, main, textord, "\u266e", "\\natural");
164
+ defineSymbol(math, main, textord, "\u2663", "\\clubsuit");
165
+ defineSymbol(math, main, textord, "\u2118", "\\wp");
166
+ defineSymbol(math, main, textord, "\u266f", "\\sharp");
167
+ defineSymbol(math, main, textord, "\u2662", "\\diamondsuit");
168
+ defineSymbol(math, main, textord, "\u211c", "\\Re");
169
+ defineSymbol(math, main, textord, "\u2661", "\\heartsuit");
170
+ defineSymbol(math, main, textord, "\u2111", "\\Im");
171
+ defineSymbol(math, main, textord, "\u2660", "\\spadesuit");
172
+
173
+ // Math and Text
174
+ defineSymbol(math, main, textord, "\u2020", "\\dag");
175
+ defineSymbol(math, main, textord, "\u2021", "\\ddag");
176
+
177
+ // Large Delimiters
178
+ defineSymbol(math, main, close, "\u23b1", "\\rmoustache");
179
+ defineSymbol(math, main, open, "\u23b0", "\\lmoustache");
180
+ defineSymbol(math, main, close, "\u27ef", "\\rgroup");
181
+ defineSymbol(math, main, open, "\u27ee", "\\lgroup");
182
+
183
+ // Binary Operators
184
+ defineSymbol(math, main, bin, "\u2213", "\\mp");
185
+ defineSymbol(math, main, bin, "\u2296", "\\ominus");
186
+ defineSymbol(math, main, bin, "\u228e", "\\uplus");
187
+ defineSymbol(math, main, bin, "\u2293", "\\sqcap");
188
+ defineSymbol(math, main, bin, "\u2217", "\\ast");
189
+ defineSymbol(math, main, bin, "\u2294", "\\sqcup");
190
+ defineSymbol(math, main, bin, "\u25ef", "\\bigcirc");
191
+ defineSymbol(math, main, bin, "\u2219", "\\bullet");
192
+ defineSymbol(math, main, bin, "\u2021", "\\ddagger");
193
+ defineSymbol(math, main, bin, "\u2240", "\\wr");
194
+ defineSymbol(math, main, bin, "\u2a3f", "\\amalg");
195
+
196
+ // Arrow Symbols
197
+ defineSymbol(math, main, rel, "\u27f5", "\\longleftarrow");
198
+ defineSymbol(math, main, rel, "\u21d0", "\\Leftarrow");
199
+ defineSymbol(math, main, rel, "\u27f8", "\\Longleftarrow");
200
+ defineSymbol(math, main, rel, "\u27f6", "\\longrightarrow");
201
+ defineSymbol(math, main, rel, "\u21d2", "\\Rightarrow");
202
+ defineSymbol(math, main, rel, "\u27f9", "\\Longrightarrow");
203
+ defineSymbol(math, main, rel, "\u2194", "\\leftrightarrow");
204
+ defineSymbol(math, main, rel, "\u27f7", "\\longleftrightarrow");
205
+ defineSymbol(math, main, rel, "\u21d4", "\\Leftrightarrow");
206
+ defineSymbol(math, main, rel, "\u27fa", "\\Longleftrightarrow");
207
+ defineSymbol(math, main, rel, "\u21a6", "\\mapsto");
208
+ defineSymbol(math, main, rel, "\u27fc", "\\longmapsto");
209
+ defineSymbol(math, main, rel, "\u2197", "\\nearrow");
210
+ defineSymbol(math, main, rel, "\u21a9", "\\hookleftarrow");
211
+ defineSymbol(math, main, rel, "\u21aa", "\\hookrightarrow");
212
+ defineSymbol(math, main, rel, "\u2198", "\\searrow");
213
+ defineSymbol(math, main, rel, "\u21bc", "\\leftharpoonup");
214
+ defineSymbol(math, main, rel, "\u21c0", "\\rightharpoonup");
215
+ defineSymbol(math, main, rel, "\u2199", "\\swarrow");
216
+ defineSymbol(math, main, rel, "\u21bd", "\\leftharpoondown");
217
+ defineSymbol(math, main, rel, "\u21c1", "\\rightharpoondown");
218
+ defineSymbol(math, main, rel, "\u2196", "\\nwarrow");
219
+ defineSymbol(math, main, rel, "\u21cc", "\\rightleftharpoons");
220
+
221
+ // AMS Negated Binary Relations
222
+ defineSymbol(math, ams, rel, "\u226e", "\\nless");
223
+ defineSymbol(math, ams, rel, "\ue010", "\\nleqslant");
224
+ defineSymbol(math, ams, rel, "\ue011", "\\nleqq");
225
+ defineSymbol(math, ams, rel, "\u2a87", "\\lneq");
226
+ defineSymbol(math, ams, rel, "\u2268", "\\lneqq");
227
+ defineSymbol(math, ams, rel, "\ue00c", "\\lvertneqq");
228
+ defineSymbol(math, ams, rel, "\u22e6", "\\lnsim");
229
+ defineSymbol(math, ams, rel, "\u2a89", "\\lnapprox");
230
+ defineSymbol(math, ams, rel, "\u2280", "\\nprec");
231
+ defineSymbol(math, ams, rel, "\u22e0", "\\npreceq");
232
+ defineSymbol(math, ams, rel, "\u22e8", "\\precnsim");
233
+ defineSymbol(math, ams, rel, "\u2ab9", "\\precnapprox");
234
+ defineSymbol(math, ams, rel, "\u2241", "\\nsim");
235
+ defineSymbol(math, ams, rel, "\ue006", "\\nshortmid");
236
+ defineSymbol(math, ams, rel, "\u2224", "\\nmid");
237
+ defineSymbol(math, ams, rel, "\u22ac", "\\nvdash");
238
+ defineSymbol(math, ams, rel, "\u22ad", "\\nvDash");
239
+ defineSymbol(math, ams, rel, "\u22ea", "\\ntriangleleft");
240
+ defineSymbol(math, ams, rel, "\u22ec", "\\ntrianglelefteq");
241
+ defineSymbol(math, ams, rel, "\u228a", "\\subsetneq");
242
+ defineSymbol(math, ams, rel, "\ue01a", "\\varsubsetneq");
243
+ defineSymbol(math, ams, rel, "\u2acb", "\\subsetneqq");
244
+ defineSymbol(math, ams, rel, "\ue017", "\\varsubsetneqq");
245
+ defineSymbol(math, ams, rel, "\u226f", "\\ngtr");
246
+ defineSymbol(math, ams, rel, "\ue00f", "\\ngeqslant");
247
+ defineSymbol(math, ams, rel, "\ue00e", "\\ngeqq");
248
+ defineSymbol(math, ams, rel, "\u2a88", "\\gneq");
249
+ defineSymbol(math, ams, rel, "\u2269", "\\gneqq");
250
+ defineSymbol(math, ams, rel, "\ue00d", "\\gvertneqq");
251
+ defineSymbol(math, ams, rel, "\u22e7", "\\gnsim");
252
+ defineSymbol(math, ams, rel, "\u2a8a", "\\gnapprox");
253
+ defineSymbol(math, ams, rel, "\u2281", "\\nsucc");
254
+ defineSymbol(math, ams, rel, "\u22e1", "\\nsucceq");
255
+ defineSymbol(math, ams, rel, "\u22e9", "\\succnsim");
256
+ defineSymbol(math, ams, rel, "\u2aba", "\\succnapprox");
257
+ defineSymbol(math, ams, rel, "\u2246", "\\ncong");
258
+ defineSymbol(math, ams, rel, "\ue007", "\\nshortparallel");
259
+ defineSymbol(math, ams, rel, "\u2226", "\\nparallel");
260
+ defineSymbol(math, ams, rel, "\u22af", "\\nVDash");
261
+ defineSymbol(math, ams, rel, "\u22eb", "\\ntriangleright");
262
+ defineSymbol(math, ams, rel, "\u22ed", "\\ntrianglerighteq");
263
+ defineSymbol(math, ams, rel, "\ue018", "\\nsupseteqq");
264
+ defineSymbol(math, ams, rel, "\u228b", "\\supsetneq");
265
+ defineSymbol(math, ams, rel, "\ue01b", "\\varsupsetneq");
266
+ defineSymbol(math, ams, rel, "\u2acc", "\\supsetneqq");
267
+ defineSymbol(math, ams, rel, "\ue019", "\\varsupsetneqq");
268
+ defineSymbol(math, ams, rel, "\u22ae", "\\nVdash");
269
+ defineSymbol(math, ams, rel, "\u2ab5", "\\precneqq");
270
+ defineSymbol(math, ams, rel, "\u2ab6", "\\succneqq");
271
+ defineSymbol(math, ams, rel, "\ue016", "\\nsubseteqq");
272
+ defineSymbol(math, ams, bin, "\u22b4", "\\unlhd");
273
+ defineSymbol(math, ams, bin, "\u22b5", "\\unrhd");
274
+
275
+ // AMS Negated Arrows
276
+ defineSymbol(math, ams, rel, "\u219a", "\\nleftarrow");
277
+ defineSymbol(math, ams, rel, "\u219b", "\\nrightarrow");
278
+ defineSymbol(math, ams, rel, "\u21cd", "\\nLeftarrow");
279
+ defineSymbol(math, ams, rel, "\u21cf", "\\nRightarrow");
280
+ defineSymbol(math, ams, rel, "\u21ae", "\\nleftrightarrow");
281
+ defineSymbol(math, ams, rel, "\u21ce", "\\nLeftrightarrow");
282
+
283
+ // AMS Misc
284
+ defineSymbol(math, ams, rel, "\u25b3", "\\vartriangle");
285
+ defineSymbol(math, ams, textord, "\u210f", "\\hslash");
286
+ defineSymbol(math, ams, textord, "\u25bd", "\\triangledown");
287
+ defineSymbol(math, ams, textord, "\u25ca", "\\lozenge");
288
+ defineSymbol(math, ams, textord, "\u24c8", "\\circledS");
289
+ defineSymbol(math, ams, textord, "\u00ae", "\\circledR");
290
+ defineSymbol(math, ams, textord, "\u2221", "\\measuredangle");
291
+ defineSymbol(math, ams, textord, "\u2204", "\\nexists");
292
+ defineSymbol(math, ams, textord, "\u2127", "\\mho");
293
+ defineSymbol(math, ams, textord, "\u2132", "\\Finv");
294
+ defineSymbol(math, ams, textord, "\u2141", "\\Game");
295
+ defineSymbol(math, ams, textord, "\u006b", "\\Bbbk");
296
+ defineSymbol(math, ams, textord, "\u2035", "\\backprime");
297
+ defineSymbol(math, ams, textord, "\u25b2", "\\blacktriangle");
298
+ defineSymbol(math, ams, textord, "\u25bc", "\\blacktriangledown");
299
+ defineSymbol(math, ams, textord, "\u25a0", "\\blacksquare");
300
+ defineSymbol(math, ams, textord, "\u29eb", "\\blacklozenge");
301
+ defineSymbol(math, ams, textord, "\u2605", "\\bigstar");
302
+ defineSymbol(math, ams, textord, "\u2222", "\\sphericalangle");
303
+ defineSymbol(math, ams, textord, "\u2201", "\\complement");
304
+ defineSymbol(math, ams, textord, "\u00f0", "\\eth");
305
+ defineSymbol(math, ams, textord, "\u2571", "\\diagup");
306
+ defineSymbol(math, ams, textord, "\u2572", "\\diagdown");
307
+ defineSymbol(math, ams, textord, "\u25a1", "\\square");
308
+ defineSymbol(math, ams, textord, "\u25a1", "\\Box");
309
+ defineSymbol(math, ams, textord, "\u25ca", "\\Diamond");
310
+ defineSymbol(math, ams, textord, "\u00a5", "\\yen");
311
+ defineSymbol(math, ams, textord, "\u2713", "\\checkmark");
312
+
313
+ // AMS Hebrew
314
+ defineSymbol(math, ams, textord, "\u2136", "\\beth");
315
+ defineSymbol(math, ams, textord, "\u2138", "\\daleth");
316
+ defineSymbol(math, ams, textord, "\u2137", "\\gimel");
317
+
318
+ // AMS Greek
319
+ defineSymbol(math, ams, textord, "\u03dd", "\\digamma");
320
+ defineSymbol(math, ams, textord, "\u03f0", "\\varkappa");
321
+
322
+ // AMS Delimiters
323
+ defineSymbol(math, ams, open, "\u250c", "\\ulcorner");
324
+ defineSymbol(math, ams, close, "\u2510", "\\urcorner");
325
+ defineSymbol(math, ams, open, "\u2514", "\\llcorner");
326
+ defineSymbol(math, ams, close, "\u2518", "\\lrcorner");
327
+
328
+ // AMS Binary Relations
329
+ defineSymbol(math, ams, rel, "\u2266", "\\leqq");
330
+ defineSymbol(math, ams, rel, "\u2a7d", "\\leqslant");
331
+ defineSymbol(math, ams, rel, "\u2a95", "\\eqslantless");
332
+ defineSymbol(math, ams, rel, "\u2272", "\\lesssim");
333
+ defineSymbol(math, ams, rel, "\u2a85", "\\lessapprox");
334
+ defineSymbol(math, ams, rel, "\u224a", "\\approxeq");
335
+ defineSymbol(math, ams, bin, "\u22d6", "\\lessdot");
336
+ defineSymbol(math, ams, rel, "\u22d8", "\\lll");
337
+ defineSymbol(math, ams, rel, "\u2276", "\\lessgtr");
338
+ defineSymbol(math, ams, rel, "\u22da", "\\lesseqgtr");
339
+ defineSymbol(math, ams, rel, "\u2a8b", "\\lesseqqgtr");
340
+ defineSymbol(math, ams, rel, "\u2251", "\\doteqdot");
341
+ defineSymbol(math, ams, rel, "\u2253", "\\risingdotseq");
342
+ defineSymbol(math, ams, rel, "\u2252", "\\fallingdotseq");
343
+ defineSymbol(math, ams, rel, "\u223d", "\\backsim");
344
+ defineSymbol(math, ams, rel, "\u22cd", "\\backsimeq");
345
+ defineSymbol(math, ams, rel, "\u2ac5", "\\subseteqq");
346
+ defineSymbol(math, ams, rel, "\u22d0", "\\Subset");
347
+ defineSymbol(math, ams, rel, "\u228f", "\\sqsubset");
348
+ defineSymbol(math, ams, rel, "\u227c", "\\preccurlyeq");
349
+ defineSymbol(math, ams, rel, "\u22de", "\\curlyeqprec");
350
+ defineSymbol(math, ams, rel, "\u227e", "\\precsim");
351
+ defineSymbol(math, ams, rel, "\u2ab7", "\\precapprox");
352
+ defineSymbol(math, ams, rel, "\u22b2", "\\vartriangleleft");
353
+ defineSymbol(math, ams, rel, "\u22b4", "\\trianglelefteq");
354
+ defineSymbol(math, ams, rel, "\u22a8", "\\vDash");
355
+ defineSymbol(math, ams, rel, "\u22aa", "\\Vvdash");
356
+ defineSymbol(math, ams, rel, "\u2323", "\\smallsmile");
357
+ defineSymbol(math, ams, rel, "\u2322", "\\smallfrown");
358
+ defineSymbol(math, ams, rel, "\u224f", "\\bumpeq");
359
+ defineSymbol(math, ams, rel, "\u224e", "\\Bumpeq");
360
+ defineSymbol(math, ams, rel, "\u2267", "\\geqq");
361
+ defineSymbol(math, ams, rel, "\u2a7e", "\\geqslant");
362
+ defineSymbol(math, ams, rel, "\u2a96", "\\eqslantgtr");
363
+ defineSymbol(math, ams, rel, "\u2273", "\\gtrsim");
364
+ defineSymbol(math, ams, rel, "\u2a86", "\\gtrapprox");
365
+ defineSymbol(math, ams, bin, "\u22d7", "\\gtrdot");
366
+ defineSymbol(math, ams, rel, "\u22d9", "\\ggg");
367
+ defineSymbol(math, ams, rel, "\u2277", "\\gtrless");
368
+ defineSymbol(math, ams, rel, "\u22db", "\\gtreqless");
369
+ defineSymbol(math, ams, rel, "\u2a8c", "\\gtreqqless");
370
+ defineSymbol(math, ams, rel, "\u2256", "\\eqcirc");
371
+ defineSymbol(math, ams, rel, "\u2257", "\\circeq");
372
+ defineSymbol(math, ams, rel, "\u225c", "\\triangleq");
373
+ defineSymbol(math, ams, rel, "\u223c", "\\thicksim");
374
+ defineSymbol(math, ams, rel, "\u2248", "\\thickapprox");
375
+ defineSymbol(math, ams, rel, "\u2ac6", "\\supseteqq");
376
+ defineSymbol(math, ams, rel, "\u22d1", "\\Supset");
377
+ defineSymbol(math, ams, rel, "\u2290", "\\sqsupset");
378
+ defineSymbol(math, ams, rel, "\u227d", "\\succcurlyeq");
379
+ defineSymbol(math, ams, rel, "\u22df", "\\curlyeqsucc");
380
+ defineSymbol(math, ams, rel, "\u227f", "\\succsim");
381
+ defineSymbol(math, ams, rel, "\u2ab8", "\\succapprox");
382
+ defineSymbol(math, ams, rel, "\u22b3", "\\vartriangleright");
383
+ defineSymbol(math, ams, rel, "\u22b5", "\\trianglerighteq");
384
+ defineSymbol(math, ams, rel, "\u22a9", "\\Vdash");
385
+ defineSymbol(math, ams, rel, "\u2223", "\\shortmid");
386
+ defineSymbol(math, ams, rel, "\u2225", "\\shortparallel");
387
+ defineSymbol(math, ams, rel, "\u226c", "\\between");
388
+ defineSymbol(math, ams, rel, "\u22d4", "\\pitchfork");
389
+ defineSymbol(math, ams, rel, "\u221d", "\\varpropto");
390
+ defineSymbol(math, ams, rel, "\u25c0", "\\blacktriangleleft");
391
+ defineSymbol(math, ams, rel, "\u2234", "\\therefore");
392
+ defineSymbol(math, ams, rel, "\u220d", "\\backepsilon");
393
+ defineSymbol(math, ams, rel, "\u25b6", "\\blacktriangleright");
394
+ defineSymbol(math, ams, rel, "\u2235", "\\because");
395
+ defineSymbol(math, ams, rel, "\u22d8", "\\llless");
396
+ defineSymbol(math, ams, rel, "\u22d9", "\\gggtr");
397
+ defineSymbol(math, ams, bin, "\u22b2", "\\lhd");
398
+ defineSymbol(math, ams, bin, "\u22b3", "\\rhd");
399
+ defineSymbol(math, ams, rel, "\u2242", "\\eqsim");
400
+ defineSymbol(math, main, rel, "\u22c8", "\\Join");
401
+ defineSymbol(math, ams, rel, "\u2251", "\\Doteq");
402
+
403
+ // AMS Binary Operators
404
+ defineSymbol(math, ams, bin, "\u2214", "\\dotplus");
405
+ defineSymbol(math, ams, bin, "\u2216", "\\smallsetminus");
406
+ defineSymbol(math, ams, bin, "\u22d2", "\\Cap");
407
+ defineSymbol(math, ams, bin, "\u22d3", "\\Cup");
408
+ defineSymbol(math, ams, bin, "\u2a5e", "\\doublebarwedge");
409
+ defineSymbol(math, ams, bin, "\u229f", "\\boxminus");
410
+ defineSymbol(math, ams, bin, "\u229e", "\\boxplus");
411
+ defineSymbol(math, ams, bin, "\u22c7", "\\divideontimes");
412
+ defineSymbol(math, ams, bin, "\u22c9", "\\ltimes");
413
+ defineSymbol(math, ams, bin, "\u22ca", "\\rtimes");
414
+ defineSymbol(math, ams, bin, "\u22cb", "\\leftthreetimes");
415
+ defineSymbol(math, ams, bin, "\u22cc", "\\rightthreetimes");
416
+ defineSymbol(math, ams, bin, "\u22cf", "\\curlywedge");
417
+ defineSymbol(math, ams, bin, "\u22ce", "\\curlyvee");
418
+ defineSymbol(math, ams, bin, "\u229d", "\\circleddash");
419
+ defineSymbol(math, ams, bin, "\u229b", "\\circledast");
420
+ defineSymbol(math, ams, bin, "\u22c5", "\\centerdot");
421
+ defineSymbol(math, ams, bin, "\u22ba", "\\intercal");
422
+ defineSymbol(math, ams, bin, "\u22d2", "\\doublecap");
423
+ defineSymbol(math, ams, bin, "\u22d3", "\\doublecup");
424
+ defineSymbol(math, ams, bin, "\u22a0", "\\boxtimes");
425
+
426
+ // AMS Arrows
427
+ defineSymbol(math, ams, rel, "\u21e2", "\\dashrightarrow");
428
+ defineSymbol(math, ams, rel, "\u21e0", "\\dashleftarrow");
429
+ defineSymbol(math, ams, rel, "\u21c7", "\\leftleftarrows");
430
+ defineSymbol(math, ams, rel, "\u21c6", "\\leftrightarrows");
431
+ defineSymbol(math, ams, rel, "\u21da", "\\Lleftarrow");
432
+ defineSymbol(math, ams, rel, "\u219e", "\\twoheadleftarrow");
433
+ defineSymbol(math, ams, rel, "\u21a2", "\\leftarrowtail");
434
+ defineSymbol(math, ams, rel, "\u21ab", "\\looparrowleft");
435
+ defineSymbol(math, ams, rel, "\u21cb", "\\leftrightharpoons");
436
+ defineSymbol(math, ams, rel, "\u21b6", "\\curvearrowleft");
437
+ defineSymbol(math, ams, rel, "\u21ba", "\\circlearrowleft");
438
+ defineSymbol(math, ams, rel, "\u21b0", "\\Lsh");
439
+ defineSymbol(math, ams, rel, "\u21c8", "\\upuparrows");
440
+ defineSymbol(math, ams, rel, "\u21bf", "\\upharpoonleft");
441
+ defineSymbol(math, ams, rel, "\u21c3", "\\downharpoonleft");
442
+ defineSymbol(math, ams, rel, "\u22b8", "\\multimap");
443
+ defineSymbol(math, ams, rel, "\u21ad", "\\leftrightsquigarrow");
444
+ defineSymbol(math, ams, rel, "\u21c9", "\\rightrightarrows");
445
+ defineSymbol(math, ams, rel, "\u21c4", "\\rightleftarrows");
446
+ defineSymbol(math, ams, rel, "\u21a0", "\\twoheadrightarrow");
447
+ defineSymbol(math, ams, rel, "\u21a3", "\\rightarrowtail");
448
+ defineSymbol(math, ams, rel, "\u21ac", "\\looparrowright");
449
+ defineSymbol(math, ams, rel, "\u21b7", "\\curvearrowright");
450
+ defineSymbol(math, ams, rel, "\u21bb", "\\circlearrowright");
451
+ defineSymbol(math, ams, rel, "\u21b1", "\\Rsh");
452
+ defineSymbol(math, ams, rel, "\u21ca", "\\downdownarrows");
453
+ defineSymbol(math, ams, rel, "\u21be", "\\upharpoonright");
454
+ defineSymbol(math, ams, rel, "\u21c2", "\\downharpoonright");
455
+ defineSymbol(math, ams, rel, "\u21dd", "\\rightsquigarrow");
456
+ defineSymbol(math, ams, rel, "\u21dd", "\\leadsto");
457
+ defineSymbol(math, ams, rel, "\u21db", "\\Rrightarrow");
458
+ defineSymbol(math, ams, rel, "\u21be", "\\restriction");
459
+
460
+ defineSymbol(math, main, textord, "\u2018", "`");
461
+ defineSymbol(math, main, textord, "$", "\\$");
462
+ defineSymbol(math, main, textord, "%", "\\%");
463
+ defineSymbol(math, main, textord, "_", "\\_");
464
+ defineSymbol(math, main, textord, "\u2220", "\\angle");
465
+ defineSymbol(math, main, textord, "\u221e", "\\infty");
466
+ defineSymbol(math, main, textord, "\u2032", "\\prime");
467
+ defineSymbol(math, main, textord, "\u25b3", "\\triangle");
468
+ defineSymbol(math, main, textord, "\u0393", "\\Gamma");
469
+ defineSymbol(math, main, textord, "\u0394", "\\Delta");
470
+ defineSymbol(math, main, textord, "\u0398", "\\Theta");
471
+ defineSymbol(math, main, textord, "\u039b", "\\Lambda");
472
+ defineSymbol(math, main, textord, "\u039e", "\\Xi");
473
+ defineSymbol(math, main, textord, "\u03a0", "\\Pi");
474
+ defineSymbol(math, main, textord, "\u03a3", "\\Sigma");
475
+ defineSymbol(math, main, textord, "\u03a5", "\\Upsilon");
476
+ defineSymbol(math, main, textord, "\u03a6", "\\Phi");
477
+ defineSymbol(math, main, textord, "\u03a8", "\\Psi");
478
+ defineSymbol(math, main, textord, "\u03a9", "\\Omega");
479
+ defineSymbol(math, main, textord, "\u00ac", "\\neg");
480
+ defineSymbol(math, main, textord, "\u00ac", "\\lnot");
481
+ defineSymbol(math, main, textord, "\u22a4", "\\top");
482
+ defineSymbol(math, main, textord, "\u22a5", "\\bot");
483
+ defineSymbol(math, main, textord, "\u2205", "\\emptyset");
484
+ defineSymbol(math, ams, textord, "\u2205", "\\varnothing");
485
+ defineSymbol(math, main, mathord, "\u03b1", "\\alpha");
486
+ defineSymbol(math, main, mathord, "\u03b2", "\\beta");
487
+ defineSymbol(math, main, mathord, "\u03b3", "\\gamma");
488
+ defineSymbol(math, main, mathord, "\u03b4", "\\delta");
489
+ defineSymbol(math, main, mathord, "\u03f5", "\\epsilon");
490
+ defineSymbol(math, main, mathord, "\u03b6", "\\zeta");
491
+ defineSymbol(math, main, mathord, "\u03b7", "\\eta");
492
+ defineSymbol(math, main, mathord, "\u03b8", "\\theta");
493
+ defineSymbol(math, main, mathord, "\u03b9", "\\iota");
494
+ defineSymbol(math, main, mathord, "\u03ba", "\\kappa");
495
+ defineSymbol(math, main, mathord, "\u03bb", "\\lambda");
496
+ defineSymbol(math, main, mathord, "\u03bc", "\\mu");
497
+ defineSymbol(math, main, mathord, "\u03bd", "\\nu");
498
+ defineSymbol(math, main, mathord, "\u03be", "\\xi");
499
+ defineSymbol(math, main, mathord, "o", "\\omicron");
500
+ defineSymbol(math, main, mathord, "\u03c0", "\\pi");
501
+ defineSymbol(math, main, mathord, "\u03c1", "\\rho");
502
+ defineSymbol(math, main, mathord, "\u03c3", "\\sigma");
503
+ defineSymbol(math, main, mathord, "\u03c4", "\\tau");
504
+ defineSymbol(math, main, mathord, "\u03c5", "\\upsilon");
505
+ defineSymbol(math, main, mathord, "\u03d5", "\\phi");
506
+ defineSymbol(math, main, mathord, "\u03c7", "\\chi");
507
+ defineSymbol(math, main, mathord, "\u03c8", "\\psi");
508
+ defineSymbol(math, main, mathord, "\u03c9", "\\omega");
509
+ defineSymbol(math, main, mathord, "\u03b5", "\\varepsilon");
510
+ defineSymbol(math, main, mathord, "\u03d1", "\\vartheta");
511
+ defineSymbol(math, main, mathord, "\u03d6", "\\varpi");
512
+ defineSymbol(math, main, mathord, "\u03f1", "\\varrho");
513
+ defineSymbol(math, main, mathord, "\u03c2", "\\varsigma");
514
+ defineSymbol(math, main, mathord, "\u03c6", "\\varphi");
515
+ defineSymbol(math, main, bin, "\u2217", "*");
516
+ defineSymbol(math, main, bin, "+", "+");
517
+ defineSymbol(math, main, bin, "\u2212", "-");
518
+ defineSymbol(math, main, bin, "\u22c5", "\\cdot");
519
+ defineSymbol(math, main, bin, "\u2218", "\\circ");
520
+ defineSymbol(math, main, bin, "\u00f7", "\\div");
521
+ defineSymbol(math, main, bin, "\u00b1", "\\pm");
522
+ defineSymbol(math, main, bin, "\u00d7", "\\times");
523
+ defineSymbol(math, main, bin, "\u2229", "\\cap");
524
+ defineSymbol(math, main, bin, "\u222a", "\\cup");
525
+ defineSymbol(math, main, bin, "\u2216", "\\setminus");
526
+ defineSymbol(math, main, bin, "\u2227", "\\land");
527
+ defineSymbol(math, main, bin, "\u2228", "\\lor");
528
+ defineSymbol(math, main, bin, "\u2227", "\\wedge");
529
+ defineSymbol(math, main, bin, "\u2228", "\\vee");
530
+ defineSymbol(math, main, textord, "\u221a", "\\surd");
531
+ defineSymbol(math, main, open, "(", "(");
532
+ defineSymbol(math, main, open, "[", "[");
533
+ defineSymbol(math, main, open, "\u27e8", "\\langle");
534
+ defineSymbol(math, main, open, "\u2223", "\\lvert");
535
+ defineSymbol(math, main, open, "\u2225", "\\lVert");
536
+ defineSymbol(math, main, close, ")", ")");
537
+ defineSymbol(math, main, close, "]", "]");
538
+ defineSymbol(math, main, close, "?", "?");
539
+ defineSymbol(math, main, close, "!", "!");
540
+ defineSymbol(math, main, close, "\u27e9", "\\rangle");
541
+ defineSymbol(math, main, close, "\u2223", "\\rvert");
542
+ defineSymbol(math, main, close, "\u2225", "\\rVert");
543
+ defineSymbol(math, main, rel, "=", "=");
544
+ defineSymbol(math, main, rel, "<", "<");
545
+ defineSymbol(math, main, rel, ">", ">");
546
+ defineSymbol(math, main, rel, ":", ":");
547
+ defineSymbol(math, main, rel, "\u2248", "\\approx");
548
+ defineSymbol(math, main, rel, "\u2245", "\\cong");
549
+ defineSymbol(math, main, rel, "\u2265", "\\ge");
550
+ defineSymbol(math, main, rel, "\u2265", "\\geq");
551
+ defineSymbol(math, main, rel, "\u2190", "\\gets");
552
+ defineSymbol(math, main, rel, ">", "\\gt");
553
+ defineSymbol(math, main, rel, "\u2208", "\\in");
554
+ defineSymbol(math, main, rel, "\u2209", "\\notin");
555
+ defineSymbol(math, main, rel, "\u2282", "\\subset");
556
+ defineSymbol(math, main, rel, "\u2283", "\\supset");
557
+ defineSymbol(math, main, rel, "\u2286", "\\subseteq");
558
+ defineSymbol(math, main, rel, "\u2287", "\\supseteq");
559
+ defineSymbol(math, ams, rel, "\u2288", "\\nsubseteq");
560
+ defineSymbol(math, ams, rel, "\u2289", "\\nsupseteq");
561
+ defineSymbol(math, main, rel, "\u22a8", "\\models");
562
+ defineSymbol(math, main, rel, "\u2190", "\\leftarrow");
563
+ defineSymbol(math, main, rel, "\u2264", "\\le");
564
+ defineSymbol(math, main, rel, "\u2264", "\\leq");
565
+ defineSymbol(math, main, rel, "<", "\\lt");
566
+ defineSymbol(math, main, rel, "\u2260", "\\ne");
567
+ defineSymbol(math, main, rel, "\u2260", "\\neq");
568
+ defineSymbol(math, main, rel, "\u2192", "\\rightarrow");
569
+ defineSymbol(math, main, rel, "\u2192", "\\to");
570
+ defineSymbol(math, ams, rel, "\u2271", "\\ngeq");
571
+ defineSymbol(math, ams, rel, "\u2270", "\\nleq");
572
+ defineSymbol(math, main, spacing, null, "\\!");
573
+ defineSymbol(math, main, spacing, "\u00a0", "\\ ");
574
+ defineSymbol(math, main, spacing, "\u00a0", "~");
575
+ defineSymbol(math, main, spacing, null, "\\,");
576
+ defineSymbol(math, main, spacing, null, "\\:");
577
+ defineSymbol(math, main, spacing, null, "\\;");
578
+ defineSymbol(math, main, spacing, null, "\\enspace");
579
+ defineSymbol(math, main, spacing, null, "\\qquad");
580
+ defineSymbol(math, main, spacing, null, "\\quad");
581
+ defineSymbol(math, main, spacing, "\u00a0", "\\space");
582
+ defineSymbol(math, main, punct, ",", ",");
583
+ defineSymbol(math, main, punct, ";", ";");
584
+ defineSymbol(math, main, punct, ":", "\\colon");
585
+ defineSymbol(math, ams, bin, "\u22bc", "\\barwedge");
586
+ defineSymbol(math, ams, bin, "\u22bb", "\\veebar");
587
+ defineSymbol(math, main, bin, "\u2299", "\\odot");
588
+ defineSymbol(math, main, bin, "\u2295", "\\oplus");
589
+ defineSymbol(math, main, bin, "\u2297", "\\otimes");
590
+ defineSymbol(math, main, textord, "\u2202", "\\partial");
591
+ defineSymbol(math, main, bin, "\u2298", "\\oslash");
592
+ defineSymbol(math, ams, bin, "\u229a", "\\circledcirc");
593
+ defineSymbol(math, ams, bin, "\u22a1", "\\boxdot");
594
+ defineSymbol(math, main, bin, "\u25b3", "\\bigtriangleup");
595
+ defineSymbol(math, main, bin, "\u25bd", "\\bigtriangledown");
596
+ defineSymbol(math, main, bin, "\u2020", "\\dagger");
597
+ defineSymbol(math, main, bin, "\u22c4", "\\diamond");
598
+ defineSymbol(math, main, bin, "\u22c6", "\\star");
599
+ defineSymbol(math, main, bin, "\u25c3", "\\triangleleft");
600
+ defineSymbol(math, main, bin, "\u25b9", "\\triangleright");
601
+ defineSymbol(math, main, open, "{", "\\{");
602
+ defineSymbol(math, main, close, "}", "\\}");
603
+ defineSymbol(math, main, open, "{", "\\lbrace");
604
+ defineSymbol(math, main, close, "}", "\\rbrace");
605
+ defineSymbol(math, main, open, "[", "\\lbrack");
606
+ defineSymbol(math, main, close, "]", "\\rbrack");
607
+ defineSymbol(math, main, open, "\u230a", "\\lfloor");
608
+ defineSymbol(math, main, close, "\u230b", "\\rfloor");
609
+ defineSymbol(math, main, open, "\u2308", "\\lceil");
610
+ defineSymbol(math, main, close, "\u2309", "\\rceil");
611
+ defineSymbol(math, main, textord, "\\", "\\backslash");
612
+ defineSymbol(math, main, textord, "\u2223", "|");
613
+ defineSymbol(math, main, textord, "\u2223", "\\vert");
614
+ defineSymbol(math, main, textord, "\u2225", "\\|");
615
+ defineSymbol(math, main, textord, "\u2225", "\\Vert");
616
+ defineSymbol(math, main, rel, "\u2191", "\\uparrow");
617
+ defineSymbol(math, main, rel, "\u21d1", "\\Uparrow");
618
+ defineSymbol(math, main, rel, "\u2193", "\\downarrow");
619
+ defineSymbol(math, main, rel, "\u21d3", "\\Downarrow");
620
+ defineSymbol(math, main, rel, "\u2195", "\\updownarrow");
621
+ defineSymbol(math, main, rel, "\u21d5", "\\Updownarrow");
622
+ defineSymbol(math, math, op, "\u2210", "\\coprod");
623
+ defineSymbol(math, math, op, "\u22c1", "\\bigvee");
624
+ defineSymbol(math, math, op, "\u22c0", "\\bigwedge");
625
+ defineSymbol(math, math, op, "\u2a04", "\\biguplus");
626
+ defineSymbol(math, math, op, "\u22c2", "\\bigcap");
627
+ defineSymbol(math, math, op, "\u22c3", "\\bigcup");
628
+ defineSymbol(math, math, op, "\u222b", "\\int");
629
+ defineSymbol(math, math, op, "\u222b", "\\intop");
630
+ defineSymbol(math, math, op, "\u222c", "\\iint");
631
+ defineSymbol(math, math, op, "\u222d", "\\iiint");
632
+ defineSymbol(math, math, op, "\u220f", "\\prod");
633
+ defineSymbol(math, math, op, "\u2211", "\\sum");
634
+
635
+ defineSymbol(math, math, op, "\u2a02", "\\bigotimes");
636
+ defineSymbol(math, math, op, "\u2a01", "\\bigoplus");
637
+ defineSymbol(math, math, op, "\u2a00", "\\bigodot");
638
+ defineSymbol(math, math, op, "\u222e", "\\oint");
639
+ defineSymbol(math, math, op, "\u2a06", "\\bigsqcup");
640
+ defineSymbol(math, math, op, "\u222b", "\\smallint");
641
+ defineSymbol(math, main, inner, "\u2026", "\\ldots");
642
+ defineSymbol(math, main, inner, "\u22ef", "\\cdots");
643
+ defineSymbol(math, main, inner, "\u22f1", "\\ddots");
644
+ defineSymbol(math, main, textord, "\u22ee", "\\vdots");
645
+ defineSymbol(math, main, accent, "\u00b4", "\\acute");
646
+ defineSymbol(math, main, accent, "\u0060", "\\grave");
647
+ defineSymbol(math, main, accent, "\u00a8", "\\ddot");
648
+ defineSymbol(math, main, accent, "\u007e", "\\tilde");
649
+ defineSymbol(math, main, accent, "\u00af", "\\bar");
650
+ defineSymbol(math, main, accent, "\u02d8", "\\breve");
651
+ defineSymbol(math, main, accent, "\u02c7", "\\check");
652
+ defineSymbol(math, main, accent, "\u005e", "\\hat");
653
+ defineSymbol(math, main, accent, "\u20d7", "\\vec");
654
+ defineSymbol(math, main, accent, "\u02d9", "\\dot");
655
+ defineSymbol(math, main, mathord, "\u0131", "\\imath");
656
+ defineSymbol(math, main, mathord, "\u0237", "\\jmath");
657
+
658
+
659
+ defineSymbol(text, main, spacing, "\u00a0", "\\ ");
660
+ defineSymbol(text, main, spacing, "\u00a0", " ");
661
+ defineSymbol(text, main, spacing, "\u00a0", "~");
662
+
663
+ // There are lots of symbols which are the same, so we add them in afterwards.
664
+ var i;
665
+ var ch;
666
+
667
+ // All of these are textords in math mode
668
+ var mathTextSymbols = "0123456789/@.\"";
669
+ for (i = 0; i < mathTextSymbols.length; i++) {
670
+ ch = mathTextSymbols.charAt(i);
671
+ defineSymbol(math, main, textord, ch, ch);
672
+ }
673
+
674
+ // All of these are textords in text mode
675
+ var textSymbols = "0123456789`!@*()-=+[]'\";:?/.,";
676
+ for (i = 0; i < textSymbols.length; i++) {
677
+ ch = textSymbols.charAt(i);
678
+ defineSymbol(text, main, textord, ch, ch);
679
+ }
680
+
681
+ // All of these are textords in text mode, and mathords in math mode
682
+ var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
683
+ for (i = 0; i < letters.length; i++) {
684
+ ch = letters.charAt(i);
685
+ defineSymbol(math, main, mathord, ch, ch);
686
+ defineSymbol(text, main, textord, ch, ch);
687
+ }
modules/tokenize_latex/third_party/katex/src/utils.js ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file contains a list of utility functions which are useful in other
3
+ * files.
4
+ */
5
+
6
+ /**
7
+ * Provide an `indexOf` function which works in IE8, but defers to native if
8
+ * possible.
9
+ */
10
+ var nativeIndexOf = Array.prototype.indexOf;
11
+ var indexOf = function(list, elem) {
12
+ if (list == null) {
13
+ return -1;
14
+ }
15
+ if (nativeIndexOf && list.indexOf === nativeIndexOf) {
16
+ return list.indexOf(elem);
17
+ }
18
+ var i = 0;
19
+ var l = list.length;
20
+ for (; i < l; i++) {
21
+ if (list[i] === elem) {
22
+ return i;
23
+ }
24
+ }
25
+ return -1;
26
+ };
27
+
28
+ /**
29
+ * Return whether an element is contained in a list
30
+ */
31
+ var contains = function(list, elem) {
32
+ return indexOf(list, elem) !== -1;
33
+ };
34
+
35
+ /**
36
+ * Provide a default value if a setting is undefined
37
+ */
38
+ var deflt = function(setting, defaultIfUndefined) {
39
+ return setting === undefined ? defaultIfUndefined : setting;
40
+ };
41
+
42
+ // hyphenate and escape adapted from Facebook's React under Apache 2 license
43
+
44
+ var uppercase = /([A-Z])/g;
45
+ var hyphenate = function(str) {
46
+ return str.replace(uppercase, "-$1").toLowerCase();
47
+ };
48
+
49
+ var ESCAPE_LOOKUP = {
50
+ "&": "&amp;",
51
+ ">": "&gt;",
52
+ "<": "&lt;",
53
+ "\"": "&quot;",
54
+ "'": "&#x27;",
55
+ };
56
+
57
+ var ESCAPE_REGEX = /[&><"']/g;
58
+
59
+ function escaper(match) {
60
+ return ESCAPE_LOOKUP[match];
61
+ }
62
+
63
+ /**
64
+ * Escapes text to prevent scripting attacks.
65
+ *
66
+ * @param {*} text Text value to escape.
67
+ * @return {string} An escaped string.
68
+ */
69
+ function escape(text) {
70
+ return ("" + text).replace(ESCAPE_REGEX, escaper);
71
+ }
72
+
73
+ /**
74
+ * A function to set the text content of a DOM element in all supported
75
+ * browsers. Note that we don't define this if there is no document.
76
+ */
77
+ var setTextContent;
78
+ if (typeof document !== "undefined") {
79
+ var testNode = document.createElement("span");
80
+ if ("textContent" in testNode) {
81
+ setTextContent = function(node, text) {
82
+ node.textContent = text;
83
+ };
84
+ } else {
85
+ setTextContent = function(node, text) {
86
+ node.innerText = text;
87
+ };
88
+ }
89
+ }
90
+
91
+ /**
92
+ * A function to clear a node.
93
+ */
94
+ function clearNode(node) {
95
+ setTextContent(node, "");
96
+ }
97
+
98
+ module.exports = {
99
+ contains: contains,
100
+ deflt: deflt,
101
+ escape: escape,
102
+ hyphenate: hyphenate,
103
+ indexOf: indexOf,
104
+ setTextContent: setTextContent,
105
+ clearNode: clearNode,
106
+ };
modules/tokenize_latex/third_party/match-at/README.md ADDED
@@ -0,0 +1 @@
 
 
1
+ # match-at [![Build Status](https://travis-ci.org/spicyj/match-at.svg?branch=master)](https://travis-ci.org/spicyj/match-at)
modules/tokenize_latex/third_party/match-at/lib/matchAt.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @flow */
2
+
3
+ "use strict";
4
+
5
+ function getRelocatable(re) {
6
+ // In the future, this could use a WeakMap instead of an expando.
7
+ if (!re.__matchAtRelocatable) {
8
+ // Disjunctions are the lowest-precedence operator, so we can make any
9
+ // pattern match the empty string by appending `|()` to it:
10
+ // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-patterns
11
+ var source = re.source + "|()";
12
+
13
+ // We always make the new regex global.
14
+ var flags = "g" + (re.ignoreCase ? "i" : "") + (re.multiline ? "m" : "") + (re.unicode ? "u" : "")
15
+ // sticky (/.../y) doesn't make sense in conjunction with our relocation
16
+ // logic, so we ignore it here.
17
+ ;
18
+
19
+ re.__matchAtRelocatable = new RegExp(source, flags);
20
+ }
21
+ return re.__matchAtRelocatable;
22
+ }
23
+
24
+ function matchAt(re, str, pos) {
25
+ if (re.global || re.sticky) {
26
+ throw new Error("matchAt(...): Only non-global regexes are supported");
27
+ }
28
+ var reloc = getRelocatable(re);
29
+ reloc.lastIndex = pos;
30
+ var match = reloc.exec(str);
31
+ // Last capturing group is our sentinel that indicates whether the regex
32
+ // matched at the given location.
33
+ if (match[match.length - 1] == null) {
34
+ // Original regex matched.
35
+ match.length = match.length - 1;
36
+ return match;
37
+ } else {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ module.exports = matchAt;
modules/tokenize_latex/third_party/match-at/package.json ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "match-at",
3
+ "version": "0.1.0",
4
+ "description": "Relocatable regular expressions.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/spicyj/match-at"
8
+ },
9
+ "main": "lib/matchAt.js",
10
+ "files": [
11
+ "lib/"
12
+ ],
13
+ "devDependencies": {
14
+ "babel": "^4.7.16",
15
+ "jest-cli": "^0.4.0",
16
+ "react-tools": "^0.13.1"
17
+ },
18
+ "jest": {
19
+ "scriptPreprocessor": "<rootDir>/jestSupport/preprocessor.js",
20
+ "unmockedModulePathPatterns": [
21
+ ""
22
+ ]
23
+ },
24
+ "scripts": {
25
+ "prepublish": "babel -d lib/ src/",
26
+ "test": "jest"
27
+ },
28
+ "gitHead": "4197daff69720734c72ba3321ed68a41c0527fb2",
29
+ "bugs": {
30
+ "url": "https://github.com/spicyj/match-at/issues"
31
+ },
32
+ "homepage": "https://github.com/spicyj/match-at",
33
+ "_id": "match-at@0.1.0",
34
+ "_shasum": "f561e7709ff9a105b85cc62c6b8ee7c15bf24f31",
35
+ "_from": "match-at@",
36
+ "_npmVersion": "2.2.0",
37
+ "_nodeVersion": "0.10.35",
38
+ "_npmUser": {
39
+ "name": "spicyj",
40
+ "email": "ben@benalpert.com"
41
+ },
42
+ "maintainers": [
43
+ {
44
+ "name": "spicyj",
45
+ "email": "ben@benalpert.com"
46
+ }
47
+ ],
48
+ "dist": {
49
+ "shasum": "f561e7709ff9a105b85cc62c6b8ee7c15bf24f31",
50
+ "tarball": "https://registry.npmjs.org/match-at/-/match-at-0.1.0.tgz"
51
+ },
52
+ "directories": {},
53
+ "_resolved": "https://registry.npmjs.org/match-at/-/match-at-0.1.0.tgz"
54
+ }
modules/tokenize_latex/tokenize_latex.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # taken and modified from https://github.com/harvardnlp/im2markup
2
+ # tokenize latex formulas
3
+ import sys
4
+ import os
5
+ import re
6
+ import argparse
7
+ import subprocess
8
+ import shutil
9
+ from threading import Timer
10
+ from datetime import datetime
11
+
12
+
13
+ def run_cmd(cmd, timeout_sec=30):
14
+ proc = subprocess.Popen(cmd, shell=True)
15
+ kill_proc = lambda p: p.kill()
16
+ timer = Timer(timeout_sec, kill_proc, [proc])
17
+ try:
18
+ timer.start()
19
+ stdout,stderr = proc.communicate()
20
+ finally:
21
+ timer.cancel()
22
+
23
+ def tokenize_latex(latex_code, latex_type="", middle_file=""):
24
+ if not latex_code:
25
+ return False, latex_code
26
+ if not latex_type:
27
+ latex_type = "tabular" if "tabular" in latex_code else "formula"
28
+ if not middle_file:
29
+ middle_file = "out-" + datetime.now().strftime('%Y_%m_%d_%H_%M_%S') + ".txt"
30
+ temp_file = middle_file + '.tmp'
31
+
32
+ if latex_type == "formula":
33
+ with open(temp_file, 'w') as f:
34
+ prepre = latex_code
35
+ # replace split, align with aligned
36
+ prepre = re.sub(r'\\begin{(split|align|alignedat|alignat|eqnarray)\*?}(.+?)\\end{\1\*?}', r'\\begin{aligned}\2\\end{aligned}', prepre, flags=re.S)
37
+ prepre = re.sub(r'\\begin{(smallmatrix)\*?}(.+?)\\end{\1\*?}', r'\\begin{matrix}\2\\end{matrix}', prepre, flags=re.S)
38
+ f.write(prepre)
39
+
40
+ cmd = r"cat %s | node %s %s > %s " % (temp_file, os.path.join(os.path.dirname(__file__), 'preprocess_formula.js'), 'normalize', middle_file)
41
+ ret = subprocess.call(cmd, shell=True)
42
+ os.remove(temp_file)
43
+ if ret != 0:
44
+ return False, latex_code
45
+
46
+ operators = '\s?'.join('|'.join(['arccos', 'arcsin', 'arctan', 'arg', 'cos', 'cosh', 'cot', 'coth', 'csc', 'deg', 'det', 'dim', 'exp', 'gcd', 'hom', 'inf',
47
+ 'injlim', 'ker', 'lg', 'lim', 'liminf', 'limsup', 'ln', 'log', 'max', 'min', 'Pr', 'projlim', 'sec', 'sin', 'sinh', 'sup', 'tan', 'tanh']))
48
+ ops = re.compile(r'\\operatorname {(%s)}' % operators)
49
+ with open(middle_file, 'r') as fin:
50
+ for line in fin:
51
+ tokens = line.strip().split()
52
+ tokens_out = []
53
+ for token in tokens:
54
+ tokens_out.append(token)
55
+ post = ' '.join(tokens_out)
56
+ # use \sin instead of \operatorname{sin}
57
+ names = ['\\'+x.replace(' ', '') for x in re.findall(ops, post)]
58
+ post = re.sub(ops, lambda match: str(names.pop(0)), post).replace(r'\\ \end{array}', r'\end{array}')
59
+ os.remove(middle_file)
60
+ return True, post
61
+
62
+ elif latex_type == "tabular":
63
+ latex_code = latex_code.replace("\\\\%", "\\\\ %")
64
+ latex_code = latex_code.replace("\%", "<PERCENTAGE_TOKEN>")
65
+ latex_code = latex_code.split('%')[0]
66
+ latex_code = latex_code.replace("<PERCENTAGE_TOKEN>", "\%")
67
+ if not "\\end{tabular}" in latex_code:
68
+ latex_code += "\\end{tabular}"
69
+ with open(middle_file, 'w') as f:
70
+ f.write(latex_code.replace('\r', ' ').replace('\n', ' '))
71
+ cmd = "perl -pe 's|hskip(.*?)(cm\\|in\\|pt\\|mm\\|em)|hspace{\\1\\2}|g' %s > %s"%(middle_file, temp_file)
72
+ ret = subprocess.call(cmd, shell=True)
73
+ if ret != 0:
74
+ return False, latex_code
75
+ os.remove(middle_file)
76
+ cmd = r"cat %s | node %s %s > %s " % (temp_file, os.path.join(os.path.dirname(__file__), 'preprocess_tabular.js'), 'tokenize', middle_file)
77
+ ret = subprocess.call(cmd, shell=True)
78
+ os.remove(temp_file)
79
+ if ret != 0:
80
+ return False, latex_code
81
+ with open(middle_file, 'r') as fin:
82
+ for line in fin:
83
+ tokens = line.strip().split()
84
+ tokens_out = []
85
+ for token in tokens:
86
+ tokens_out.append(token)
87
+ post = ' '.join(tokens_out)
88
+ os.remove(middle_file)
89
+ return True, post
90
+ else:
91
+ print(f"latex type{latex_type} unrecognized.")
92
+ return False, latex_code
93
+
94
+ if __name__ == '__main__':
95
+ latex_code = open("2.txt", 'r').read().replace('\r', ' ')
96
+ print("=>", latex_code)
97
+ new_code = tokenize_latex(latex_code)
98
+ print("=>", new_code)
modules/visual_matcher.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import numpy as np
3
+ from PIL import Image
4
+ from scipy.spatial.distance import cdist
5
+ from scipy.optimize import linear_sum_assignment
6
+
7
+
8
+ class SimpleAffineTransform:
9
+ """
10
+ simple affine transform, only translation and scale.
11
+ """
12
+ def __init__(self, translation=(0, 0), scale=1.0):
13
+ self.translation = np.array(translation)
14
+ self.scale = scale
15
+
16
+ def estimate(self, src, dst):
17
+ src_center = np.mean(src, axis=0)
18
+ dst_center = np.mean(dst, axis=0)
19
+ self.translation = dst_center - src_center
20
+
21
+ src_dists = np.linalg.norm(src - src_center, axis=1)
22
+ dst_dists = np.linalg.norm(dst - dst_center, axis=1)
23
+ self.scale = np.mean(dst_dists) / (np.mean(src_dists) + 1e-10)
24
+
25
+ def inverse(self):
26
+ inverse_transform = AffineTransform(-self.translation, 1.0/self.scale)
27
+ return inverse_transform
28
+
29
+ def __call__(self, coords):
30
+ return self.scale * (coords - np.mean(coords, axis=0)) + np.mean(coords, axis=0) + self.translation
31
+
32
+ def residuals(self, src, dst):
33
+ return np.sqrt(np.sum((self(src) - dst) ** 2, axis=1))
34
+
35
+
36
+ def norm_coords(x, left, right):
37
+ if x < left:
38
+ return left
39
+ if x > right:
40
+ return right
41
+ return x
42
+
43
+ def norm_same_token(token):
44
+ special_map = {
45
+ "\\cdot": ".",
46
+ "\\mid": "|",
47
+ "\\to": "\\rightarrow",
48
+ "\\top": "T",
49
+ "\\Tilde": "\\tilde",
50
+ "\\cdots": "\\dots",
51
+ "\\prime": "'",
52
+ "\\ast": "*",
53
+ "\\left<": "\\langle",
54
+ "\\right>": "\\rangle"
55
+ }
56
+ if token in special_map.keys():
57
+ token = special_map[token]
58
+ if token.startswith('\\left') or token.startswith('\\right'):
59
+ token = token.replace("\\left", "").replace("\\right", "")
60
+ if token.startswith('\\big') or token.startswith('\\Big'):
61
+ if "\\" in token[4:]:
62
+ token = "\\"+token[4:].split("\\")[-1]
63
+ else:
64
+ token = token[-1]
65
+
66
+ if token in ['\\leq', '\\geq']:
67
+ return token[0:-1]
68
+ if token in ['\\lVert', '\\rVert', '\\Vert']:
69
+ return '\\|'
70
+ if token in ['\\lvert', '\\rvert', '\\vert']:
71
+ return '|'
72
+ if token.endswith("rightarrow"):
73
+ return "\\rightarrow"
74
+ if token.endswith("leftarrow"):
75
+ return "\\leftarrow"
76
+ if token.startswith('\\wide'):
77
+ return token.replace("wide", "")
78
+ if token.startswith('\\var'):
79
+ return token.replace("\\var", "")
80
+ return token
81
+
82
+
83
+ class HungarianMatcher:
84
+ def __init__(
85
+ self,
86
+ cost_token: float = 1,
87
+ cost_position: float = 0.05,
88
+ cost_order: float = 0.15,
89
+ ):
90
+ self.cost_token = cost_token
91
+ self.cost_position = cost_position
92
+ self.cost_order = cost_order
93
+ self.cost = {}
94
+
95
+ def calculate_token_cost_old(self, box_gt, box_pred):
96
+ token_cost = np.ones((len(box_gt), len(box_pred)))
97
+ for i in range(token_cost.shape[0]):
98
+ box1 = box_gt[i]
99
+ for j in range(token_cost.shape[1]):
100
+ box2 = box_pred[j]
101
+ if box1['token'] == box2['token']:
102
+ token_cost[i, j] = 0
103
+ elif norm_same_token(box1['token']) == norm_same_token(box2['token']):
104
+ token_cost[i, j] = 0.05
105
+ return np.array(token_cost)
106
+
107
+ def calculate_token_cost(self, box_gt, box_pred):
108
+ token2id = {}
109
+ for data in box_gt+box_pred:
110
+ if data['token'] not in token2id:
111
+ token2id[data['token']] = len(token2id)
112
+ num_classes = len(token2id)
113
+
114
+ token2id_norm = {}
115
+ for data in box_gt+box_pred:
116
+ if norm_same_token(data['token']) not in token2id_norm:
117
+ token2id_norm[norm_same_token(data['token'])] = len(token2id_norm)
118
+ num_classes_norm = len(token2id_norm)
119
+
120
+ gt_token_array = []
121
+ norm_gt_token_array = []
122
+ for data in box_gt:
123
+ gt_token_array.append(token2id[data['token']])
124
+ norm_gt_token_array.append(token2id_norm[norm_same_token(data['token'])])
125
+
126
+ pred_token_logits = []
127
+ norm_pred_token_logits = []
128
+ for data in box_pred:
129
+ logits = [0] * num_classes
130
+ logits[token2id[data['token']]] = 1
131
+ pred_token_logits.append(logits)
132
+
133
+ logits_norm = [0] * num_classes_norm
134
+ logits_norm[token2id_norm[norm_same_token(data['token'])]] = 1
135
+ norm_pred_token_logits.append(logits_norm)
136
+
137
+ gt_token_array = np.array(gt_token_array)
138
+ pred_token_logits = np.array(pred_token_logits)
139
+
140
+ norm_gt_token_array = np.array(norm_gt_token_array)
141
+ norm_pred_token_logits = np.array(norm_pred_token_logits)
142
+
143
+ token_cost = 1.0 - pred_token_logits[:, gt_token_array]
144
+ norm_token_cost = 1.0 - norm_pred_token_logits[:, norm_gt_token_array]
145
+
146
+ token_cost[np.logical_and(token_cost==1, norm_token_cost==0)] = 0.05
147
+ return token_cost.T
148
+
149
+
150
+ def box2array(self, box_list, size):
151
+ W, H = size
152
+ box_array = []
153
+ for box in box_list:
154
+ x_min, y_min, x_max, y_max = box['bbox']
155
+ box_array.append([x_min/W, y_min/H, x_max/W, y_max/H])
156
+ return np.array(box_array)
157
+
158
+ def order2array(self, box_list):
159
+ order_array = []
160
+ for idx, box in enumerate(box_list):
161
+ order_array.append([idx / len(box_list)])
162
+ return np.array(order_array)
163
+
164
+ def calculate_l1_cost(self, gt_array, pred_array):
165
+ scale = gt_array.shape[-1]
166
+ l1_cost = cdist(gt_array, pred_array, 'minkowski', p=1)
167
+ return l1_cost / scale
168
+
169
+ def __call__(self, box_gt, box_pred, gt_size, pred_size):
170
+ aa = time.time()
171
+ gt_box_array = self.box2array(box_gt, gt_size)
172
+ pred_box_array = self.box2array(box_pred, pred_size)
173
+ gt_order_array = self.order2array(box_gt)
174
+ pred_order_array = self.order2array(box_pred)
175
+
176
+ token_cost = self.calculate_token_cost(box_gt, box_pred)
177
+ position_cost = self.calculate_l1_cost(gt_box_array, pred_box_array)
178
+ order_cost = self.calculate_l1_cost(gt_order_array, pred_order_array)
179
+
180
+ self.cost["token"] = token_cost
181
+ self.cost["position"] = position_cost
182
+ self.cost["order"] = order_cost
183
+
184
+ cost = self.cost_token * token_cost + self.cost_position * position_cost + self.cost_order * order_cost
185
+ cost[np.isnan(cost) | np.isinf(cost)] = 100
186
+ indexes = linear_sum_assignment(cost)
187
+ matched_idxes = []
188
+ for a, b in zip(*indexes):
189
+ matched_idxes.append((a, b))
190
+
191
+ return matched_idxes
requriements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ tqdm
2
+ matplotlib
3
+ gradio==4.16.0
4
+ numpy<2.0.0
5
+ scikit-image<=0.20.0