sskatex 0.9.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3b5feb74566bcc5031cb615e5bacf5e795c6e465
4
+ data.tar.gz: a5163ab70c56af3d2aba6f7670883e5114348922
5
+ SHA512:
6
+ metadata.gz: 7d3e59e7a9d7005779e656528c09714b50d27c5def895baa2dd97560003e22a2342059698359043c548e222dc2d8d23fde368ba97e206de0878798f3a70476fb
7
+ data.tar.gz: baa2384e98b60595061776f234fcc7be036c9ef51f2a586e294e9b3487f39d0d515cc0e7c48679e994558d78f00491ec09c8533956e4c7b386efc07522c9b5e7
data/COPYING ADDED
@@ -0,0 +1,23 @@
1
+ SsKaTeX - Server-side KaTeX for Ruby
2
+ (not including KaTeX itself)
3
+ MIT License
4
+ Copyright (C) 2017 Christian Cornelssen <ccorn@1tein.de>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a
7
+ copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included
15
+ in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # SsKaTeX
2
+ ## Server-side KaTeX for Ruby
3
+
4
+ This is a TeX-to-HTML+MathML+CSS converter class using the Javascript-based
5
+ [KaTeX], interpreted by one of the Javascript engines supported by [ExecJS].
6
+ The intended purpose is to eliminate the need for math-rendering Javascript
7
+ in the client's HTML browser. Therefore the name: SsKaTeX means *server-side*
8
+ KaTeX.
9
+
10
+ Javascript execution context initialization can be done once and then reused
11
+ for formula renderings with the same general configuration. As a result, the
12
+ performance is reasonable. Consider this a fast and lightweight alternative to
13
+ [mathjax-node-cli].
14
+
15
+ Requirements for using SsKaTeX:
16
+
17
+ * Ruby gem [ExecJS],
18
+ * A Javascript engine supported by ExecJS, e.g. via one of
19
+ - Ruby gem [therubyracer],
20
+ - Ruby gem [therubyrhino],
21
+ - Ruby gem [duktape.rb],
22
+ - [Node.js],
23
+ * `katex.min.js` from [KaTeX].
24
+
25
+ Although the converter only needs `katex.min.js`, you may need to serve the
26
+ rest of the KaTeX package, that is, CSS and fonts, as resources to the
27
+ targeted web browsers. The upside is that your HTML templates need no longer
28
+ include Javascripts for Math (neither `katex.js` nor any search-and-replace
29
+ script). Your HTML templates should continue referencing the KaTeX CSS.
30
+ If you host your own copy of the CSS, also keep hosting the fonts.
31
+
32
+ Minimal usage example:
33
+
34
+ tex_to_html = SsKaTeX.new(katex_js: 'path-to-katex/katex.min.js')
35
+ # Here you could verify contents of tex_to_html.js_source for security...
36
+
37
+ body_html = '<p>By Pythagoras, %s. Furthermore:</p>' %
38
+ tex_to_html.call('a^2 + b^2 = c^2', false) # inline math
39
+ body_html << # block display
40
+ tex_to_html.call('\frac{1}{2} + \frac{1}{3} + \frac{1}{6} = 1', true)
41
+ # etc, etc.
42
+
43
+ More configuration options are described in the Rdoc. Most options, with the
44
+ notable exception of `katex_opts`, do not affect usage nor output, but may be
45
+ needed to make SsKaTeX work with all the external parts (JS engine and KaTeX).
46
+ Since KaTeX is distributed separately from the SsKaTeX gem, configuration of
47
+ the latter must support the specification of Javascript file locations. This
48
+ implies that execution of arbitrary Javascript code is possible. Specifically,
49
+ options with `js` in their names should be accepted from trusted sources only.
50
+ Applications using SsKaTeX need to check this.
51
+
52
+ [duktape.rb]: https://github.com/judofyr/duktape.rb#duktaperb
53
+ [ExecJS]: https://github.com/rails/execjs#execjs
54
+ [KaTeX]: https://khan.github.io/KaTeX/
55
+ [mathjax-node-cli]: https://github.com/mathjax/mathjax-node-cli
56
+ [Node.js]: https://nodejs.org/
57
+ [therubyracer]: https://github.com/cowboyd/therubyracer#therubyracer
58
+ [therubyrhino]: https://github.com/cowboyd/therubyrhino#therubyrhino
@@ -0,0 +1,12 @@
1
+ // -*- coding: utf-8 -*-
2
+ //
3
+ // Copyright (C) 2017 Christian Cornelssen <ccorn@1tein.de>
4
+ //
5
+ // This file is part of SsKaTeX which is licensed under the MIT.
6
+
7
+ // Transform non-ASCII characters and '\0' in given string to HTML numeric character references
8
+ function escape_nonascii_html(str) {
9
+ return str.replace(/[^\x01-\x7F]/g, function (u) {
10
+ return "&#x" + u.charCodeAt(0).toString(16) + ";";
11
+ });
12
+ };
@@ -0,0 +1,15 @@
1
+ // -*- coding: utf-8 -*-
2
+ //
3
+ // Copyright (C) 2017 Christian Cornelssen <ccorn@1tein.de>
4
+ //
5
+ // This file is part of SsKaTeX which is licensed under the MIT.
6
+
7
+ // Given a LaTeX math string, a boolean display_mode
8
+ // (true for block display, false for inline),
9
+ // and a dict katex_opts with general KaTeX options,
10
+ // return a string with corresponding HTML+MathML output.
11
+ // The implementation is allowed to set katex_opts.displayMode .
12
+ function tex_to_html(tex, display_mode, katex_opts) {
13
+ katex_opts.displayMode = display_mode;
14
+ return escape_nonascii_html(katex.renderToString(tex, katex_opts));
15
+ };
data/lib/sskatex.rb ADDED
@@ -0,0 +1,354 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2017 Christian Cornelssen <ccorn@1tein.de>
5
+ #
6
+ # This file is part of SsKaTeX which is licensed under the MIT.
7
+ #++
8
+
9
+ require 'json'
10
+
11
+ # This is a TeX-to-HTML+MathML+CSS converter class using the Javascript-based
12
+ # KaTeX[https://khan.github.io/KaTeX/], interpreted by one of the Javascript
13
+ # engines supported by ExecJS[https://github.com/rails/execjs#execjs].
14
+ # The intended purpose is to eliminate the need for math-rendering Javascript
15
+ # in the client's HTML browser. Therefore the name: SsKaTeX means _server-side_
16
+ # KaTeX.
17
+ #
18
+ # Javascript execution context initialization can be done once and then reused
19
+ # for formula renderings with the same general configuration. As a result, the
20
+ # performance is reasonable. Consider this a fast and lightweight alternative to
21
+ # mathjax-node-cli[https://github.com/mathjax/mathjax-node-cli].
22
+ #
23
+ # Requirements for using SsKaTeX:
24
+ #
25
+ # - Ruby gem ExecJS[https://github.com/rails/execjs#execjs],
26
+ # - A Javascript engine supported by ExecJS, e.g. via one of
27
+ # - Ruby gem therubyracer[https://github.com/cowboyd/therubyracer#therubyracer],
28
+ # - Ruby gem therubyrhino[https://github.com/cowboyd/therubyrhino#therubyrhino],
29
+ # - Ruby gem duktape.rb[https://github.com/judofyr/duktape.rb#duktaperb],
30
+ # - Node.js[https://nodejs.org/],
31
+ # - +katex.min.js+ from KaTeX[https://khan.github.io/KaTeX/].
32
+ #
33
+ # Although the converter only needs +katex.min.js+, you may need to serve the
34
+ # rest of the KaTeX package, that is, CSS and fonts, as resources to the
35
+ # targeted web browsers. The upside is that your HTML templates need no longer
36
+ # include Javascripts for Math (neither +katex.js+ nor any search-and-replace
37
+ # script). Your HTML templates should continue referencing the KaTeX CSS.
38
+ # If you host your own copy of the CSS, also keep hosting the fonts.
39
+ #
40
+ # Minimal usage example:
41
+ #
42
+ # tex_to_html = SsKaTeX.new(katex_js: 'path-to-katex/katex.min.js')
43
+ # # Here you could verify contents of tex_to_html.js_source for security...
44
+ #
45
+ # body_html = '<p>By Pythagoras, %s. Furthermore:</p>' %
46
+ # tex_to_html.call('a^2 + b^2 = c^2', false) # inline math
47
+ # body_html << # block display
48
+ # tex_to_html.call('\frac{1}{2} + \frac{1}{3} + \frac{1}{6} = 1', true)
49
+ # # etc, etc.
50
+ #
51
+ # More configuration options are described in the Rdoc. Most options, with the
52
+ # notable exception of #katex_opts, do not affect usage nor output, but may be
53
+ # needed to make SsKaTeX work with all the external parts (JS engine and KaTeX).
54
+ # Since KaTeX is distributed separately from the SsKaTeX gem, configuration of
55
+ # the latter must support the specification of Javascript file locations. This
56
+ # implies that execution of arbitrary Javascript code is possible. Specifically,
57
+ # options with +js+ in their names should be accepted from trusted sources only.
58
+ # Applications using SsKaTeX need to check this.
59
+ class SsKaTeX
60
+
61
+ # Original value of the +EXECJS_RUNTIME+ environment variable, if any.
62
+ # Used when deferring ExecJS's engine auto-selection.
63
+ ENV_EXECJS_RUNTIME = ENV['EXECJS_RUNTIME']
64
+ begin
65
+ ::ENV['EXECJS_RUNTIME'] = 'Disabled' # Defer automatic JS engine selection
66
+ require 'execjs'
67
+ ensure
68
+ ::ENV['EXECJS_RUNTIME'] = ENV_EXECJS_RUNTIME
69
+ end
70
+
71
+ # Root directory for auxiliary files of this gem
72
+ DATADIR = File.expand_path(File.join(File.dirname(__FILE__),
73
+ '..', 'data', 'sskatex'))
74
+
75
+ # The default for the #js_dir configuration option.
76
+ # Path of a directory with Javascript helper files.
77
+ DEFAULT_JS_DIR = File.join(DATADIR, 'js')
78
+
79
+ # The default path to +katex.js+, cf. the #katex_js configuration option.
80
+ # For a relative path, the starting point is the current working directory.
81
+ DEFAULT_KATEX_JS = File.join('katex', 'katex.min.js')
82
+
83
+ # The default for the #js_libs configuration option.
84
+ # A list of UTF-8-encoded Javascript helper files to load.
85
+ # Relative paths are interpreted relative to #js_dir.
86
+ DEFAULT_JS_LIBS = ['escape_nonascii_html.js', 'tex_to_html.js']
87
+
88
+ # This is a module with miscellaneous utility functions needed by SsKaTeX.
89
+ module Utils
90
+ # Dictionary for escape sequences used in Javascript string literals
91
+ JS_ESCAPE = {
92
+ "\\" => "\\\\",
93
+ "\"" => "\\\"",
94
+ # Escaping single quotes not necessary in double-quoted string literals
95
+ #"'" => "\\'",
96
+ # JS does not recognize \a nor GNU's \e
97
+ # \b is ambiguous in regexps, as in Perl and Ruby
98
+ #"\b" => "\\b",
99
+ "\f" => "\\f",
100
+ "\n" => "\\n",
101
+ "\r" => "\\r",
102
+ "\t" => "\\t",
103
+ "\v" => "\\v",
104
+ }
105
+ private_constant :JS_ESCAPE
106
+
107
+ # ExecJS uses <tt>runtime = Runtimes.const_get(name)</tt> without checks.
108
+ # That is fragile and potentially insecure with arbitrary user input.
109
+ # Instead we use a fixed dictionary restricted to valid contents.
110
+ # Note that there are aliases like <tt>SpiderMonkey = Spidermonkey</tt>.
111
+ JSRUN_FROMSYM = {}.tap do |dict|
112
+ ExecJS::Runtimes.constants.each do |name|
113
+ runtime = ExecJS::Runtimes.const_get(name)
114
+ dict[name] = runtime if runtime.is_a?(ExecJS::Runtime)
115
+ end
116
+ end
117
+
118
+ # Subclasses of +ExecJS::Runtime+ provide +.name+ (which is too verbose),
119
+ # but not, say, +.symbol+. This dictionary associates each JS runtime
120
+ # class with a representative symbol. For aliases like <tt>SpiderMonkey =
121
+ # Spidermonkey</tt>, an unspecified choice is made.
122
+ JSRUN_TOSYM = JSRUN_FROMSYM.invert
123
+
124
+ class << self # class-level methods
125
+ # Turn a string into an equivalent Javascript literal, double-quoted.
126
+ # Similar to +.to_json+, but escape all non-ASCII codes as well.
127
+ def js_quote(str)
128
+ # No portable way of escaping Unicode points above 0xFFFF
129
+ '"%s"' % str.encode(Encoding::UTF_8).
130
+ gsub(/[\0-\u{001F}\"\\\u{0080}-\u{FFFF}]/u) do |c|
131
+ JS_ESCAPE[c] || "\\u%04x" % c.ord
132
+ end
133
+ end
134
+
135
+ # This should really be provided by +ExecJS::Runtimes+:
136
+ # A list of available JS engines, as symbols, in the order of preference.
137
+ def js_runtimes
138
+ ExecJS::Runtimes.runtimes.select(&:available?).map(&JSRUN_TOSYM)
139
+ end
140
+
141
+ # Configuration dicts may contain keys in both string and symbol form.
142
+ # This bloats the output of +.to_json+ with duplicate key-value pairs.
143
+ # While this does not affect the result, it looks strange in logfiles.
144
+ # Therefore here is a function that recursively dedups dict keys,
145
+ # removing symbolic keys if there are corresponding string keys.
146
+ # Nondestructive.
147
+ def dedup_keys(conf)
148
+ # Lazy solution would be: JSON.parse(conf.to_json)
149
+ case conf
150
+ when Hash
151
+ conf.reject {|key, _| key.is_a?(Symbol) && conf.has_key?(key.to_s)}.
152
+ tap {|dict| dict.each {|key, value| dict[key] = dedup_keys(value)}}
153
+ when Array
154
+ conf.map {|value| dedup_keys(value)}
155
+ else
156
+ conf
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ # This can be used for monitoring or debugging. When set to a
163
+ # proc {|level, &block| ...}
164
+ # the #logger will be used internally as
165
+ # logger.call(level) {msg}
166
+ # with the log message constructed in the given block. _level_ is one of:
167
+ #
168
+ # +:verbose+::
169
+ # For information about the effective engine configuration.
170
+ # Issued on first use of a changed configuration option.
171
+ # +:debug+::
172
+ # For the Javascript expressions used when converting TeX.
173
+ # Issued once per TeX snippet.
174
+ #
175
+ # For example, to ignore +:debug+ yet trace +:verbose+ messages, set
176
+ # .logger = lambda {|level, &block| warn(block.call) if level == :verbose}
177
+ # The default after construction is to log nothing.
178
+ attr_accessor :logger
179
+
180
+ # A dictionary with the used configuration options.
181
+ # The resulting effective option values can be read from the same-named
182
+ # attributes #katex_js, #katex_opts, #js_dir, #js_libs, #js_run.
183
+ # See also #config=.
184
+ def config
185
+ @config
186
+ end
187
+
188
+ # Reconfigure the conversion engine by passing in a dictionary, without
189
+ # affecting the #logger setting. Changes become effective on first use.
190
+ #
191
+ # Note: The dict will be shared by reference. Its deep object tree should
192
+ # remain unchanged at least until #js_context or #call has been invoked.
193
+ # Thereafter changes do not matter until #config= is assigned again.
194
+ def config=(cfg)
195
+ @js_context = nil
196
+ @js_source = nil
197
+ @katex_opts = nil
198
+ @katex_js = nil
199
+ @js_libs = nil
200
+ @js_dir = nil
201
+ @js_runtime = nil
202
+ @js_run = nil
203
+ @config = cfg
204
+ end
205
+
206
+ # Create a new instance configured with keyword arguments. Disable logging.
207
+ # The keyword arguments can be retrieved as dictionary #config, and the
208
+ # resulting effective option values can be read from the same-named attributes
209
+ # #katex_js, #katex_opts, #js_dir, #js_libs, #js_run.
210
+ def initialize(cfg = {})
211
+ @logger = lambda {|level, &block|}
212
+ self.config = cfg
213
+ end
214
+
215
+ # A symbol for the Javascript engine to be used. Recognized identifiers
216
+ # include: +:RubyRacer+, +:RubyRhino+, +:Duktape+, +:MiniRacer+, +:Node+,
217
+ # +:JavaScriptCore+, +:Spidermonkey+, +:JScript+, +:V8+, and +:Disabled+;
218
+ # that last one would raise an error on first run (by #js_context).
219
+ # Which engines are actually available depends on your installation.
220
+ #
221
+ # #js_run is determined on demand as follows and then cached for reuse.
222
+ # If #config[ +:js_run+ ] is not defined, the contents of the environment
223
+ # variable +EXECJS_RUNTIME+ will be considered instead; and if that is not
224
+ # defined, an automatic choice will be made. For more information, set the
225
+ # #logger to show +:verbose+ messages and consult the documentation of
226
+ # ExecJS[https://github.com/rails/execjs#execjs].
227
+ def js_run
228
+ @js_run ||= begin
229
+ log = lambda {|&block| @logger.call(:verbose, &block)}
230
+
231
+ log.call {"Available JS runtimes: #{Utils.js_runtimes.join(', ')}"}
232
+ jsrun = (@config[:js_run] ||
233
+ ENV_EXECJS_RUNTIME ||
234
+ Utils::JSRUN_TOSYM[ExecJS::Runtimes.best_available] ||
235
+ 'Disabled').to_s.to_sym
236
+ log.call {"Selected JS runtime: #{jsrun}"}
237
+ jsrun
238
+ end
239
+ end
240
+
241
+ # The +ExecJS::Runtime+ subclass to be used, corresponding to #js_run.
242
+ def js_runtime
243
+ @js_runtime ||= Utils::JSRUN_FROMSYM[js_run]
244
+ end
245
+
246
+ # The path to a directory with Javascript helper files as specified by
247
+ # #config[ +:js_dir+ ], or its default which is the subdirectory +js+ in the
248
+ # data directory of SsKaTeX. There is no need to change that setting unless
249
+ # you want to experiment with Javascript details.
250
+ def js_dir
251
+ @js_dir ||= @config[:js_dir] || DEFAULT_JS_DIR
252
+ end
253
+
254
+ # A list of UTF-8-encoded Javascript helper files to load.
255
+ # Can be overridden with #config[ +:js_libs+ ].
256
+ # Relative paths are interpreted relative to #js_dir.
257
+ # The default setting (in YAML notation) is
258
+ #
259
+ # js_libs:
260
+ # - escape_nonascii_html.js
261
+ # - tex_to_html.js
262
+ #
263
+ # And there is no need to change that unless you want to experiment with
264
+ # Javascript details.
265
+ #
266
+ # Files available in the default #js_dir are:
267
+ #
268
+ # +escape_nonascii_html.js+::
269
+ # defines a function +escape_nonascii_html+ that converts non-ASCII
270
+ # characters to HTML numeric character references.
271
+ # Intended as postprocessing filter.
272
+ # +tex_to_html.js+::
273
+ # defines a function +tex_to_html+(_tex_, _display_mode_, _katex_opts_)
274
+ # that takes a LaTeX math string, a boolean display mode (+true+ for block
275
+ # display, +false+ for inline), and a dict with general KaTeX options, and
276
+ # returns a string with corresponding HTML+MathML output. The implementation
277
+ # is allowed to set +katex_opts.displayMode+. SsKaTeX applies +tex_to_html+
278
+ # to each math fragment encountered.
279
+ # The implementation given here uses +katex.renderToString+ and
280
+ # postprocesses the output with +escape_nonascii_html+.
281
+ def js_libs
282
+ @js_libs ||= @config[:js_libs] || DEFAULT_JS_LIBS
283
+ end
284
+
285
+ # The path to your copy of +katex.min.js+ as specified by
286
+ # #config[ +:katex_js+ ] or its default <tt>'katex/katex.min.js'</tt>.
287
+ # For a relative path, the starting point is the current working directory.
288
+ def katex_js
289
+ @katex_js ||= @config[:katex_js] || DEFAULT_KATEX_JS
290
+ end
291
+
292
+ # A dictionary filled with the contents of #config[ +:katex_opts+ ] if given.
293
+ # These are general KaTeX options such as +throwOnError+, +errorColor+,
294
+ # +colorIsTextColor+, and +macros+. See the KaTeX
295
+ # documentation[https://github.com/Khan/KaTeX#rendering-options] for details.
296
+ # Use <tt>throwOnError: false</tt> if you want parse errors highlighted in the
297
+ # HTML output rather than raised as exceptions when compiling.
298
+ # Note that +displayMode+ is computed dynamically and should not be specified
299
+ # here. Keys can be symbols or strings; if a key is given in both forms, the
300
+ # symbol will be ignored.
301
+ def katex_opts
302
+ @katex_opts ||= Utils.dedup_keys(@config[:katex_opts] || {})
303
+ end
304
+
305
+ # The concatenation of the contents of the files in #js_libs, in #katex_js,
306
+ # and a JS variable definition for #katex_opts, each item followed by a
307
+ # newline. Created at first use. Can be used to validate JS contents if used
308
+ # before #js_context.
309
+ def js_source
310
+ @js_source ||= begin
311
+ log = lambda {|&block| @logger.call(:verbose, &block)}
312
+
313
+ # Concatenate sources
314
+ js = ''
315
+ js_libs.each do |libfile|
316
+ absfile = File.expand_path(libfile, js_dir)
317
+ log.call {"Loading JS file: #{absfile}"}
318
+ js << IO.read(absfile, external_encoding: Encoding::UTF_8) << "\n"
319
+ end
320
+ log.call {"Loading KaTeX JS file: #{katex_js}"}
321
+ js << IO.read(katex_js, external_encoding: Encoding::UTF_8) << "\n"
322
+
323
+ # Initialize JS variable katex_opts
324
+ js_katex_opts = "var katex_opts = #{katex_opts.to_json}"
325
+ log.call {"JS eval: #{js_katex_opts}"}
326
+ js << js_katex_opts << "\n"
327
+ end
328
+ end
329
+
330
+ # The JS engine context resulting from compilation of #js_source by the
331
+ # #js_runtime selected with #js_run. Created at first use e.g. by #call.
332
+ def js_context
333
+ @js_context ||= js_runtime.compile(js_source)
334
+ end
335
+
336
+ # Given a TeX math fragment _tex_ as well as a boolean _display_mode_ (true
337
+ # for block, false for inline), run the JS engine (using #js_context) and let
338
+ # KaTeX compile the math fragment. Return the resulting HTML string.
339
+ # Can raise errors if something in the process fails.
340
+ def call(tex, display_mode)
341
+ ctx = js_context
342
+ js = "tex_to_html(#{Utils.js_quote(tex)}, #{display_mode.to_json}, katex_opts)"
343
+ @logger.call(:debug) {"JS eval: #{js}"}
344
+ ans = ctx.eval(js)
345
+ raise (<<MSG) unless ans && ans.start_with?('<') && ans.end_with?('>')
346
+ KaTeX conversion failed!
347
+ Input:
348
+ #{tex}
349
+ Output:
350
+ #{ans}
351
+ MSG
352
+ ans
353
+ end
354
+ end
@@ -0,0 +1,2 @@
1
+ <span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi mathvariant="normal">&#x394;</mi><mo>=</mo><mfrac><mrow><mn>1</mn></mrow><mrow><mn>2</mn></mrow></mfrac><mrow><mo fence="true">&#x2223;</mo><mtable><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>a</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>b</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>c</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>d</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>e</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>f</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>g</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>h</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>i</mi></mrow></mstyle></mtd></mtr></mtable><mo fence="true">&#x2223;</mo></mrow></mrow><annotation encoding="application/x-tex">\Delta = \frac{1}{2}
2
+ \begin{vmatrix}a &amp; b &amp; c \\ d &amp; e &amp; f \\ g &amp; h &amp; i\end{vmatrix}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:2.08597em;"></span><span class="strut bottom" style="height:3.636em;vertical-align:-1.5500299999999998em;"></span><span class="base"><span class="mord mathrm">&#x394;</span><span class="mrel">=</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.32144em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathrm">2</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathrm">1</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.08597em;"><span style="top:-1.05597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-1.6619700000000002em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.26797em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.87397em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-3.47997em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-4.08597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500299999999998em;"></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">a</span></span></span><span style="top:-3.0099999999999993em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">d</span></span></span><span style="top:-1.8099999999999994em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">g</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500000000000007em;"></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">b</span></span></span><span style="top:-3.0099999999999993em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">e</span></span></span><span style="top:-1.8099999999999994em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">h</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500000000000007em;"></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">c</span></span></span><span style="top:-3.0099999999999993em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.10764em;">f</span></span></span><span style="top:-1.8099999999999994em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">i</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500000000000007em;"></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.08597em;"><span style="top:-1.05597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-1.6619700000000002em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.26797em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.87397em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-3.47997em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-4.08597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500299999999998em;"></span></span></span></span></span></span></span></span></span></span>
@@ -0,0 +1 @@
1
+ <span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup><mo>=</mo><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">a^2+b^2=c^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8641079999999999em;"></span><span class="strut bottom" style="height:0.9474379999999999em;vertical-align:-0.08333em;"></span><span class="base"><span class="mord"><span class="mord mathit">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathrm mtight">2</span></span></span></span></span></span></span></span><span class="mbin">+</span><span class="mord"><span class="mord mathit">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathrm mtight">2</span></span></span></span></span></span></span></span><span class="mrel">=</span><span class="mord"><span class="mord mathit">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathrm mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>
@@ -0,0 +1,2 @@
1
+ <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi mathvariant="normal">&#x394;</mi><mo>=</mo><mfrac><mrow><mn>1</mn></mrow><mrow><mn>2</mn></mrow></mfrac><mrow><mo fence="true">&#x2223;</mo><mtable><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>a</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>b</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>c</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>d</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>e</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>f</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>g</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>h</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>i</mi></mrow></mstyle></mtd></mtr></mtable><mo fence="true">&#x2223;</mo></mrow></mrow><annotation encoding="application/x-tex">\Delta = \frac{1}{2}
2
+ \begin{vmatrix}a &amp; b &amp; c \\ d &amp; e &amp; f \\ g &amp; h &amp; i\end{vmatrix}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:2.08597em;"></span><span class="strut bottom" style="height:3.636em;vertical-align:-1.5500299999999998em;"></span><span class="base"><span class="mord mathrm">&#x394;</span><span class="mrel">=</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.845108em;"><span style="top:-2.6550000000000002em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathrm mtight">2</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathrm mtight">1</span></span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.08597em;"><span style="top:-1.05597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-1.6619700000000002em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.26797em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.87397em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-3.47997em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-4.08597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500299999999998em;"></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">a</span></span></span><span style="top:-3.0099999999999993em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">d</span></span></span><span style="top:-1.8099999999999994em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">g</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500000000000007em;"></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">b</span></span></span><span style="top:-3.0099999999999993em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">e</span></span></span><span style="top:-1.8099999999999994em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">h</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500000000000007em;"></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">c</span></span></span><span style="top:-3.0099999999999993em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.10764em;">f</span></span></span><span style="top:-1.8099999999999994em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">i</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500000000000007em;"></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.08597em;"><span style="top:-1.05597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-1.6619700000000002em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.26797em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-2.87397em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-3.47997em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span><span style="top:-4.08597em;"><span class="pstrut" style="height:2.606em;"></span><span class="delimsizinginner delim-size1"><span>&#x2223;</span></span></span></span><span class="vlist-s">&#x200b;</span></span><span class="vlist-r"><span class="vlist" style="height:1.5500299999999998em;"></span></span></span></span></span></span></span></span></span>
@@ -0,0 +1 @@
1
+ <span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup><mo>=</mo><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">a^2+b^2=c^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8141079999999999em;"></span><span class="strut bottom" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="base"><span class="mord"><span class="mord mathit">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathrm mtight">2</span></span></span></span></span></span></span></span><span class="mbin">+</span><span class="mord"><span class="mord mathit">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathrm mtight">2</span></span></span></span></span></span></span></span><span class="mrel">=</span><span class="mord"><span class="mord mathit">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathrm mtight">2</span></span></span></span></span></span></span></span></span></span></span>
data/test/test_all.rb ADDED
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2017 Christian Cornelssen <ccorn@1tein.de>
5
+ #
6
+ # This file is part of SsKaTeX which is licensed under the MIT.
7
+ #++
8
+
9
+ require 'minitest/autorun'
10
+ require 'sskatex'
11
+
12
+ class SsKaTeXTest < Minitest::Test
13
+ def setup
14
+ @tex2html = SsKaTeX.new
15
+ @tex2html.logger = lambda do |level, &block|
16
+ # warn(block.call) if level == :verbose
17
+ end
18
+ end
19
+
20
+ TESTDIR = File.dirname(__FILE__)
21
+
22
+ # Generate test methods
23
+ Dir[File.join(TESTDIR, 'tex', '*.tex')].each do |f|
24
+ testcase = File.basename(f, '.tex').tr('^a-zA-Z0-9_', '_')
25
+ ['block', 'span'].each do |mode|
26
+ define_method("test_#{testcase}_#{mode}") do
27
+ tex = IO.read(f, external_encoding: Encoding::UTF_8).chomp
28
+ html = IO.read(File.join(TESTDIR, mode, testcase) + '.html',
29
+ external_encoding: Encoding::UTF_8).chomp
30
+ assert_equal @tex2html.call(tex, mode == 'block'), html
31
+ end
32
+ end
33
+ end
34
+ end
data/test/tex/det.tex ADDED
@@ -0,0 +1,2 @@
1
+ \Delta = \frac{1}{2}
2
+ \begin{vmatrix}a & b & c \\ d & e & f \\ g & h & i\end{vmatrix}
data/test/tex/sup.tex ADDED
@@ -0,0 +1 @@
1
+ a^2+b^2=c^2
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sskatex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.20
5
+ platform: ruby
6
+ authors:
7
+ - Christian Cornelssen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: execjs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: duktape
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 1.6.1
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.6'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.6.1
47
+ description: |
48
+ This is a TeX-to-HTML+MathML+CSS converter class using the Javascript-based
49
+ KaTeX, interpreted by one of the Javascript engines supported by ExecJS.
50
+ The intended purpose is to eliminate the need for math-rendering Javascript
51
+ in the client's HTML browser. Therefore the name: SsKaTeX means Server-side
52
+ KaTeX.
53
+
54
+ Javascript execution context initialization can be done once and then reused
55
+ for formula renderings with the same general configuration. As a result, the
56
+ performance is reasonable.
57
+
58
+ The configuration supports arbitrary locations of the external file katex.min.js
59
+ as well as custom Javascript for pre- and postprocessing.
60
+ For that reason, the configuration must not be left to untrusted users.
61
+ email: ccorn@1tein.de
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - COPYING
67
+ - README.md
68
+ - data/sskatex/js/escape_nonascii_html.js
69
+ - data/sskatex/js/tex_to_html.js
70
+ - lib/sskatex.rb
71
+ - test/block/det.html
72
+ - test/block/sup.html
73
+ - test/span/det.html
74
+ - test/span/sup.html
75
+ - test/test_all.rb
76
+ - test/tex/det.tex
77
+ - test/tex/sup.tex
78
+ homepage: https://github.com/ccorn/sskatex
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options:
84
+ - "-a"
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '2.0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements:
98
+ - Javascript engine supported by the ExecJS gem
99
+ rubyforge_project:
100
+ rubygems_version: 2.6.11
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Server-side KaTeX for Ruby
104
+ test_files: []