因項目中需要導入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 就不詳細在這裏說了