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