123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import os
- from datetime import datetime
- from bs4 import BeautifulSoup
- from docx import Document
- from docx.shared import Pt
- from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
- from docx.shared import RGBColor
- from docx.oxml.ns import qn
- from docx.oxml import OxmlElement
- from DCbackend.settings import MEDIA_ROOT
- # 添加标题和书签
- def add_title_with_bookmark(doc, text, style, bookmark_id):
- paragraph = doc.add_paragraph(text, style=style)
- run = paragraph.add_run()
- tag = run._r
- start = OxmlElement('w:bookmarkStart')
- start.set(qn('w:id'), str(bookmark_id))
- start.set(qn('w:name'), text)
- tag.append(start)
- tr = OxmlElement('w:r')
- tr.text = ''
- tag.append(tr)
- end = OxmlElement('w:bookmarkEnd')
- end.set(qn('w:id'), str(bookmark_id))
- end.set(qn('w:name'), text)
- tag.append(end)
- # 获取文档文件名称
- def getDocxFileName():
- date = datetime.now().strftime("%Y%m%d%H%M%S")
- fileName = "output.docx"
- filename = f"{date}_{fileName}"
- path = os.path.join(MEDIA_ROOT, filename)
- return path
- def saveDocFile(title,htmlContent):
- doc = Document()
- # 标记目录的位置
- soup = BeautifulSoup(htmlContent, 'html.parser')
- paragraphs = soup.find_all(['h1'])
- # 将HTML内容转换为Word文档
- bookMarkId = 0
- if paragraphs:
- doc.add_paragraph(title)
- doc.add_paragraph("目录")
- catalog_p = doc.add_paragraph('')
- for p in paragraphs:
- text = p.get_text()
- style = p.name
- if style.startswith('h1'):
- # 添加标题
- bookMarkId=bookMarkId+1
- add_title_with_bookmark(doc, text, style='Heading 1', bookmark_id=bookMarkId)
- else:
- doc.add_paragraph(text)
- else:
- paragraphs = soup.find_all(['div'])
- for paragraph in paragraphs:
- doc.add_paragraph(paragraph.getText())
- if bookMarkId>0:
- # 开始写入目录
- # 目录开头增加值域,方便手动更新整个目录。自动生成的目录标题包含在值域中。
- toc_paragraph = catalog_p.insert_paragraph_before() # 在标记的目录位置前添加段落
- r1 = toc_paragraph.add_run()
- toc_field = OxmlElement('w:fldChar')
- toc_field.set(qn('w:fldCharType'), 'begin')
- r1._r.append(toc_field)
- r2 = toc_paragraph.add_run()
- toc_field = OxmlElement('w:instrText')
- toc_field.set(qn('xml:space'), 'preserve')
- toc_field.text = 'TOC \\o "1-3" \\h \\z '
- r2._r.append(toc_field)
- r3 = toc_paragraph.add_run()
- toc_field = OxmlElement('w:fldChar')
- toc_field.set(qn('w:fldCharType'), 'separate')
- r3._r.append(toc_field)
- # 自动生成目录内容,不包含页码
- for paragraph in doc.paragraphs:
- if 'Heading' in paragraph.style.name:
- b = paragraph._element.findall('.//' + qn('w:bookmarkStart'))
- bookmark_name = b[0].get(qn('w:name'))
- text = paragraph.text
- level = int(paragraph.style.name[-1])
- # print(text, bookmark_name)
- toc_paragraph = catalog_p.insert_paragraph_before(style='Normal')
- # 二级标题设置缩进
- if level == 2:
- toc_paragraph.paragraph_format.first_line_indent = Pt(24)
- # 设置制表符,可显示页码前的"…………"
- tabs = OxmlElement('w:tabs')
- tab1 = OxmlElement('w:tab')
- tab1.set(qn('w:val'), "left")
- tab1.set(qn('w:leader'), "dot")
- tab1.set(qn('w:pos'), "8400")
- tabs.append(tab1)
- toc_paragraph._p.pPr.append(tabs)
- # toc_paragraph若未设定style,toc_paragraph._p没有pPr属性,需注释前一句代码,使用以下语句
- # pPr = OxmlElement('w:pPr')
- # pPr.append(tabs)
- # toc_paragraph._p.append(pPr)
- hyperlink = OxmlElement('w:hyperlink')
- hyperlink.set(qn('w:anchor'), bookmark_name)
- hyperlink.set(qn('w:history'), '1')
- hr1 = OxmlElement('w:r')
- rPr = OxmlElement('w:rPr')
- rStyle = OxmlElement('w:rStyle')
- rStyle.set(qn('w:val'), "a4")
- rPr.append(rStyle)
- hr1.text = text
- hr1.append(rPr)
- hyperlink.append(hr1)
- hr2 = OxmlElement('w:r')
- rPr = OxmlElement('w:rPr')
- fldChar = OxmlElement('w:fldChar')
- fldChar.set(qn('w:fldCharType'), 'begin')
- hr2.append(rPr)
- hr2.append(fldChar)
- hyperlink.append(hr2)
- hr3 = OxmlElement('w:r')
- rPr = OxmlElement('w:rPr')
- instrText = OxmlElement('w:instrText')
- instrText.set(qn('xml:space'), 'preserve')
- instrText.text = ' PAGEREF {} \h '.format(bookmark_name)
- hr3.append(rPr)
- hr3.append(instrText)
- hyperlink.append(hr3)
- hr4 = OxmlElement('w:r')
- rPr = OxmlElement('w:rPr')
- fldChar = OxmlElement('w:fldChar')
- fldChar.set(qn('w:fldCharType'), 'separate')
- hr4.append(rPr)
- hr4.append(fldChar)
- hyperlink.append(hr4)
- hrt = OxmlElement('w:r')
- tab = OxmlElement('w:tab')
- hrt.append(tab)
- hyperlink.append(hrt)
- hr5 = OxmlElement('w:r')
- rPr = OxmlElement('w:rPr')
- hr5.text = ''
- hr5.append(rPr)
- hyperlink.append(hr5)
- hr6 = OxmlElement('w:r')
- rPr = OxmlElement('w:rPr')
- fldChar = OxmlElement('w:fldChar')
- fldChar.set(qn('w:fldCharType'), 'end')
- hr6.append(rPr)
- hr6.append(fldChar)
- hyperlink.append(hr6)
- toc_paragraph._p.append(hyperlink)
- # 目录结尾的值域
- toc_paragraph = catalog_p.insert_paragraph_before()
- r4 = toc_paragraph.add_run()
- toc_field = OxmlElement('w:fldChar')
- toc_field.set(qn('w:fldCharType'), 'end')
- r4._r.append(toc_field)
- # 分页
- break_page_p = catalog_p.insert_paragraph_before()
- break_page_p.add_run().add_break(WD_BREAK.PAGE)
- fileName=getDocxFileName()
- doc.save(fileName)
- return fileName
|