Skip to content

Commit db5fc3d

Browse files
committed
feat: fix arnog#182 mhchem support
1 parent 6333d2e commit db5fc3d

13 files changed

+192
-133
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
### New Features
44

55
- Added support for `\phantom`, `\vphantom`, `\hphantom` and `\smash[]`
6+
- **#182** Added support for the mhchem package, with the commands `\ce` and `\pu` to
7+
display chemical equations
68

79
## 0.55.0 (2020-08-17)
810

css/core.less

+3
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,9 @@ body.ML__fonts-loading .ML__base {
652652
text-align: center;
653653
}
654654
}
655+
.op-over-under {
656+
position: relative;
657+
}
655658
.op-over-under > .vlist > span:first-child,
656659
.op-over-under > .vlist > span:last-child {
657660
text-align: center;

examples/test-cases/index.html

+87-18
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,7 @@
2727
<h1>MathLive Test Cases</h1>
2828
</header>
2929
<main>
30-
<div class="mathfield" id="mf">\sqrt{\smash[b]{\frac{a}{b}}}</div>
31-
<!-- \ce{ H2O } -->
32-
<!-- \ce{ CrO4^2- } \ce{ ^{227}_{90}Th+ } \ce{ {}^{227}_{90}Th+ } \ce{ KCr(SO4)2.12H2O } \ce{ C6H5-CHO } \ce{ X=Y#Z } \ce{ A\sbond B\dbond C\tbond D } \ce{ A\bond{-}B\bond{=}C\bond{#}D } \ce{ A\bond{~}B\bond{~-}C } -->
33-
<!-- \ce{ $x\,$ Na(NH4)HPO4 ->[\Delta](NaPO3)_{$x$} + $x\,$ NH3 ^ + $x\,$ H2O } -->
34-
<!-- \ce{ CO2 + C -> 2CO } -->
35-
<!-- \ce{ CO2 + C <=> 2CO } -->
36-
<!-- \ce{ H+ + OH- <=>> H2O } -->
37-
<!-- \ce{ CO2 + C ->[\alpha][\beta] 2CO } -->
38-
<!-- \ce{ $A$ ->C[+H2O] $B$ } -->
39-
<!-- \ce{ SO4^2- + Ba^2+ -> BaSO4 v } -->
40-
<!-- \ce{ Zn^2+ <=>[\ce{+ 2OH-}][\ce{+ 2H+}]$\underset{\text{amphoteric hydroxide}}{\ce{Zn(OH)2 v}}$<=>C[+2OH-][{+ 2H+}]$\underset{\text{tetrahydroxozincate}}{\cf{[Zn(OH)4]^2-}}$ } -->
41-
<!-- \ce{ Hg^2+ ->[\ce{I-}]$\underset{\mathrm{red}}{\ce{HgI2}}$->C[I-]$\underset{\mathrm{red}}{\ce{[Hg^{II}I4]^2-}}$ } -->
30+
<div class="mathfield" id="mf">\sqrt</div>
4231
<!-- (x,,2) -->
4332
<!-- -123, 456.789, -->
4433
<!-- x_5 -->
@@ -98,6 +87,86 @@ <h1>Examples</h1>
9887

9988
// prettier-ignore
10089
const TESTING_SAMPLES = [
90+
{
91+
title: 'Chemistry',
92+
latex: '\\ce{ CrO4^2- }'
93+
},
94+
{
95+
title: 'Chemistry',
96+
latex: '\\ce{ CrO4^2-}'
97+
},
98+
{
99+
title: 'Chemistry',
100+
latex: '\\ce{^{227}_{90}Th+ }'
101+
},
102+
{
103+
title: 'Chemistry',
104+
latex: '\\ce{ {}^{227}_{90}Th+ }'
105+
},
106+
{
107+
title: 'Chemistry',
108+
latex: '\\ce{ KCr(SO4)2.12H2O }'
109+
},
110+
{
111+
title: 'Chemistry',
112+
latex: '\\ce{ C6H5-CHO}'
113+
},
114+
{
115+
title: 'Chemistry',
116+
latex: '\\ce{X=Y#Z }'
117+
},
118+
{
119+
title: 'Chemistry',
120+
latex: '\\ce{A\\sbond B\\dbond C\\tbond D }'
121+
},
122+
{
123+
title: 'Chemistry',
124+
latex: '\\ce{ A\\bond{-}B\\bond{=}C\\bond{#}D}'
125+
},
126+
// {
127+
// title: 'Chemistry',
128+
// latex: '\\ce{ A\\bond{~}B\\bond{~-}C}'
129+
// },
130+
{
131+
title: 'Chemistry',
132+
latex: '\\ce{$x\\,$ Na(NH4)HPO4 ->[\\Delta](NaPO3)_{$x$} + $x\\,$ NH3 ^ + $x\\,$ H2O }'
133+
},
134+
{
135+
title: 'Chemistry',
136+
latex: '\\ce{ CO2 + C -> 2CO}'
137+
},
138+
{
139+
title: 'Chemistry',
140+
latex: '\\ce{CO2 + C <=> 2CO }'
141+
},
142+
{
143+
title: 'Chemistry',
144+
latex: '\\ce{H+ + OH- <=>> H2O }'
145+
},
146+
{
147+
title: 'Chemistry',
148+
latex: '\\ce{ CO2 + C ->[\\alpha][\\beta] 2CO } '
149+
},
150+
{
151+
title: 'Chemistry',
152+
latex: '\\ce{ $A$ ->C[+H2O] $B$ } '
153+
},
154+
{
155+
title: 'Chemistry',
156+
latex: '\\ce{ SO4^2- + Ba^2+ -> BaSO4 v } '
157+
},
158+
{
159+
title: 'Chemistry',
160+
latex: '\\ce{ Zn^2+ <=>[\\ce{+ 2OH-}][\\ce{+ 2H+}]$\\underset{\\text{amphoteric hydroxide}}{\\ce{Zn(OH)2 v}}$<=>C[+2OH-][{+ 2H+}]$\\underset{\\text{tetrahydroxozincate}}{\\ce{[Zn(OH)4]^2-}}$ }'
161+
},
162+
{
163+
title: 'Chemistry',
164+
latex: '\\ce{ Hg^2+ ->[\\ce{I-}]$\\underset{\\mathrm{red}}{\\ce{HgI2}}$->C[I-]$\\underset{\\mathrm{red}}{\\ce{[Hg^{II}I4]^2-}}$ } '
165+
},
166+
167+
168+
169+
101170
{
102171
title: 'Ordinary Symbols and letterShapeStyle',
103172
id: 'ordinary-1',
@@ -1256,12 +1325,12 @@ <h1>Examples</h1>
12561325
});
12571326
updateContent(mf);
12581327
}
1259-
// MathLive.renderMathInDocument({
1260-
// onError: (err) => {
1261-
// console.error(err.latex, err.code + ' ' + (err.arg ?? ''));
1262-
// },
1263-
// });
1264-
if (false) {
1328+
MathLive.renderMathInDocument({
1329+
onError: (err) => {
1330+
console.error(err.latex, err.code + ' ' + (err.arg ?? ''));
1331+
},
1332+
});
1333+
if (true) {
12651334
let error = [];
12661335
let savedMathfieldContent;
12671336
if (mf) {

src/addons/math-ml.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -872,26 +872,26 @@ function atomToMathML(atom, options): string {
872872
(atom.svgBelow || underscript)
873873
) {
874874
body = atom.body;
875-
} else if (overscript) {
875+
} else if (overscript && overscript.length > 0) {
876876
body = atom.body;
877877
if (atom.body?.[0]?.underscript) {
878878
underscript = atom.body[0].underscript;
879879
body = atom.body[0].body;
880880
} else if (
881-
atom?.body[0]?.type === 'first' &&
882-
atom?.body[1]?.underscript
881+
atom.body?.[0]?.type === 'first' &&
882+
atom.body?.[1]?.underscript
883883
) {
884884
underscript = atom.body[1].underscript;
885885
body = atom.body[1].body;
886886
}
887-
} else if (underscript) {
887+
} else if (underscript && underscript.length > 0) {
888888
body = atom.body;
889-
if (atom?.body?.[0]?.overscript) {
889+
if (atom.body?.[0]?.overscript) {
890890
overscript = atom.body[0].overscript;
891891
body = atom.body[0].body;
892892
} else if (
893-
atom?.body[0]?.type === 'first' &&
894-
atom?.body[1]?.overscript
893+
atom.body?.[0]?.type === 'first' &&
894+
atom.body?.[1]?.overscript
895895
) {
896896
overscript = atom.body[1].overscript;
897897
body = atom.body[1].body;
@@ -1081,6 +1081,13 @@ function atomToMathML(atom, options): string {
10811081
case 'space':
10821082
result += '&nbsp;';
10831083
break;
1084+
1085+
case 'msupsub':
1086+
break;
1087+
1088+
case 'phantom':
1089+
break;
1090+
10841091
default:
10851092
console.log(
10861093
'In conversion to MathML, unknown type : ' + atom.type

src/core/atom-overunder.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { isArray } from '../common/types';
2-
31
import { registerAtomType, decompose, Atom } from './atom-utils';
42
import {
53
Span,
@@ -34,15 +32,15 @@ registerAtomType('overunder', (context: Context, atom: Atom): Span[] => {
3432
let below: Span;
3533
if (atom.svgAbove) {
3634
above = makeSVGSpan(atom.svgAbove);
37-
} else if (atom.overscript) {
35+
} else if (atom.overscript && atom.overscript.length > 0) {
3836
above = makeSpan(
3937
decompose(annotationStyle, atom.overscript),
4038
context.mathstyle.adjustTo(annotationStyle.mathstyle)
4139
);
4240
}
4341
if (atom.svgBelow) {
4442
below = makeSVGSpan(atom.svgBelow);
45-
} else if (atom.underscript) {
43+
} else if (atom.underscript && atom.underscript.length > 0) {
4644
below = makeSpan(
4745
decompose(annotationStyle, atom.underscript),
4846
context.mathstyle.adjustTo(annotationStyle.mathstyle)
@@ -60,7 +58,7 @@ registerAtomType('overunder', (context: Context, atom: Atom): Span[] => {
6058
body,
6159
above,
6260
below,
63-
isSpanType(atom.type) ? atom.type : 'mord'
61+
isSpanType(atom.type) ? atom.type : 'mrel'
6462
);
6563
if (atom.superscript || atom.subscript) {
6664
result = atom.attachLimits(context, result, 0, 0);
@@ -85,7 +83,10 @@ function makeOverunderStack(
8583
type: SpanType
8684
): Span {
8785
// If nothing above and nothing below, nothing to do.
88-
if (!above && !below) return isArray(nucleus) ? makeSpan(nucleus) : nucleus;
86+
if (!above && !below) {
87+
return makeSpan(nucleus, 'op-over-under', type);
88+
// return isArray(nucleus) ? makeSpan(nucleus) : nucleus;
89+
}
8990

9091
let aboveShift = 0;
9192
let belowShift = 0;

src/core/atom-surd.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ registerAtomType('surd', (context: Context, atom: Atom): Span[] => {
2020
const mathstyle = context.mathstyle;
2121
// First, we do the same steps as in overline to build the inner group
2222
// and line
23-
console.assert(isArray(atom.body));
24-
const inner = decompose(context.cramp(), atom.body as Atom[]);
23+
console.assert(atom.body === null || isArray(atom.body));
24+
const inner =
25+
decompose(context.cramp(), atom.body as Atom[]) ?? makeSpan('');
2526
const ruleWidth =
2627
FONTMETRICS.defaultRuleThickness / mathstyle.sizeMultiplier;
2728
let phi = ruleWidth;

src/core/context.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export type ParseModePrivate =
1212
| 'dimen' // `'25mu'`, `'2pt'`
1313
| 'number' //`+/-12.56`
1414
| 'skip' // `'25mu plus 2em minus fiLll'`, `'2pt'`
15-
| 'string'
15+
| 'string' // delimiter is a non-literal token (e.g. `<}>` `<$>`, etc
16+
| 'balanced-string' // Delimiter is a balanced closing brace
1617
);
1718

1819
export interface ContextInterface {

src/core/definitions-extensible-symbols.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ defineFunction(
8989
'[:auto]{:auto}',
9090
null,
9191
(name, args) => {
92+
// The overscript is optional, i.e. `\xtofrom` is valid
93+
let overscript: Atom[] | null = args[1] as Atom[];
94+
if (overscript?.length === 0) {
95+
overscript = null;
96+
}
9297
return {
9398
type: 'overunder',
9499

@@ -98,7 +103,7 @@ defineFunction(
98103
// Set the "svgBody" to the name of a SVG object (which is the same
99104
// as the command name)
100105
svgBody: name.slice(1),
101-
overscript: args[1],
106+
overscript: overscript,
102107
underscript: args[0],
103108

104109
skipBoundary: true,

0 commit comments

Comments
 (0)