php将word中的omath转成mathml
获取word的xml后,可用以下方式将所有的公式都提取并转换成mathml格式,再根据 自己的业务场景进行转换成latex、公式图片即可,mathml可用mathjax直接渲染。
修改
PhpWord/Shared/XMLReader.php
中的getDomFromZip
/** * Get DOMDocument from ZipArchive * * @param string $zipFile * @param string $xmlFile * @throws \Exception * @return \DOMDocument|false */ public function getDomFromZip($zipFile, $xmlFile) { if (file_exists($zipFile) === false) { throw new \Exception('Cannot find archive file.'); } $zip = new \ZipArchive(); $zip->open($zipFile); $content = $zip->getFromName($xmlFile); $zip->close(); if ($content === false) { return false; } if($xmlFile === 'word/document.xml'){ // 字符串替换,将公式替换成$Math$ $oMathParaPreg = '/<m:oMathPara>.*?<m:oMath>([\s|\S]*?)<\/m:oMath>.*?<\/m:oMathPara>/'; $content = preg_replace($oMathParaPreg, '<m:oMath>$1</m:oMath>', $content); // 这里可以随便替换成别的,我这边是用的$Math$ $content = preg_replace('/<m:oMath>[\s|\S]*?<\/m:oMath>/', '<w:r><w:t>$Math$</w:t></w:r>', $content); } return $this->getDomFromString($content); }
获取
word
对应的xml
内容
public function toXML($source)
{
$zip = new \ZipArchive();
$zip->open($source);
return $zip->getFromName("word/document.xml");
}
- 转换公式,插入到html中
public function handlerMath($xml,$html)
{
// 解析xml
$xml_document_idx = stripos($xml, '<w:body>');
$xml_document = substr($xml, 0, $xml_document_idx);
$mml_arrs= [];
libxml_disable_entity_loader(false);
// 正则匹配提取所有的公式,转化成mathml
preg_replace_callback('/(<m:oMath>)([\s\S]*?)(<\/m:oMath>)/', function ($matches) use ($xml_document,&$mml_arrs) {
$mml =$xml_document.'<w:body><m:oMathPara>' . $matches[0] . '</m:oMathPara></w:body></w:document>';
$domDocument = new DOMDocument();
$domDocument->loadXML($mml);
$numberings = $domDocument->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'body');
$numberings = $numberings->item(0);
$xsl = new DOMDocument();
$xsl->load('OMML2MML.XSL');
$processor = new XSLTProcessor();
$processor->importStyleSheet($xsl);
$omml = $processor->transformToXML($numberings);
$omml = str_replace('<?xml version="1.0" encoding="UTF-8"?>', '', $omml);
$omml = str_replace('xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"', '', $omml);
$omml = str_replace("mml:", '', $omml);
$omml = str_replace("\n", '', $omml);
$mml_arrs[] = $omml;
return "";
}, $xml);
// 如果有公式,就替换到HTML中
if($mml_arrs){
$preg = '/\$Math\$/';
preg_match_all($preg, $html, $htmlTargets);
if($htmlTargets && $htmlTargets[0]){
$max = count($mml_arrs);
if($max > count($htmlTargets[0])){
$max = count($htmlTargets[0]);
}
$disk = Storage::disk('h5_res');
$appPath = $this->getResPath();
$imgPath = $disk->path($appPath);
for ($i=0; $i <$max; $i++) {
$math = $mml_arrs[$i];
if($this->mathType == 'mathml'){
$html = $this->str_replace_once($htmlTargets[0][$i],$math,$html);
}else if($this->mathType == 'latex'){
// 根据业务转换成latex,这里没找到方法,让我领导用java给我写了一个mathml转latex的方法,哈哈哈
$latex = mathmlToLatex($math);
$html = $this->str_replace_once($htmlTargets[0][$i],'[latex]'.$latex.'[/latex]',$html);
}else if($this->mathType == 'img'){
// 根据业务转换成图片,这里没找到方法,让我领导用java给我写了一个mathml转图片的方法
// 要根据前端展示的场景,调整生成图片的大小
$imgName = 'math_'.$i.'.png';
$img = mathML2Img($imgPath,'math',$imgName,300,$math,15,'word');
$html = $this->str_replace_once($htmlTargets[0][$i],$img,$html);
}
}
}
}
return $html;
}
/**
* 将字符串中第一个匹配到的字符串修改为其他字符串
* 别的方法应该也是可以的,如用Str::replaceFirst()
*/
private function str_replace_once($needle, $replace, $haystack)
{
// Looks for the first occurence of $needle in $haystack
// and replaces it with $replace.
$pos = strpos($haystack, $needle);
if ($pos === false) {
return $haystack;
}
return substr_replace($haystack, $replace, $pos, strlen($needle));
}
转换完成~,如果是mathml
和latex
的话,可以在前端使用MathJax
渲染。
对了,这个word 解析还有个问题就是,那个插件没有解析公式,公式转成mathml后还有一个问题是再原样插回到对应的位置。
目前我这边的解决方案是在插件中将公式先替换成一个字符串,然后在解析出所有的公式后替换回去