latexml-ruby 0.0.1
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 +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +21 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/Rakefile +10 -0
- data/latexml-ruby.gemspec +25 -0
- data/lib/latexml.rb +270 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9f12825ac7bc96cc0bcde6d9d4b10cd303760873
|
4
|
+
data.tar.gz: 832c989f6b175c6cc0ad3e45d489cbe547ecdb83
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b69ee41107db0eb159a9c1a7a412a64f8497c75e74402d9bd9acfe4b2d9b822f115359921ebabe8d889b05ec570a16b3ced06e130d77820b66947f8b0686c32
|
7
|
+
data.tar.gz: dc5aafc5be83e8e305dee9b6d646647faf426607c98650bc51e07820d4ff3863d2b85c94b4d5631dd0c3557d9105ce43c52b84e2f5484ffd1758feda455db174
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.2.4
|
5
|
+
addons:
|
6
|
+
apt:
|
7
|
+
packages:
|
8
|
+
- libdb-dev
|
9
|
+
- libxml2-dev
|
10
|
+
- libxslt1-dev
|
11
|
+
- libgdbm-dev
|
12
|
+
before_install:
|
13
|
+
- gem install bundler -v 1.12.5
|
14
|
+
# Thanks to SO: http://stackoverflow.com/a/32358866
|
15
|
+
# Install modules into ~/perl5 using system perl
|
16
|
+
- curl -L https://cpanmin.us | perl - App::cpanminus
|
17
|
+
- export PATH=$PATH:~/perl5/bin/
|
18
|
+
- cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
|
19
|
+
- cpanm JSON
|
20
|
+
- cpanm --notest https://github.com/brucemiller/LaTeXML.git
|
21
|
+
- cpanm https://github.com/dginev/LaTeXML-Plugin-latexmls.git
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Deyan Ginev
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# LaTeXML-Ruby
|
2
|
+
|
3
|
+
[](https://travis-ci.org/Authorea/latexml-ruby)
|
4
|
+
[](https://raw.githubusercontent.com/authorea/LaTeXML-Ruby/master/LICENSE)
|
5
|
+
|
6
|
+
|
7
|
+
A Ruby wrapper for the [LaTeXML](http://dlmf.nist.gov/LaTeXML/) LaTeX to XML/HTML/ePub converter.
|
8
|
+
|
9
|
+
Includes support for daemonized conversion runs, for additional performance, via the [latexmls](https://github.com/dginev/LaTeXML-Plugin-latexmls) socket server.
|
10
|
+
|
11
|
+
## Why LaTeXML?
|
12
|
+
|
13
|
+
You may be familiar with other LaTeX conversion tools such as Pandoc or tex4ht. LaTeXML attempts to be a complete TeX interpreter, and covers a vastly larger range of the TeX/LaTeX ecosystem than Pandoc. At the same time it allows for just-in-time binding of structural and semantic macros, which allows it to create higher quality HTML5 than tex4ht, and makes bridging the impedance mismatch between PDF and HTML an achievable goal.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'latexml-ruby'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install latexml-ruby
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
A hello world conversion job looks like:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
@latexml = LaTeXML.new
|
37
|
+
|
38
|
+
response = @latexml.convert(literal: "hello world")
|
39
|
+
|
40
|
+
result = response[:result]
|
41
|
+
messages = response[:messages]
|
42
|
+
|
43
|
+
```
|
44
|
+
|
45
|
+
## Contributing
|
46
|
+
|
47
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Authorea/latexml-ruby.
|
48
|
+
|
49
|
+
The 0.0.1 release of the wrapper brings support for easy conversion of latex fragments, which only scratches the surface of LaTeXML's versatile conversion use cases. If you are interested in a different workflow that is not yet supported, we will be very happy to hear from you.
|
50
|
+
|
51
|
+
## License
|
52
|
+
|
53
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
54
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
Gem::Specification.new do |spec|
|
3
|
+
spec.name = "latexml-ruby"
|
4
|
+
spec.version = "0.0.1"
|
5
|
+
|
6
|
+
spec.authors = ["Deyan Ginev"]
|
7
|
+
spec.email = ["deyan@authorea.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{Ruby wrapper for LaTeXML}
|
10
|
+
spec.description = %q{The wrapper automates LaTeX to HTML5 conversions with LaTeXML, addressing common production needs such as error-handling, timeouts, managing option sets and automatic recognition of available binaries.}
|
11
|
+
spec.homepage = "https://github.com/Authorea/latexml-ruby"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.require_paths = ["lib"]
|
16
|
+
|
17
|
+
spec.add_dependency 'escape_utils', '~> 1.2'
|
18
|
+
spec.add_dependency 'json', '~> 1.8'
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
23
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.1"
|
24
|
+
|
25
|
+
end
|
data/lib/latexml.rb
ADDED
@@ -0,0 +1,270 @@
|
|
1
|
+
class LaTeXML
|
2
|
+
require 'socket'
|
3
|
+
require 'timeout'
|
4
|
+
require 'net/http'
|
5
|
+
require 'escape_utils'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
# Timeouts are tricky with this setup.
|
10
|
+
# For example, we know a regular Authorea latexml job should be done in 12 seconds,
|
11
|
+
# but sometimes we boot a new latexmls server, which takes about 5-6 seconds total, so we end up with
|
12
|
+
# http requests that may take up to 18 seconds. Requires setup-specific tuning.
|
13
|
+
options = {debug: true, preload_timeout: 6, latexml_timeout: 12, timeout_rescue_sleep: 0.5,
|
14
|
+
setup: [
|
15
|
+
{expire: 86400},
|
16
|
+
{autoflush: 10000},
|
17
|
+
{cache_key: 'latexml_ruby'}, # Cache this setup, for avoiding the startup runtime costs
|
18
|
+
{nocomments: true},
|
19
|
+
{nographicimages: true},
|
20
|
+
{nopictureimages: true},
|
21
|
+
{noparse: true}, # Don't parse the math, using MathJaX for now
|
22
|
+
{format: 'html5'},
|
23
|
+
{nodefaultresources: true}, # Don't copy any aux files over
|
24
|
+
{whatsin: 'fragment'},
|
25
|
+
{whatsout: 'fragment'},
|
26
|
+
# TeX preloads:
|
27
|
+
# The more preloads are provided on initialization, the faster the conversion overall
|
28
|
+
# NOTE: LaTeXML will gracefully handle repeated \usepackage loads of the same package, so better add more preloads
|
29
|
+
# than worry about conflicts
|
30
|
+
%w(article.cls graphicx.sty latexsym.sty amsfonts.sty amsmath.sty amsthm.sty
|
31
|
+
amstext.sty amssymb.sty eucal.sty [utf8]inputenc.sty url.sty hyperref.sty textcomp.sty longtable.sty
|
32
|
+
multirow.sty booktabs.sty fixltx2e.sty
|
33
|
+
fullpage.sty [table,dvipsnames]xcolor.sty listings.sty deluxetable.sty xspace.sty
|
34
|
+
[noids]latexml.sty [labels]lxRDFa.sty
|
35
|
+
secureio.sty) # This is crucial for security reasons.
|
36
|
+
.collect{|style| {preload: style} }].flatten
|
37
|
+
}.merge(options)
|
38
|
+
@debug = options[:debug]
|
39
|
+
@preload_timeout = options[:preload_timeout]
|
40
|
+
|
41
|
+
@latexml_timeout = options[:latexml_timeout]
|
42
|
+
options[:setup].push({timeout: @latexml_timeout.to_s}) # also pass to latexml
|
43
|
+
|
44
|
+
@http_timeout = @latexml_timeout + @preload_timeout
|
45
|
+
@timeout_rescue_sleep = options[:timeout_rescue_sleep]
|
46
|
+
# The current set of default options has historically been used for converting Authorea content
|
47
|
+
# with LaTeXML 0.8.1 and up
|
48
|
+
|
49
|
+
# Note that we need an array of hashes, because duplicate keys ARE allowed (e.g. path)
|
50
|
+
# and more importantly, the ORDER of options is meaningful (overrides are also allowed)
|
51
|
+
@setup_options = options[:setup]
|
52
|
+
|
53
|
+
@response_server_unreachable = options[:response_server_unreachable] || {
|
54
|
+
result: '',
|
55
|
+
log:[{
|
56
|
+
severity: 'fatal',
|
57
|
+
category: 'latexmls',
|
58
|
+
what: 'server unreachable',
|
59
|
+
details: "The LaTeXML server was unreachable at this time"}]
|
60
|
+
}
|
61
|
+
|
62
|
+
@response_connection_reset = options[:response_connection_reset] || {
|
63
|
+
result: '',
|
64
|
+
log:[{
|
65
|
+
severity: 'fatal',
|
66
|
+
category: 'latexmls',
|
67
|
+
what: 'connection reset',
|
68
|
+
details: "The LaTeXML server was unreachable at this time"}]
|
69
|
+
}
|
70
|
+
|
71
|
+
@response_empty_input = options[:response_empty_input] || {
|
72
|
+
result: '',
|
73
|
+
log:[{severity: 'no_problem'}]
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def convert(options={})
|
78
|
+
source = options.delete(:literal) || options.delete(:source)
|
79
|
+
if source.to_s.strip.empty?
|
80
|
+
return @response_empty_input.deep_dup
|
81
|
+
end
|
82
|
+
source = "literal:#{source}"
|
83
|
+
|
84
|
+
render_options = [{source: EscapeUtils.escape_uri(source)}]
|
85
|
+
if !options[:preamble].to_s.strip.empty?
|
86
|
+
render_options << {preamble: EscapeUtils.escape_uri(options[:preamble])}
|
87
|
+
end
|
88
|
+
# Discussion: This could be useful if/when we decide to have IDs in the document fragments of an article
|
89
|
+
# However, at the moment it is hitting a flaw in the LaTeXML design which causes all packages to reload on every conversion
|
90
|
+
# and that is SLOW. So commenting out for now.
|
91
|
+
# if options[:documentid].present?
|
92
|
+
# render_options << {documentid: EscapeUtils.escape_uri(options[:documentid])}'"
|
93
|
+
# end
|
94
|
+
|
95
|
+
render_options.concat @setup_options
|
96
|
+
|
97
|
+
time_before_call = Time.now
|
98
|
+
# We are talking to socket servers, via the LaTeXML-Plugin-latexmls extension:
|
99
|
+
server_port = options[:server_port] || 3334
|
100
|
+
server_address = options[:server_address] || "0.0.0.0"
|
101
|
+
# We can only proceed if we have a working socket server
|
102
|
+
if !ensure_latexmls(server_port)
|
103
|
+
return @response_server_unreachable.deep_dup
|
104
|
+
end
|
105
|
+
|
106
|
+
# Setting up POST request
|
107
|
+
post_body = render_options.map{|h| h.map{|k,v| (v == true) ? k : "#{k}=#{v}"}}.flatten.join("&")
|
108
|
+
latexmls_uri = URI.parse("http://#{server_address}:#{server_port}")
|
109
|
+
request = Net::HTTP::Post.new(latexmls_uri.request_uri)
|
110
|
+
request.body = post_body
|
111
|
+
request['Content-Type'] = 'application/x-www-form-urlencoded'
|
112
|
+
http = Net::HTTP.new(latexmls_uri.host, latexmls_uri.port)
|
113
|
+
http.read_timeout = @http_timeout # give up after X seconds
|
114
|
+
|
115
|
+
puts "*** Starting LaTeXML call to port #{server_port}" if @debug
|
116
|
+
http_response = nil
|
117
|
+
begin
|
118
|
+
Timeout::timeout(@http_timeout) do # we'll keep trying for X seconds before giving up
|
119
|
+
# we are going to retry on failure, as this is likely an autoflush process reboot (expected behaviour)
|
120
|
+
loop do
|
121
|
+
begin
|
122
|
+
http_response = http.request(request)
|
123
|
+
break
|
124
|
+
rescue => e
|
125
|
+
puts "*** latexmls http request error: #{e.message}" if @debug
|
126
|
+
sleep @timeout_rescue_sleep #avoid DoS
|
127
|
+
if !ensure_latexmls(server_port)
|
128
|
+
response = @response_connection_reset.deep_dup
|
129
|
+
if e && e.message
|
130
|
+
response[:what] = e.message
|
131
|
+
end
|
132
|
+
return response
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
rescue Timeout::Error
|
138
|
+
if @debug
|
139
|
+
latexml_time = Time.now - time_before_call
|
140
|
+
puts "LATEXML: Timeout took #{latexml_time} seconds"
|
141
|
+
puts "LATEXML: request: #{request.body}"
|
142
|
+
end
|
143
|
+
return @response_connection_reset.deep_dup
|
144
|
+
end
|
145
|
+
latexml_time = Time.now - time_before_call
|
146
|
+
puts "*** LaTeXML call to port #{server_port} took #{latexml_time} seconds" if @debug
|
147
|
+
|
148
|
+
if @debug && (latexml_time > 5.0)
|
149
|
+
puts "LATEXML: Slow render took #{latexml_time} seconds"
|
150
|
+
puts "LATEXML: request: #{request.body}"
|
151
|
+
# email_subject = "LATEXML: Slow render took #{latexml_time} seconds"
|
152
|
+
# email_content = "#{request.body}"
|
153
|
+
# Resque.enqueue(NotificationsWorker, email_subject, email_content)
|
154
|
+
end
|
155
|
+
|
156
|
+
response = JSON.parse(http_response.body)
|
157
|
+
|
158
|
+
html = response["result"] || ""
|
159
|
+
log = response["log"] || ""
|
160
|
+
# if html.to_s.strip.empty?
|
161
|
+
# puts "LATEXML: Empty result"
|
162
|
+
# puts "LATEXML: request: #{request.body}"
|
163
|
+
# email_subject = "LATEXML: Empty result"
|
164
|
+
# email_content = "#{request.body}\n\nLog: #{log}"
|
165
|
+
# Resque.enqueue(NotificationsWorker, email_subject, email_content)
|
166
|
+
# end
|
167
|
+
# We can check for the error code if we want to: 0 is ok, 1 is warning, 2 is error and 3 is fatal error
|
168
|
+
# status = response["status"]
|
169
|
+
|
170
|
+
# Return the HTML content:
|
171
|
+
return {result: html, messages: parse_log(log)}
|
172
|
+
end
|
173
|
+
|
174
|
+
# Parses a log string which follows the LaTeXML convention
|
175
|
+
# (described at http://dlmf.nist.gov/LaTeXML/manual/errorcodes/index.html)
|
176
|
+
def parse_log(content)
|
177
|
+
# Quit unless we have some data
|
178
|
+
content = content.to_s.strip
|
179
|
+
return if content.empty?
|
180
|
+
# Obtain the individual lines
|
181
|
+
messages = []
|
182
|
+
in_details_mode = false
|
183
|
+
content.split("\n").reject{|l| l.to_s.strip.empty?}.each do |line|
|
184
|
+
# If we have found a message header and we're collecting details:
|
185
|
+
if in_details_mode
|
186
|
+
# If the line starts with tab, we are indeed reading in details
|
187
|
+
if line.match(/^\t/)
|
188
|
+
# Append details line to the last message"
|
189
|
+
messages.last[:details].concat("\n#{line}")
|
190
|
+
if messages.last[:line].to_s.strip.empty? # Only get the first line#col report
|
191
|
+
if posmatch = line.match(/at Literal String(.*); line (\d+) col (\d+)/)
|
192
|
+
messages.last[:line] = posmatch[2]
|
193
|
+
messages.last[:col] = posmatch[3]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
next # This line has been consumed, next
|
197
|
+
else
|
198
|
+
in_details_mode = false
|
199
|
+
# Not a details line, continue the current iteration with analyzing a new message
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Since this isn't a details line, check if it's a message line:
|
204
|
+
if matches = line.match(/^([^ :]+)\:([^ :]+)\:([^ ]+)(\s(.+))?$/)
|
205
|
+
# Indeed a message, so record it:
|
206
|
+
message = {severity: matches[1].downcase, category: matches[2].downcase, what: matches[3].downcase, details: matches[5] ? matches[5].downcase : ''}
|
207
|
+
# Prepare to record follow-up lines with the message details:
|
208
|
+
in_details_mode = true
|
209
|
+
# Add to the array of parsed messages
|
210
|
+
messages.push(message)
|
211
|
+
else
|
212
|
+
# Otherwise line is just noise, continue...
|
213
|
+
in_details_mode = false
|
214
|
+
end
|
215
|
+
end
|
216
|
+
# Return the parsed messages
|
217
|
+
return messages
|
218
|
+
end
|
219
|
+
|
220
|
+
# One way of checking if we have a socket server running at a given port
|
221
|
+
def local_port_open?(port, seconds=1)
|
222
|
+
Timeout::timeout(seconds) do
|
223
|
+
begin
|
224
|
+
TCPSocket.new('localhost', port).close
|
225
|
+
true
|
226
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
227
|
+
false
|
228
|
+
end
|
229
|
+
end
|
230
|
+
rescue Timeout::Error
|
231
|
+
false
|
232
|
+
end
|
233
|
+
|
234
|
+
def ensure_latexmls(server_port = 3334)
|
235
|
+
Timeout::timeout(@preload_timeout) do # we'll try for X seconds before giving up
|
236
|
+
loop do
|
237
|
+
if local_port_open?(server_port)
|
238
|
+
break
|
239
|
+
else
|
240
|
+
# expire=600: daemonized, if idle for 10 minutes will self-terminate
|
241
|
+
# expire=86400: daemonized, if idle for 24 hours will self-terminate
|
242
|
+
# autoflush: auto-restart process after X conversions. Useful if memory is leaking (shouldn't be). 0 to disable.
|
243
|
+
sys_options = @setup_options.map do |h|
|
244
|
+
h.map do |k,v|
|
245
|
+
(v==true) ? ["--#{k}"] : ["--#{k}", v.to_s]
|
246
|
+
end
|
247
|
+
end.flatten
|
248
|
+
system(LaTeXML.executable,"--port",server_port.to_s, *sys_options)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
return true
|
252
|
+
end
|
253
|
+
rescue Timeout::Error
|
254
|
+
return false
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.is_installed?
|
258
|
+
self.executable
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.executable
|
262
|
+
@@executable ||= if system("which latexmls > /dev/null 2>&1")
|
263
|
+
'latexmls'
|
264
|
+
elsif system("which latexmlc > /dev/null 2>&1")
|
265
|
+
'latexmlc'
|
266
|
+
else
|
267
|
+
nil
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: latexml-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Deyan Ginev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: escape_utils
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.12'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.12'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest-reporters
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.1'
|
97
|
+
description: The wrapper automates LaTeX to HTML5 conversions with LaTeXML, addressing
|
98
|
+
common production needs such as error-handling, timeouts, managing option sets and
|
99
|
+
automatic recognition of available binaries.
|
100
|
+
email:
|
101
|
+
- deyan@authorea.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- ".travis.yml"
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- latexml-ruby.gemspec
|
113
|
+
- lib/latexml.rb
|
114
|
+
homepage: https://github.com/Authorea/latexml-ruby
|
115
|
+
licenses:
|
116
|
+
- MIT
|
117
|
+
metadata: {}
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.4.8
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Ruby wrapper for LaTeXML
|
138
|
+
test_files: []
|