sskatex 0.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []