mathematical 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 020b6daf960edb5a9e5436aef6fa8c406188896c
4
- data.tar.gz: ac15939f237b1086ed76ab8cd36fd55b7cb261e5
3
+ metadata.gz: f7920616d037c8cdab3ade36bceda055362a6de9
4
+ data.tar.gz: a6d2688b1275203704cf01146a952ee7391c4d76
5
5
  SHA512:
6
- metadata.gz: 9f73b02d173514430ff94664fa28e2b2d7cdfd863556796b399c3ee590a97a1904ab9f119b13ea617a677a47bde1351d7c5a4bbcd069d8732ffa097b46a418f9
7
- data.tar.gz: 773a25ece459f013a69554c33018f2be6530ffc0e31d6b821a89690d5a652d84286e51697037b69887c09a682c50669927ff0ee4252cbf29fda3dad301a939df
6
+ metadata.gz: 54a36429dd2a1bd98712ee5a194ef07396a8b76404894d9d3260ca0597b46896a86c97ab6f6664887c15c101dac1c49417880745c55c97ded3672e61390dbb2c
7
+ data.tar.gz: 51fdd320022dd0555be2a3141b7a2de65d5c7c076bd441565da36a61e3ba2a77f172e479e104367b268c3983cd362e170f7dda48a1624971d95bc0ff30b94890
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Quickly convert math equations into beautiful SVGs (or PNGs/MathML).
4
4
 
5
- [![Build Status](https://travis-ci.org/gjtorikian/mathematical.svg?branch=master)](https://travis-ci.org/gjtorikian/mathematical)
5
+ [![Build Status](https://travis-ci.org/gjtorikian/mathematical.svg?branch=master)](https://travis-ci.org/gjtorikian/mathematical) [![Gem Version](https://badge.fury.io/rb/mathematical.svg)](http://badge.fury.io/rb/mathematical)
6
6
 
7
7
  ![Mathematical](https://i.imgur.com/JC7HT32.gif)
8
8
 
@@ -35,15 +35,18 @@ Mathematical.new.render(string_with_math)
35
35
  The output will be a hash, with keys that depend on the format you want:
36
36
 
37
37
  * If you asked for an SVG, you'll get:
38
- * `width`: the width of the resulting image
39
- * `height`: the height of the resulting image
40
- * `svg`: the actual string of SVG
38
+ * `:width`: the width of the resulting image
39
+ * `:height`: the height of the resulting image
40
+ * `:data`: the actual string of SVG
41
41
  * If you asked for a PNG, you'll get:
42
- * `width`: the width of the resulting image
43
- * `height`: the height of the resulting image
44
- * `png`: the PNG data
42
+ * `:width`: the width of the resulting image
43
+ * `:height`: the height of the resulting image
44
+ * `:data`: the PNG data
45
45
  * If you asked for MathML, you'll get:
46
- * `mathml`: the MathML data
46
+ * `:data`: the MathML data
47
+ * If you pass in invalid LaTeX, you'll get:
48
+ * `:data`: the original invalid LaTeX
49
+ * `:error`: the error class (with message)
47
50
 
48
51
  **Note**: If you pass in invalid LaTeX, an error is not raised, but a message *is* printed to STDERR, and the original string is returned (not a hash).
49
52
 
@@ -60,10 +63,7 @@ inputs << '$c$'
60
63
  Mathematical.new.render(inputs)
61
64
  ```
62
65
 
63
- This returns an array of hashes, possessing the same keys as above.
64
-
65
- **Note**: With an array, it is possible to receive elements that are not hashes. For example, given the following input:
66
-
66
+ This returns an array of hashes, possessing the same keys as above. For example:
67
67
  ```
68
68
  array = ['$foof$', '$not__thisisnotreal$', '$poof$']
69
69
  ```
@@ -72,10 +72,10 @@ You will receive the following output:
72
72
 
73
73
  ```
74
74
  Mathematical.new.render(array)
75
- [ {:svg => "...", :width => ... }, '$not__thisisnotreal$', {:svg => "...", :width => ... }]
75
+ [ {:data => "...", :width => ... }, { :data => '$not__thisisnotreal$', :error => "...", {:data => "...", :width => ... }]
76
76
  ```
77
77
 
78
- That is, while the first and last elements are valid LaTeX math, the middle one is not, so the same string is returned. As with a single string, a message is also printed to STDERR.
78
+ That is, while the first and last elements are valid LaTeX math, the middle one is not, so the same string is returned. As with single strings, a message is also printed to STDERR.
79
79
 
80
80
  ### Options
81
81
 
@@ -76,10 +76,20 @@ void print_and_raise(VALUE error_type, const char* format, ...)
76
76
  va_end(args);
77
77
  }
78
78
 
79
+ static VALUE process_rescue(VALUE args, VALUE exception_object)
80
+ {
81
+ VALUE rescue_hash = rb_hash_new();
82
+
83
+ rb_hash_aset (rescue_hash, CSTR2SYM ("data"), args);
84
+ rb_hash_aset (rescue_hash, CSTR2SYM ("exception"), exception_object);
85
+
86
+ return rescue_hash;
87
+ }
88
+
79
89
  VALUE process(VALUE self, unsigned long maxsize, const char *latex_code, unsigned long latex_size)
80
90
  {
81
91
  if (latex_size > maxsize) {
82
- print_and_raise(rb_eMaxsizeError, "Size of latex string (%lu) is greater than the maxsize (%lu)!", latex_size, maxsize);
92
+ print_and_raise(rb_eMaxsizeError, "Size of latex string is greater than the maxsize");
83
93
  }
84
94
 
85
95
  VALUE result_hash = rb_hash_new();
@@ -87,7 +97,7 @@ VALUE process(VALUE self, unsigned long maxsize, const char *latex_code, unsigne
87
97
 
88
98
  // convert the LaTeX math to MathML
89
99
  char * mathml = lsm_mtex_to_mathml(latex_code, latex_size, global_start);
90
- if (mathml == NULL) { print_and_raise(rb_eParseError, "Failed to parse mtex: %s", latex_code); }
100
+ if (mathml == NULL) { print_and_raise(rb_eParseError, "Failed to parse mtex"); }
91
101
 
92
102
  // basically, only update the next equation counter if the last math had a numbered equation
93
103
  if (strstr(mathml, "<mlabeledtr>") != NULL) {
@@ -95,7 +105,7 @@ VALUE process(VALUE self, unsigned long maxsize, const char *latex_code, unsigne
95
105
  }
96
106
 
97
107
  if (format == FORMAT_MATHML) {
98
- rb_hash_aset (result_hash, rb_tainted_str_new2 ("mathml"), rb_str_new2(mathml));
108
+ rb_hash_aset (result_hash, CSTR2SYM ("data"), rb_str_new2(mathml));
99
109
  mtex2MML_free_string(mathml);
100
110
  return result_hash;
101
111
  }
@@ -157,12 +167,12 @@ VALUE process(VALUE self, unsigned long maxsize, const char *latex_code, unsigne
157
167
  switch (format) {
158
168
  case FORMAT_SVG: {
159
169
  if (rb_iv_get(self, "@svg") == Qnil) { print_and_raise(rb_eDocumentReadError, "Failed to read SVG contents"); }
160
- rb_hash_aset (result_hash, rb_tainted_str_new2 ("svg"), rb_iv_get(self, "@svg"));
170
+ rb_hash_aset (result_hash, CSTR2SYM ("data"), rb_iv_get(self, "@svg"));
161
171
  break;
162
172
  }
163
173
  case FORMAT_PNG: {
164
174
  if (rb_iv_get(self, "@png") == Qnil) { print_and_raise(rb_eDocumentReadError, "Failed to read PNG contents"); }
165
- rb_hash_aset (result_hash, rb_tainted_str_new2 ("png"), rb_iv_get(self, "@png"));
175
+ rb_hash_aset (result_hash, CSTR2SYM ("data"), rb_iv_get(self, "@png"));
166
176
  break;
167
177
  }
168
178
  default: {
@@ -172,8 +182,8 @@ VALUE process(VALUE self, unsigned long maxsize, const char *latex_code, unsigne
172
182
  }
173
183
  }
174
184
 
175
- rb_hash_aset (result_hash, rb_tainted_str_new2 ("width"), INT2FIX(width_pt));
176
- rb_hash_aset (result_hash, rb_tainted_str_new2 ("height"), INT2FIX(height_pt));
185
+ rb_hash_aset (result_hash, CSTR2SYM ("width"), INT2FIX(width_pt));
186
+ rb_hash_aset (result_hash, CSTR2SYM ("height"), INT2FIX(height_pt));
177
187
 
178
188
  // we need to clear out this key when attempting multiple calls. See http://git.io/i1hblQ
179
189
  rb_iv_set(self, "@svg", Qnil);
@@ -189,11 +199,6 @@ static VALUE process_helper(VALUE data)
189
199
  return process(args[0], NUM2ULONG(args[1]), StringValueCStr(args[2]), NUM2ULONG(args[3]));
190
200
  }
191
201
 
192
- static VALUE process_failed(void)
193
- {
194
- return Qnil;
195
- }
196
-
197
202
  static VALUE MATHEMATICAL_process(VALUE self, VALUE rb_Input)
198
203
  {
199
204
  unsigned long maxsize = (unsigned long) FIX2INT(rb_iv_get(self, "@maxsize"));
@@ -241,14 +246,9 @@ static VALUE MATHEMATICAL_process(VALUE self, VALUE rb_Input)
241
246
  args[1] = ULONG2NUM(maxsize);
242
247
  args[2] = math;
243
248
  args[3] = ULONG2NUM(latex_size);
244
- hash = rb_rescue(process_helper, args, process_failed, 0);
245
-
246
- // the call errored; just store the same string
247
- if (hash == Qnil) {
248
- rb_ary_store(output, i, math);
249
- } else {
250
- rb_ary_store(output, i, hash);
251
- }
249
+ hash = rb_rescue(process_helper, args, process_rescue, math);
250
+
251
+ rb_ary_store(output, i, hash);
252
252
  }
253
253
  break;
254
254
  }
data/lib/mathematical.rb CHANGED
@@ -34,13 +34,13 @@ class Mathematical
34
34
  maths = validate_content(maths)
35
35
 
36
36
  begin
37
- data = @processer.process(maths)
38
- fail RuntimeError if data.nil? || (!data.is_a?(Hash) && !data.is_a?(Array))
37
+ result_data = @processer.process(maths)
38
+ fail RuntimeError if result_data.nil? || (!result_data.is_a?(Hash) && !result_data.is_a?(Array))
39
39
 
40
- if data.is_a? Array
41
- data.map { |d| format_data(d) }
40
+ if result_data.is_a? Array
41
+ result_data.map { |d| format_data(d) }
42
42
  else
43
- format_data(data)
43
+ format_data(result_data)
44
44
  end
45
45
  rescue ParseError, DocumentCreationError, DocumentReadError => e
46
46
  # an error in the C code, probably a bad TeX parse
@@ -96,19 +96,19 @@ class Mathematical
96
96
  maths =~ /\A\${1,2}/
97
97
  end
98
98
 
99
- def format_data(data)
99
+ def format_data(result_hash)
100
100
  # we passed in an array of math, and found an unprocessable element
101
- return data unless data.is_a? Hash
101
+ return result_hash if result_hash[:exception]
102
102
 
103
103
  case @config[:format]
104
104
  when :svg
105
105
  # remove starting <?xml...> tag
106
- data['svg'] = data['svg'][XML_HEADER.length..-1]
107
- data['svg'] = svg_to_base64(data['svg']) if @config[:base64]
106
+ result_hash[:data] = result_hash[:data][XML_HEADER.length..-1]
107
+ result_hash[:data] = svg_to_base64(result_hash[:data]) if @config[:base64]
108
108
 
109
- data
109
+ result_hash
110
110
  when :png, :mathml # do nothing with these...for now?
111
- data
111
+ result_hash
112
112
  end
113
113
  end
114
114
 
@@ -1,3 +1,3 @@
1
1
  class Mathematical
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -9,7 +9,7 @@ class Mathematical::BasicTest < Test::Unit::TestCase
9
9
  def test_multiple_calls
10
10
  render = Mathematical.new
11
11
  render.render('$\pi$')
12
- output = render.render('$\pi$')['svg']
12
+ output = render.render('$\pi$')[:data]
13
13
  assert_equal 1, output.scan(/<svg/).size, 'should only contain one svg'
14
14
  end
15
15
 
@@ -18,14 +18,14 @@ class Mathematical::FixturesTest < Test::Unit::TestCase
18
18
  svg_content = Mathematical.new(:base64 => false).render(eq)
19
19
  # remove \ and $, remove whitespace, keep alphanums, remove extraneous - and trailing -
20
20
  filename = eq.gsub(/[\$\\]*/, '').gsub(/\s+/, '-').gsub(/[^a-zA-Z\d]/, '-').gsub(/-{2,}/, '-').gsub(/-$/, '')
21
- File.open("samples/fixtures/#{filename}.svg", 'w') { |file| file.write svg_content['svg'] }
21
+ File.open("samples/fixtures/#{filename}.svg", 'w') { |file| file.write svg_content[:data] }
22
22
  end
23
23
  end
24
24
 
25
25
  actual = MathToItex(source).convert do |eq, type|
26
26
  svg_content = Mathematical.new(:base64 => true).render(eq)
27
27
 
28
- %|<img class="#{type.to_s}-math" data-math-type="#{type.to_s}-math" src="#{svg_content['svg']}"/>|
28
+ %(<img class="#{type}-math" data-math-type="#{type}-math" src="#{svg_content[:data]}"/>)
29
29
  end.rstrip
30
30
 
31
31
  expected_file = before.sub(/before/, 'after').sub(/text/, 'html')
@@ -34,7 +34,7 @@ class Mathematical::FixturesTest < Test::Unit::TestCase
34
34
 
35
35
  expected = File.read(expected_file)
36
36
 
37
- expected = (MathToItex(expected).convert {|string| Mathematical.new.render(string)}).rstrip
37
+ expected = (MathToItex(expected).convert { |string| Mathematical.new.render(string) }).rstrip
38
38
 
39
39
  # Travis and OS X each render SVGs differently. For now, let's just be happy
40
40
  # that something renders at all.
@@ -109,8 +109,30 @@ class Mathematical::MaliciousnessTest < Test::Unit::TestCase
109
109
  assert_equal 3, output.length
110
110
  assert_equal Hash, output.first.class
111
111
  assert_equal Hash, output.last.class
112
- assert_equal '$/this___istotallyfake$', output[1]
113
- # array errors output to STDERR
114
- assert_match /Failed to parse mtex: \$\/this___istotallyfake\$/, err
112
+
113
+ assert_equal '$/this___istotallyfake$', output[1][:data]
114
+ assert_equal Mathematical::ParseError, output[1][:exception].class
115
+ assert_match 'Failed to parse mtex', output[1][:exception].message
116
+
117
+ # array errors also output to STDERR
118
+ assert_match /Failed to parse mtex/, err
119
+ end
120
+
121
+ def test_it_passes_a_legible_error_for_maxsize
122
+ output = nil
123
+ render = Mathematical.new({:maxsize => 2})
124
+
125
+ _, err = capture_subprocess_io do
126
+ output = render.render(['$a \ne b$'])
127
+ end
128
+
129
+ assert_equal 1, output.length
130
+
131
+ assert_equal '$a \ne b$', output[0][:data]
132
+ assert_equal Mathematical::MaxsizeError, output[0][:exception].class
133
+ assert_match 'Size of latex string is greater than the maxsize', output[0][:exception].message
134
+
135
+ # array errors also output to STDERR
136
+ assert_match /Size of latex string is greater than the maxsize/, err
115
137
  end
116
138
  end
@@ -16,7 +16,7 @@ class Mathematical::MathJaxTest < Test::Unit::TestCase
16
16
  assert_nothing_raised { data = render_svg.render(tex_contents) }
17
17
 
18
18
  # assert the SVG actually rendered
19
- doc = Nokogiri::HTML(data['svg'])
19
+ doc = Nokogiri::HTML(data[:data])
20
20
  assert_empty doc.search(%(//svg[@width='0pt']))
21
21
  assert_empty doc.search(%(//svg[@height='0pt']))
22
22
  end
@@ -14,7 +14,7 @@ class Mathematical::MathMLTest < Test::Unit::TestCase
14
14
  $$
15
15
  '''
16
16
  render = Mathematical.new({:format => :mathml})
17
- mathml = render.render(string)['mathml']
17
+ mathml = render.render(string)[:data]
18
18
 
19
19
  assert_match %r{<math xmlns='http://www.w3.org/1998/Math/MathML' display='block'><semantics><mrow><mrow><mo>\(}, mathml
20
20
  end
@@ -16,7 +16,7 @@ $$
16
16
  '''
17
17
 
18
18
  output = @render.render([string])
19
- svg = output.first['svg']
19
+ svg = output.first[:data]
20
20
 
21
21
  assert_equal 1, svg.scan(/svg\+xml;/).size, 'should only contain one svg'
22
22
  assert_match 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My', svg
@@ -38,13 +38,13 @@ $$
38
38
  output = @render.render(inputs)
39
39
  assert_equal 1000, output.length
40
40
  output.each do |data|
41
- assert_match 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My', data['svg']
41
+ assert_match 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My', data[:data]
42
42
  end
43
43
  end
44
44
 
45
45
  def test_it_properly_accounts_for_equations
46
46
  inputs = []
47
- [1, 2].each do |i|
47
+ (1..2).each do |i|
48
48
  string = """
49
49
  $$
50
50
  \\begin{equation}
@@ -60,8 +60,8 @@ $$
60
60
  output = render.render(inputs)
61
61
  assert_equal 3, output.length
62
62
  output.each_with_index do |data_hash, i|
63
- header = data_hash['png'].unpack('H*').first.slice(0, 18)
64
- File.open("#{fixtures_dir}/png/numeric_test_#{i + 1}.png", 'w') { |f| f.write(data_hash['png'])}
63
+ header = data_hash[:data].unpack('H*').first.slice(0, 18)
64
+ File.open("#{fixtures_dir}/png/numeric_test_#{i + 1}.png", 'w') { |f| f.write(data_hash[:data])}
65
65
  assert_equal header, '89504e470d0a1a0a00'
66
66
  end
67
67
  end
@@ -18,8 +18,8 @@ $$
18
18
  '''
19
19
  render = Mathematical.new({:format => :png})
20
20
  data_hash = render.render(string)
21
- header = data_hash['png'].unpack('H*').first.slice(0, 18)
22
- File.open("#{fixtures_dir}/png/pmatrix.png", 'w') { |f| f.write(data_hash['png'])}
21
+ header = data_hash[:data].unpack('H*').first.slice(0, 18)
22
+ File.open("#{fixtures_dir}/png/pmatrix.png", 'w') { |f| f.write(data_hash[:data])}
23
23
  assert_equal header, '89504e470d0a1a0a00'
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mathematical
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian