Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4188923
MhchemTexify.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
MhchemTexify.php
View Options
<?php
/**
* Copyright (c) 2023 Johannes Stegmüller
*
* This file is a port of mhchemParser originally authored by Martin Hensel in javascript/typescript.
* The original license for this software can be found in the accompanying LICENSE.mhchemParser-ts.txt file.
*/
namespace
MediaWiki\Extension\Math\WikiTexVC\Mhchem
;
use
MediaWiki\Extension\Math\WikiTexVC\MHChem\MhchemUtil
as
MU
;
use
RuntimeException
;
/**
* Takes MhchemParser output and convert it to TeX
*
* Functionality is the same as mhchemTexify class at ~line 1505 in mhchemParser.js
* in mhchemParser by Martin Hensel.
*
* @author Johannes Stegmüller
* @license GPL-2.0-or-later
*/
class
MhchemTexify
{
/** @var bool optimize the output TeX for WikiTexVC */
private
bool
$optimizeForTexVC
;
/**
* Takes MhchemParser output and convert it to TeX
* @param bool $optimizeForTexVC optimizes the output for WikiTexVC grammar by
* wrapping dimensions for some TeX commands in curly brackets.
*/
public
function
__construct
(
bool
$optimizeForTexVC
=
false
)
{
$this
->
optimizeForTexVC
=
$optimizeForTexVC
;
}
/**
* @param array|mixed $input
* @param bool $addOuterBraces
*/
public
function
go
(
$input
,
bool
$addOuterBraces
):
string
{
if
(
!
MhchemUtil
::
issetJS
(
$input
)
)
{
return
""
;
}
$res
=
""
;
$cee
=
false
;
for
(
$i
=
0
;
$i
<
count
(
$input
);
$i
++
)
{
$inputI
=
$input
[
$i
];
if
(
is_string
(
$inputI
)
)
{
$res
.=
$inputI
;
}
else
{
$res
.=
self
::
go2
(
$inputI
);
if
(
$inputI
[
"type_"
]
===
'1st-level escape'
)
{
$cee
=
true
;
}
}
}
if
(
$addOuterBraces
&&
!
$cee
&&
$res
)
{
$res
=
"{"
.
$res
.
"}"
;
}
return
$res
;
}
private
function
goInner
(
array
$input
):
string
{
return
self
::
go
(
$input
,
false
);
}
private
function
strReplaceFirst
(
string
$search
,
string
$replace
,
string
$subject
):
string
{
return
implode
(
$replace
,
explode
(
$search
,
$subject
,
2
)
);
}
private
function
go2
(
array
$buf
):
string
{
switch
(
$buf
[
"type_"
]
)
{
case
'chemfive'
:
$res
=
""
;
$b5
=
[
"a"
=>
self
::
goInner
(
$buf
[
"a"
]
),
"b"
=>
self
::
goInner
(
$buf
[
"b"
]
),
"p"
=>
self
::
goInner
(
$buf
[
"p"
]
),
"o"
=>
self
::
goInner
(
$buf
[
"o"
]
),
"q"
=>
self
::
goInner
(
$buf
[
"q"
]
),
"d"
=>
self
::
goInner
(
$buf
[
"d"
]
)
];
if
(
MU
::
issetJS
(
$b5
[
"a"
]
)
)
{
if
(
preg_match
(
"/^[+
\-
]/"
,
$b5
[
"a"
]
)
)
{
$b5
[
"a"
]
=
"{"
.
$b5
[
"a"
]
.
"}"
;
}
$res
.=
$b5
[
"a"
]
.
"
\\
,"
;
}
if
(
MU
::
issetJS
(
$b5
[
"b"
]
)
||
MU
::
issetJS
(
$b5
[
"p"
]
)
)
{
$res
.=
"{
\\
vphantom{A}}"
;
$res
.=
"^{
\\
hphantom{"
.
(
$b5
[
"b"
]
)
.
"}}_{
\\
hphantom{"
.
(
$b5
[
"p"
]
)
.
"}}"
;
$res
.=
!
$this
->
optimizeForTexVC
?
"
\\
mkern-1.5mu"
:
"
\\
mkern{-1.5mu}"
;
$res
.=
"{
\\
vphantom{A}}"
;
$res
.=
"^{
\\
smash[t]{
\\
vphantom{2}}
\\
llap{"
.
(
$b5
[
"b"
]
)
.
"}}"
;
$res
.=
"_{
\\
vphantom{2}
\\
llap{
\\
smash[t]{"
.
(
$b5
[
"p"
]
)
.
"}}}"
;
}
if
(
MU
::
issetJS
(
$b5
[
"o"
]
)
)
{
if
(
preg_match
(
"/^[+
\-
]/"
,
$b5
[
"o"
]
)
)
{
$b5
[
"o"
]
=
"{"
.
$b5
[
"o"
]
.
"}"
;
}
$res
.=
$b5
[
"o"
];
}
if
(
isset
(
$buf
[
"dType"
]
)
&&
$buf
[
"dType"
]
===
'kv'
)
{
if
(
MU
::
issetJS
(
$b5
[
"d"
]
)
||
MU
::
issetJS
(
$b5
[
"q"
]
)
)
{
$res
.=
"{
\\
vphantom{A}}"
;
}
if
(
MU
::
issetJS
(
$b5
[
"d"
]
)
)
{
$res
.=
"^{"
.
$b5
[
"d"
]
.
"}"
;
}
if
(
MU
::
issetJS
(
$b5
[
"q"
]
)
)
{
$res
.=
"_{
\\
smash[t]{"
.
$b5
[
"q"
]
.
"}}"
;
}
}
elseif
(
MU
::
issetJS
(
$buf
[
"dType"
]
??
null
)
&&
$buf
[
"dType"
]
===
'oxidation'
)
{
if
(
MU
::
issetJS
(
$b5
[
"d"
]
)
)
{
$res
.=
"{
\\
vphantom{A}}"
;
$res
.=
"^{"
.
$b5
[
"d"
]
.
"}"
;
}
if
(
MU
::
issetJS
(
$b5
[
"q"
]
)
)
{
$res
.=
"{
\\
vphantom{A}}"
;
$res
.=
"_{
\\
smash[t]{"
.
$b5
[
"q"
]
.
"}}"
;
}
}
else
{
if
(
MU
::
issetJS
(
$b5
[
"q"
]
)
)
{
$res
.=
"{
\\
vphantom{A}}"
;
$res
.=
"_{
\\
smash[t]{"
.
$b5
[
"q"
]
.
"}}"
;
}
if
(
MU
::
issetJS
(
$b5
[
"d"
]
)
)
{
$res
.=
"{
\\
vphantom{A}}"
;
$res
.=
"^{"
.
$b5
[
"d"
]
.
"}"
;
}
}
break
;
case
'roman numeral'
:
case
'rm'
:
$res
=
"
\\
mathrm{"
.
$buf
[
"p1"
]
.
"}"
;
break
;
case
'text'
:
if
(
preg_match
(
"/[
\^
_]/"
,
$buf
[
"p1"
]
)
)
{
$buf
[
"p1"
]
=
self
::
strReplaceFirst
(
"-"
,
"
\\
text{-}"
,
self
::
strReplaceFirst
(
" "
,
"~"
,
$buf
[
"p1"
]
)
);
$res
=
"
\\
mathrm{"
.
$buf
[
"p1"
]
.
"}"
;
}
else
{
$res
=
"
\\
text{"
.
$buf
[
"p1"
]
.
"}"
;
}
break
;
case
'state of aggregation'
:
$res
=
(
!
$this
->
optimizeForTexVC
?
"
\\
mskip2mu "
:
"
\\
mskip{2mu} "
)
.
self
::
goInner
(
$buf
[
"p1"
]
);
break
;
case
'state of aggregation subscript'
:
$res
=
(
!
$this
->
optimizeForTexVC
?
"
\\
mskip1mu "
:
"
\\
mskip{1mu} "
)
.
self
::
goInner
(
$buf
[
"p1"
]
);
break
;
case
'bond'
:
$res
=
self
::
getBond
(
$buf
[
"kind_"
]
);
if
(
!
$res
)
{
throw
new
RuntimeException
(
"MhchemErrorBond: mhchem Error. Unknown bond type ("
.
$buf
[
"kind_"
]
.
")"
);
}
break
;
case
'frac'
:
$c
=
"
\\
frac{"
.
$buf
[
"p1"
]
.
"}{"
.
$buf
[
"p2"
]
.
"}"
;
$res
=
"
\\
mathchoice{
\\
textstyle"
.
$c
.
"}{"
.
$c
.
"}{"
.
$c
.
"}{"
.
$c
.
"}"
;
break
;
case
'pu-frac'
:
$d
=
"
\\
frac{"
.
self
::
goInner
(
$buf
[
"p1"
]
)
.
"}{"
.
self
::
goInner
(
$buf
[
"p2"
]
)
.
"}"
;
$res
=
"
\\
mathchoice{
\\
textstyle"
.
$d
.
"}{"
.
$d
.
"}{"
.
$d
.
"}{"
.
$d
.
"}"
;
break
;
case
'1st-level escape'
:
case
'tex-math'
:
$res
=
$buf
[
"p1"
]
.
" "
;
break
;
case
'frac-ce'
:
$res
=
"
\\
frac{"
.
self
::
goInner
(
$buf
[
"p1"
]
)
.
"}{"
.
self
::
goInner
(
$buf
[
"p2"
]
)
.
"}"
;
break
;
case
'overset'
:
$res
=
"
\\
overset{"
.
self
::
goInner
(
$buf
[
"p1"
]
)
.
"}{"
.
self
::
goInner
(
$buf
[
"p2"
]
)
.
"}"
;
break
;
case
'underset'
:
$res
=
"
\\
underset{"
.
self
::
goInner
(
$buf
[
"p1"
]
)
.
"}{"
.
self
::
goInner
(
$buf
[
"p2"
]
)
.
"}"
;
break
;
case
'underbrace'
:
$res
=
"
\\
underbrace{"
.
self
::
goInner
(
$buf
[
"p1"
]
)
.
"}_{"
.
self
::
goInner
(
$buf
[
"p2"
]
)
.
"}"
;
break
;
case
'color'
:
$res
=
"{
\\
color{"
.
$buf
[
"color1"
]
.
"}{"
.
self
::
goInner
(
$buf
[
"color2"
]
)
.
"}}"
;
break
;
case
'color0'
:
$res
=
"
\\
color{"
.
$buf
[
"color"
]
.
"}"
;
break
;
case
'arrow'
:
$b6
=
[
"rd"
=>
self
::
goInner
(
$buf
[
"rd"
]
),
"rq"
=>
self
::
goInner
(
$buf
[
"rq"
]
)
];
$arrow
=
self
::
getArrow
(
$buf
[
"r"
]
);
if
(
MU
::
issetJS
(
$b6
[
"rd"
]
)
||
MU
::
issetJS
(
$b6
[
"rq"
]
)
)
{
if
(
$buf
[
"r"
]
===
"<=>"
||
$buf
[
"r"
]
===
"<=>>"
||
$buf
[
"r"
]
===
"<<=>"
||
$buf
[
"r"
]
===
"<-->"
)
{
$arrow
=
"
\\
long"
.
$arrow
;
if
(
MU
::
issetJS
(
$b6
[
"rd"
]
)
)
{
$arrow
=
"
\\
overset{"
.
$b6
[
"rd"
]
.
"}{"
.
$arrow
.
"}"
;
}
if
(
MU
::
issetJS
(
$b6
[
"rq"
]
)
)
{
if
(
$buf
[
"r"
]
===
"<-->"
)
{
$arrow
=
!
$this
->
optimizeForTexVC
?
"
\\
underset{
\\
lower2mu{"
.
$b6
[
"rq"
]
.
"}}{"
.
$arrow
.
"}"
:
"
\\
underset{
\\
lower{2mu}{"
.
$b6
[
"rq"
]
.
"}}{"
.
$arrow
.
"}"
;
}
else
{
$arrow
=
!
$this
->
optimizeForTexVC
?
"
\\
underset{
\\
lower6mu{"
.
$b6
[
"rq"
]
.
"}}{"
.
$arrow
.
"}"
:
"
\\
underset{
\\
lower{6mu}{"
.
$b6
[
"rq"
]
.
"}}{"
.
$arrow
.
"}"
;
}
}
$arrow
=
" {}
\\
mathrel{"
.
$arrow
.
"}{} "
;
}
else
{
if
(
MU
::
issetJS
(
$b6
[
"rq"
]
)
)
{
$arrow
.=
"[{"
.
$b6
[
"rq"
]
.
"}]"
;
}
$arrow
.=
"{"
.
$b6
[
"rd"
]
.
"}"
;
$arrow
=
" {}
\\
mathrel{
\\
x"
.
$arrow
.
"}{} "
;
}
}
else
{
$arrow
=
" {}
\\
mathrel{
\\
long"
.
$arrow
.
"}{} "
;
}
$res
=
$arrow
;
break
;
case
'operator'
:
$res
=
self
::
getOperator
(
$buf
[
"kind_"
]
);
break
;
default
:
$res
=
null
;
}
if
(
$res
!==
null
)
{
return
$res
;
}
switch
(
$buf
[
"type_"
]
)
{
case
'space'
:
$res
=
" "
;
break
;
case
'tinySkip'
:
$res
=
!
$this
->
optimizeForTexVC
?
'
\\
mkern2mu'
:
'
\\
mkern{2mu}'
;
break
;
case
'pu-space-1'
:
case
'entitySkip'
:
$res
=
"~"
;
break
;
case
'pu-space-2'
:
$res
=
!
$this
->
optimizeForTexVC
?
"
\\
mkern3mu "
:
"
\\
mkern{3mu} "
;
break
;
case
'1000 separator'
:
$res
=
!
$this
->
optimizeForTexVC
?
"
\\
mkern2mu "
:
"
\\
mkern{2mu} "
;
break
;
case
'commaDecimal'
:
$res
=
"{,}"
;
break
;
case
'comma enumeration L'
:
$res
=
"{"
.
$buf
[
"p1"
]
.
"}"
.
(
!
$this
->
optimizeForTexVC
?
"
\\
mkern6mu "
:
"
\\
mkern{6mu} "
);
break
;
case
'comma enumeration M'
:
$res
=
"{"
.
$buf
[
"p1"
]
.
"}"
.
(
!
$this
->
optimizeForTexVC
?
"
\\
mkern3mu "
:
"
\\
mkern{3mu} "
);
break
;
case
'comma enumeration S'
:
$res
=
"{"
.
$buf
[
"p1"
]
.
"}"
.
(
!
$this
->
optimizeForTexVC
?
"
\\
mkern1mu "
:
"
\\
mkern{1mu} "
);
break
;
case
'hyphen'
:
$res
=
"
\\
text{-}"
;
break
;
case
'addition compound'
:
$res
=
"
\\
,{
\\
cdot}
\\
,"
;
break
;
case
'electron dot'
:
$res
=
!
$this
->
optimizeForTexVC
?
"
\\
mkern1mu
\\
bullet
\\
mkern1mu "
:
"
\\
mkern{1mu}
\\
bullet
\\
mkern{1mu} "
;
break
;
case
'KV x'
:
$res
=
"{
\\
times}"
;
break
;
case
'prime'
:
$res
=
"
\\
prime "
;
break
;
case
'cdot'
:
$res
=
"
\\
cdot "
;
break
;
case
'tight cdot'
:
$res
=
!
$this
->
optimizeForTexVC
?
"
\\
mkern1mu{
\\
cdot}
\\
mkern1mu "
:
"
\\
mkern{1mu}{
\\
cdot}
\\
mkern{1mu} "
;
break
;
case
'times'
:
$res
=
"
\\
times "
;
break
;
case
'circa'
:
$res
=
"{
\\
sim}"
;
break
;
case
'^'
:
$res
=
"uparrow"
;
break
;
case
'v'
:
$res
=
"downarrow"
;
break
;
case
'ellipsis'
:
$res
=
"
\\
ldots "
;
break
;
case
'/'
:
$res
=
"/"
;
break
;
case
' / '
:
$res
=
"
\\
,/
\\
,"
;
break
;
default
:
throw
new
RuntimeException
(
"MhchemBugT: mhchem bug T. Please report."
);
}
return
$res
;
}
private
function
getArrow
(
string
$a
):
string
{
switch
(
$a
)
{
case
"
\u
2192"
:
case
"
\u
27F6"
:
case
"->"
:
return
"rightarrow"
;
case
"<-"
:
return
"leftarrow"
;
case
"<->"
:
return
"leftrightarrow"
;
case
"<-->"
:
return
"leftrightarrows"
;
case
"
\u
21CC"
:
case
"<=>"
:
return
"rightleftharpoons"
;
case
"<=>>"
:
return
"Rightleftharpoons"
;
case
"<<=>"
:
return
"Leftrightharpoons"
;
default
:
throw
new
RuntimeException
(
"MhchemBugT: mhchem bug T. Please report."
);
}
}
private
function
getBond
(
string
$a
):
string
{
switch
(
$a
)
{
case
"1"
:
case
"-"
:
return
"{-}"
;
case
"2"
:
case
"="
:
return
"{=}"
;
case
"3"
:
case
"#"
:
return
"{
\\
equiv}"
;
case
"~"
:
return
"{
\\
tripledash}"
;
case
"~-"
:
return
!
$this
->
optimizeForTexVC
?
"{
\\
rlap{
\\
lower.1em{-}}
\\
raise.1em{
\\
tripledash}}"
:
"{
\\
rlap{
\\
lower{.1em}{-}}
\\
raise{.1em}{
\\
tripledash}}"
;
case
"~--"
:
case
"~="
:
return
!
$this
->
optimizeForTexVC
?
"{
\\
rlap{
\\
lower.2em{-}}
\\
rlap{
\\
raise.2em{
\\
tripledash}}-}"
:
"{
\\
rlap{
\\
lower{.2em}{-}}
\\
rlap{
\\
raise{.2em}{
\\
tripledash}}-}"
;
case
"-~-"
:
return
!
$this
->
optimizeForTexVC
?
"{
\\
rlap{
\\
lower.2em{-}}
\\
rlap{
\\
raise.2em{-}}
\\
tripledash}"
:
"{
\\
rlap{
\\
lower{.2em}{-}}
\\
rlap{
\\
raise{.2em}{-}}
\\
tripledash}"
;
case
"..."
:
return
"{{
\\
cdot}{
\\
cdot}{
\\
cdot}}"
;
case
"...."
:
return
"{{
\\
cdot}{
\\
cdot}{
\\
cdot}{
\\
cdot}}"
;
case
"->"
:
return
"{
\\
rightarrow}"
;
case
"<-"
:
return
"{
\\
leftarrow}"
;
case
"<"
:
return
"{<}"
;
case
">"
:
return
"{>}"
;
default
:
throw
new
RuntimeException
(
"MhchemBugT: mhchem bug T. Please report."
);
}
}
private
function
getOperator
(
string
$a
):
string
{
switch
(
$a
)
{
case
"+"
:
return
" {}+{} "
;
case
"-"
:
return
" {}-{} "
;
case
"="
:
return
" {}={} "
;
case
"<"
:
return
" {}<{} "
;
case
">"
:
return
" {}>{} "
;
case
"<<"
:
return
" {}
\\
ll{} "
;
case
">>"
:
return
" {}
\\
gg{} "
;
case
"
\\
pm"
:
return
" {}
\\
pm{} "
;
case
"$
\\
approx$"
:
case
"
\\
approx"
:
return
" {}
\\
approx{} "
;
case
"(v)"
:
case
"v"
:
return
"
\\
downarrow{} "
;
case
"(^)"
:
case
"^"
:
return
"
\\
uparrow{} "
;
default
:
throw
new
RuntimeException
(
"MhchemBugT: mhchem bug T. Please report."
);
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 20 2026, 16:43 (11 w, 5 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
87/05/219420da6a386680094600aac8b2
Default Alt Text
MhchemTexify.php (11 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment