因项目中需要导入word文档,但其中存在的公式,系统不支持,但又需要导入进系统,之前都是手动截图后再重新插进去,关键时候上千章试卷要进行截图也很耗时间,所以研究了一下转换方式
首先通过将docx后缀改成zip解压后可以发现,公式分成两部分,一部分是.wmf的矢量图文件,一个是objectbin文件,但实际通过画图打开矢量图后发现公式已经可以完整显示出来了,那需要解决的关键部分就是.wmf转.png文件就行了。最开始采用了java来做,但发现通过wmf转svg再转png,一些符号会显示错误,例如微积分符号,可能和格式或者转换方式有关,故放弃。偶然间通过画图将矢量图wmf另存为png时发现所有符号都是显示正确的,那么C#中肯定存在可以完成转换的方式,果然,翻阅一些资料后发现wmf可以通过图元文件写入位图,最后再将图像信息存入文件中就可以很方便的完成转换了,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml;
namespace Project1
{
class Class2
{
static void Main(string[] args)
{
String FileName = args[0];
using (System.Drawing.Imaging.Metafile img = new System.Drawing.Imaging.Metafile(FileName))
{
System.Drawing.Imaging.MetafileHeader header = img.GetMetafileHeader();
float scale = header.DpiX / 96f;
using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap((int)(scale * img.Width / header.DpiX * 100), (int)(scale * img.Height / header.DpiY * 100)))
{
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap))
{
g.Clear(System.Drawing.Color.White);
g.ScaleTransform(scale, scale);
g.DrawImage(img, 0, 0);
}
bitmap.Save(@args[1], System.Drawing.Imaging.ImageFormat.Png);
}
}
Console.WriteLine("转换完成");
}
}
}
再完成转换后,打算通过python-docx提取所有图片转换后再存入新的docx文件中来实现转换,但踩了一个很大的坑,因为python-docx中取出的图片数量和实际有的公式数量并不匹配,主要是一些C2和C1之类的小字符。之后再用pydocx转成html后发现,所有图片信息都以base64的形式储存在img标签的src中,并且数量与实际公式的数量是一致的,那么解决方式就确定了
先说一下个人的解决思路,java应该可以采用poi完成同样的操作:
1.先将docx文件转为html,其中公式或者图片按照base64编码直接写入html中(因如果不使用此种方式,直接通过 python-docx提取图片数量可能会小于实际含有的图片数量 导致转换失败)
2.提取html中的所有img标签的内容,将src重新写入文件中,cmd调用c#程序直接转为png后转成base64编码,替换回html中
3.将html转化为docx文件,就是最后的结果了,pydocx转换的html有如下规律
1)所有内容都在body中
2)paragraph对应为p标签,遇到直接再新建的文档中添加paragraph即可
3)所有文字以run类型直接添加到paragraph
4)遇到图片直接再run中添加图片即可,注意大小,大小的计算方式(获取html中的width和height,并除以80转为Inches就是较合适的大小)
5)table处理较为特别,table是不会存在于p标签中,而是与p标签并列,遇到table标签后,获取tr和td的数量并创建table,通过循环遍历方式获取cell, 再cell中通过添加paragraph来插入内容或图片(处理与上述标签一致)
tag的处理如下代码所示:
def handle_tag(tag,parent,photo_base_path):
if type(tag) == bs4.element.NavigableString:
content=tag.replace("\n", "")
if content=="":
pass
else:
parent.add_run(content)
elif type(tag)==bs4.element.Tag:
if tag.name == "p":
p = parent.add_paragraph()
# 遍历
for child in tag.contents:
handle_tag(child,p,photo_base_path)
elif tag.name == "img":
#按parent类型是否是进行区分
run = parent.add_run()
if tag["width"].endswith("pt"):
width = float(tag["width"].replace("pt", ""))
elif tag["width"].endswith("px"):
width = float(tag["width"].replace("px", ""))
if tag["height"].endswith("pt"):
height = float(tag["height"].replace("pt", ""))
elif tag["height"].endswith("px"):
height = float(tag["height"].replace("px", ""))
resultdata,path=savephoto(tag["src"], photo_base_path)
run.add_picture(path, width=Inches(width / 80), height=Inches(height / 80))
elif tag.name=="table":
#获取table下所有tr标签和每一行的td标签
tr=tag.find_all("tr")
if len(tr)!=0:
td = tr[0].find_all("td")
table = parent.add_table(rows=len(tr), cols=len(td))
startrowindex=0
for row in tr:
td = row.find_all("td")
startcolumnindex=0
for column in td:
cell=table.cell(startrowindex,startcolumnindex)
p=cell.add_paragraph()
for child in column.contents:
handle_tag(child, p,photo_base_path)
startcolumnindex=startcolumnindex+1
startrowindex=startrowindex+1
else:
pass
具体代码见github:https://github.com/peoplhappy/wordformulatopng 就不详细在这里说了