环境
from xml.dom import minidom
目标
从一个word文档保留原格式复制到另一个word文档中,直接使用xml.dom去操作word文档,而不使用python-docx库。
Word格式
文件格式
word的docx本质是个压缩包,其中最主要的文档相关的内容使用xml格式存储的。当然绝大多数的文件都是以xml格式存在。
文件结构
├─1
│ │ [Content_Types].xml 包含了word要使用到的扩展
│ │
│ ├─docProps
│ │ app.xml
│ │ core.xml 包含文件作者
│ │
│ ├─word
│ │ │ document.xml word文档中文字表格图片等详细内容,也是编辑word的主要文件
│ │ │ endnotes.xml
│ │ │ fontTable.xml
│ │ │ footnotes.xml
│ │ │ settings.xml
│ │ │ styles.xml
│ │ │ webSettings.xml
│ │ │
│ │ ├─media 媒体文件夹中包含了word中需要用的的媒体文件
│ │ │ image1.gif
│ │ │
│ │ ├─theme 主题文件夹
│ │ │ theme1.xml
│ │ │
│ │ └─_rels 里面包含着document.xml引用的关系,将各个文件用rId联系
│ │ document.xml.rels 里面包含着图片的引用关系,如果你修改的图片不显示大概这里没有改
│ │
│ └─_rels
│ .rels
├─2
│ │ [Content_Types].xml
│ │
│ ├─docProps
│ │ app.xml
│ │ core.xml
│ │
│ ├─word
│ │ │ document.xml
│ │ │ endnotes.xml
│ │ │ fontTable.xml
│ │ │ footnotes.xml
│ │ │ settings.xml
│ │ │ styles.xml
│ │ │ webSettings.xml
│ │ │
│ │ ├─theme
│ │ │ theme1.xml
│ │ │
│ │ └─_rels
│ │ document.xml.rels
│ │
│ └─_rels
│ .rels
- <w:p> 表示一个段落
- <w:r> 表示一个样式串,指明它包括的文本的显示样式
- <w:t> 表示真正的文本内容
- <w:tbl> 表示一个表格
- <w:tr> 表示表格一行
- <w:tc> 表示一个cell,我立理解是单元格
其中文字被包含在<w:t>的节点下应该用firstchild来指向
问题复现
当我去从一个word文档的文字,图片以及公式保留格式复制到另一个word文档中,我首先是将源文档中document.xml相关节点复制到目标文档中。效果是一切ok的,除了照片无法显示,这时我就去查看源文件信息,发现还有media文件夹里的内容没有复制过去。于是复制图片到目标文档的对应目录中,结果还是无法显示,并且这时打开word文档会出现无法读取的内容。
于是两个问题需要解决
- 如何正确显示图片?
- 如何解决word报错?
正确显示图片
以下是doucument.xml里图片相关信息
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0"
wp14:anchorId="77FAF658" wp14:editId="331AEBE1">
<wp:extent cx="2842786" cy="2132374" />
<wp:effectExtent l="0" t="0" r="0" b="1270" />
<wp:docPr id="1" name="图片 1" />
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
noChangeAspect="1" />
</wp:cNvGraphicFramePr>
<a:graphic
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData
uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic
xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="Picture 1" />
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1"
noChangeArrowheads="1" />
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId6">
<a:extLst>
<a:ext
uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi
xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main"
val="0" />
</a:ext>
</a:extLst>
</a:blip>
<a:srcRect />
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0" />
<a:ext cx="2847489" cy="2135902" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
<a:noFill />
<a:ln>
<a:noFill />
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
棘手的问题在于这个,图片的插入是通过这个id实现的
<a:blip r:embed="rId6">
<Relationship Id="rId6"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Target="media/image1.gif" />
这个是document.xml.rels文件对应的
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId8"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
Target="theme/theme1.xml" />
<Relationship Id="rId3"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings"
Target="webSettings.xml" />
<Relationship Id="rId7"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable"
Target="fontTable.xml" />
<Relationship Id="rId2"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"
Target="settings.xml" />
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
Target="styles.xml" />
<Relationship Id="rId6"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Target="media/image1.gif" />
<Relationship Id="rId5"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes"
Target="endnotes.xml" />
<Relationship Id="rId4"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"
Target="footnotes.xml" />
</Relationships>
这个是文档2的
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId3"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings"
Target="webSettings.xml" />
<Relationship Id="rId7"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
Target="theme/theme1.xml" />
<Relationship Id="rId2"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"
Target="settings.xml" />
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
Target="styles.xml" />
<Relationship Id="rId6"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable"
Target="fontTable.xml" />
<Relationship Id="rId5"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes"
Target="endnotes.xml" />
<Relationship Id="rId4"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"
Target="footnotes.xml" />
</Relationships>
这个rId并没有自己想的这么简单,主要是这个rId安排是按顺序的,整个页面的顺序。因此当从一个文件的图片复制到另一个文档中,需要考虑到rId冲突的问题。
解决思路
- 首先获取文档中的rid
- 再去document.xml.rels中找到源文件的信息比如Type,Target
- 然后我们要再去读取目标文件的document.xml.rels获取当前最高的rId数避免冲突
- 然后将文档中的rid相关信息加入到document.xml.rels中
- 然后修改文档中的rID添加到目标文档中
无法读取的报错
可疑点
[Content_Types].xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="gif" ContentType="image/gif" />
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
<Default Extension="xml" ContentType="application/xml" />
<Override PartName="/word/document.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" />
<Override PartName="/word/styles.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml" />
<Override PartName="/word/settings.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" />
<Override PartName="/word/webSettings.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml" />
<Override PartName="/word/footnotes.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" />
<Override PartName="/word/endnotes.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml" />
<Override PartName="/word/fontTable.xml"
ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" />
<Override PartName="/word/theme/theme1.xml"
ContentType="application/vnd.openxmlformats-officedocument.theme+xml" />
<Override PartName="/docProps/core.xml"
ContentType="application/vnd.openxmlformats-package.core-properties+xml" />
<Override PartName="/docProps/app.xml"
ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" />
</Types>
<Default Extension="gif" ContentType="image/gif" />
是不是要加入这个模块来实现gif的读取,要不然word读不了那张gif?
经过手动输入这行文档就不会报错了,推测是因为没有加入gif对应的插件,文件word读取不出gif所以报错。因此我只需要在[Content_Types].xml中引入需要的扩展信息才不会报错。