build_ref_doc.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # -*- coding: utf-8 -*-
  2. import sys
  3. from docx import Document
  4. from docx.shared import Pt, Cm, RGBColor
  5. from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
  6. from docx.enum.table import WD_TABLE_ALIGNMENT
  7. from docx.enum.section import WD_SECTION
  8. from docx.oxml.ns import qn
  9. from docx.oxml import OxmlElement
  10. import docx.opc.constants
  11. sys.stdout.reconfigure(encoding="utf-8")
  12. OUT = r"C:\Users\aroun\Desktop\在线考试系统\在线考试系统开源方案调研与论文写作参考.docx"
  13. doc = Document()
  14. # ---------- page setup ----------
  15. sec = doc.sections[0]
  16. sec.page_width = Cm(21.0)
  17. sec.page_height = Cm(29.7)
  18. sec.top_margin = Cm(2.54)
  19. sec.bottom_margin = Cm(2.54)
  20. sec.left_margin = Cm(2.8)
  21. sec.right_margin = Cm(2.8)
  22. TEXT_W = 21.0 - 2.8 * 2 # 15.4 cm
  23. # ---------- styles ----------
  24. def set_cn_font(style_or_run, name_cn, name_ascii=None, size=None, bold=None, color=None):
  25. f = style_or_run.font
  26. f.name = name_ascii or name_cn
  27. if size is not None:
  28. f.size = Pt(size)
  29. if bold is not None:
  30. f.bold = bold
  31. if color is not None:
  32. f.color.rgb = color
  33. el = style_or_run.element if hasattr(style_or_run, "element") else style_or_run._element
  34. rPr = el.get_or_add_rPr()
  35. rFonts = rPr.find(qn("w:rFonts"))
  36. if rFonts is None:
  37. rFonts = OxmlElement("w:rFonts")
  38. rPr.append(rFonts)
  39. rFonts.set(qn("w:ascii"), name_ascii or name_cn)
  40. rFonts.set(qn("w:hAnsi"), name_ascii or name_cn)
  41. rFonts.set(qn("w:eastAsia"), name_cn)
  42. normal = doc.styles["Normal"]
  43. set_cn_font(normal, "宋体", "Times New Roman", 10.5)
  44. normal.paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE
  45. normal.paragraph_format.space_before = Pt(0)
  46. normal.paragraph_format.space_after = Pt(4)
  47. BLACK = RGBColor(0, 0, 0)
  48. h1 = doc.styles["Heading 1"]
  49. set_cn_font(h1, "黑体", "Times New Roman", 16, True, BLACK)
  50. h1.paragraph_format.space_before = Pt(18)
  51. h1.paragraph_format.space_after = Pt(8)
  52. h2 = doc.styles["Heading 2"]
  53. set_cn_font(h2, "黑体", "Times New Roman", 13, True, BLACK)
  54. h2.paragraph_format.space_before = Pt(14)
  55. h2.paragraph_format.space_after = Pt(6)
  56. h3 = doc.styles["Heading 3"]
  57. set_cn_font(h3, "黑体", "Times New Roman", 11, True, BLACK)
  58. h3.paragraph_format.space_before = Pt(10)
  59. h3.paragraph_format.space_after = Pt(4)
  60. # ---------- helpers ----------
  61. def add_hyperlink(paragraph, url, text, size=9, bold=False):
  62. part = paragraph.part
  63. r_id = part.relate_to(url, docx.opc.constants.RELATIONSHIP_TYPE.HYPERLINK, is_external=True)
  64. hyperlink = OxmlElement("w:hyperlink")
  65. hyperlink.set(qn("r:id"), r_id)
  66. new_run = OxmlElement("w:r")
  67. rPr = OxmlElement("w:rPr")
  68. rFonts = OxmlElement("w:rFonts")
  69. rFonts.set(qn("w:ascii"), "Consolas")
  70. rFonts.set(qn("w:hAnsi"), "Consolas")
  71. rFonts.set(qn("w:eastAsia"), "宋体")
  72. rPr.append(rFonts)
  73. sz = OxmlElement("w:sz"); sz.set(qn("w:val"), str(int(size * 2))); rPr.append(sz)
  74. szcs = OxmlElement("w:szCs"); szcs.set(qn("w:val"), str(int(size * 2))); rPr.append(szcs)
  75. if bold:
  76. b = OxmlElement("w:b"); rPr.append(b)
  77. color = OxmlElement("w:color"); color.set(qn("w:val"), "1F4E79"); rPr.append(color)
  78. u = OxmlElement("w:u"); u.set(qn("w:val"), "single"); rPr.append(u)
  79. new_run.append(rPr)
  80. t = OxmlElement("w:t")
  81. t.text = text
  82. t.set(qn("xml:space"), "preserve")
  83. new_run.append(t)
  84. hyperlink.append(new_run)
  85. paragraph._p.append(hyperlink)
  86. return hyperlink
  87. def shade(cell, hexcolor):
  88. tcPr = cell._tc.get_or_add_tcPr()
  89. shd = OxmlElement("w:shd")
  90. shd.set(qn("w:val"), "clear")
  91. shd.set(qn("w:color"), "auto")
  92. shd.set(qn("w:fill"), hexcolor)
  93. tcPr.append(shd)
  94. def cell_margins(table, top=60, bottom=60, left=90, right=90):
  95. tblPr = table._tbl.tblPr
  96. mar = OxmlElement("w:tblCellMar")
  97. for tag, val in (("top", top), ("left", left), ("bottom", bottom), ("right", right)):
  98. el = OxmlElement("w:" + tag)
  99. el.set(qn("w:w"), str(val))
  100. el.set(qn("w:type"), "dxa")
  101. mar.append(el)
  102. tblPr.append(mar)
  103. def repeat_header(row):
  104. trPr = row._tr.get_or_add_trPr()
  105. el = OxmlElement("w:tblHeader")
  106. el.set(qn("w:val"), "true")
  107. trPr.append(el)
  108. def cant_split(row):
  109. trPr = row._tr.get_or_add_trPr()
  110. el = OxmlElement("w:cantSplit")
  111. el.set(qn("w:val"), "true")
  112. trPr.append(el)
  113. def set_cell_text(cell, text, size=9, bold=False, align=None, color=None):
  114. cell.text = ""
  115. p = cell.paragraphs[0]
  116. p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.SINGLE
  117. p.paragraph_format.space_before = Pt(0)
  118. p.paragraph_format.space_after = Pt(0)
  119. if align is not None:
  120. p.alignment = align
  121. run = p.add_run(text)
  122. set_cn_font(run, "宋体", "Times New Roman", size, bold, color)
  123. def make_table(headers, rows, widths, link_col=None, font=8.5, head_font=8.5):
  124. table = doc.add_table(rows=1, cols=len(headers))
  125. table.style = "Table Grid"
  126. table.alignment = WD_TABLE_ALIGNMENT.CENTER
  127. table.autofit = False
  128. cell_margins(table)
  129. hdr = table.rows[0]
  130. repeat_header(hdr)
  131. cant_split(hdr)
  132. for i, h in enumerate(headers):
  133. c = hdr.cells[i]
  134. c.width = Cm(widths[i])
  135. shade(c, "D9D9D9")
  136. set_cell_text(c, h, head_font, True, WD_ALIGN_PARAGRAPH.CENTER)
  137. for r in rows:
  138. row = table.add_row()
  139. cant_split(row)
  140. for i, val in enumerate(r):
  141. c = row.cells[i]
  142. c.width = Cm(widths[i])
  143. if link_col is not None and i == link_col:
  144. c.text = ""
  145. p = c.paragraphs[0]
  146. p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.SINGLE
  147. p.paragraph_format.space_before = Pt(0)
  148. p.paragraph_format.space_after = Pt(0)
  149. add_hyperlink(p, val[1], val[0], size=font - 0.5)
  150. else:
  151. set_cell_text(c, val, font)
  152. # force column widths on every cell (Word honours cell-level w)
  153. for row in table.rows:
  154. for i, c in enumerate(row.cells):
  155. c.width = Cm(widths[i])
  156. return table
  157. def para(text, size=10.5, bold=False, indent=True, space_after=4, align=None, color=None, first_indent=True):
  158. p = doc.add_paragraph()
  159. p.paragraph_format.space_after = Pt(space_after)
  160. if align is not None:
  161. p.alignment = align
  162. if indent and first_indent:
  163. p.paragraph_format.first_line_indent = Pt(21)
  164. run = p.add_run(text)
  165. set_cn_font(run, "宋体", "Times New Roman", size, bold, color)
  166. return p
  167. def bullet(text, size=10.5, level=0):
  168. p = doc.add_paragraph(style="List Bullet")
  169. p.paragraph_format.left_indent = Cm(0.75 + 0.6 * level)
  170. p.paragraph_format.first_line_indent = Cm(-0.4)
  171. p.paragraph_format.space_after = Pt(2)
  172. run = p.add_run(text)
  173. set_cn_font(run, "宋体", "Times New Roman", size)
  174. return p
  175. def numitem(n, title, text, size=10.5):
  176. p = doc.add_paragraph()
  177. p.paragraph_format.space_after = Pt(4)
  178. p.paragraph_format.left_indent = Cm(0.75)
  179. p.paragraph_format.first_line_indent = Cm(-0.75)
  180. r1 = p.add_run("%s " % n)
  181. set_cn_font(r1, "宋体", "Times New Roman", size, True)
  182. r2 = p.add_run(title)
  183. set_cn_font(r2, "黑体", "Times New Roman", size, True)
  184. if text:
  185. r3 = p.add_run(text)
  186. set_cn_font(r3, "宋体", "Times New Roman", size)
  187. return p
  188. def caption(text):
  189. p = doc.add_paragraph()
  190. p.alignment = WD_ALIGN_PARAGRAPH.CENTER
  191. p.paragraph_format.space_before = Pt(6)
  192. p.paragraph_format.space_after = Pt(3)
  193. run = p.add_run(text)
  194. set_cn_font(run, "黑体", "Times New Roman", 9, True)
  195. return p
  196. def note(text):
  197. p = doc.add_paragraph()
  198. p.paragraph_format.space_before = Pt(2)
  199. p.paragraph_format.space_after = Pt(10)
  200. p.paragraph_format.left_indent = Cm(0.2)
  201. run = p.add_run(text)
  202. set_cn_font(run, "楷体", "Times New Roman", 9.5, False, RGBColor(0x40, 0x40, 0x40))
  203. return p
  204. def quote_block(lines):
  205. for ln in lines:
  206. p = doc.add_paragraph()
  207. p.paragraph_format.left_indent = Cm(0.9)
  208. p.paragraph_format.right_indent = Cm(0.4)
  209. p.paragraph_format.space_after = Pt(4)
  210. p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE
  211. run = p.add_run(ln)
  212. set_cn_font(run, "楷体", "Times New Roman", 10)
  213. return
  214. # footer page number
  215. footer = sec.footer
  216. fp = footer.paragraphs[0]
  217. fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
  218. run = fp.add_run()
  219. fld1 = OxmlElement("w:fldChar"); fld1.set(qn("w:fldCharType"), "begin")
  220. instr = OxmlElement("w:instrText"); instr.set(qn("xml:space"), "preserve"); instr.text = " PAGE "
  221. fld2 = OxmlElement("w:fldChar"); fld2.set(qn("w:fldCharType"), "end")
  222. run._r.append(fld1); run._r.append(instr); run._r.append(fld2)
  223. set_cn_font(run, "宋体", "Times New Roman", 9)
  224. print("setup ok")
  225. doc.save(OUT)
  226. print("saved skeleton")