|
|
@@ -0,0 +1,496 @@
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+"""Build 分册 + 合订版 Word/PDF for 大洋 MES manuals 9.19."""
|
|
|
+import os, re, html as htmlmod
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+import fitz
|
|
|
+from docx import Document
|
|
|
+from docx.shared import Pt, Cm, RGBColor, Emu
|
|
|
+from docx.oxml.ns import qn
|
|
|
+from docx.enum.table import WD_TABLE_ALIGNMENT
|
|
|
+from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
|
+
|
|
|
+ROOT = Path(r"D:\产线\大洋\大洋操作手册汇总9.19")
|
|
|
+FONT_FILE = r"C:\Windows\Fonts\simhei.ttf"
|
|
|
+FONT_FILE_REG = r"C:\Windows\Fonts\msyh.ttc"
|
|
|
+DATE = "2026年9月19日"
|
|
|
+A4 = fitz.paper_rect("a4")
|
|
|
+
|
|
|
+FASCICLES = [
|
|
|
+ ("1、系统管理操作手册.md", "系统管理操作手册", "分册一"),
|
|
|
+ ("2、设备管理操作手册.md", "设备管理操作手册", "分册二"),
|
|
|
+ ("3、物料管理操作手册.md", "物料管理操作手册", "分册三"),
|
|
|
+ ("4、质量管理操作手册.md", "质量管理操作手册", "分册四"),
|
|
|
+ ("5、生产管理操作手册.md", "生产管理操作手册", "分册五"),
|
|
|
+ ("6、ERP对接操作手册.md", "ERP对接操作手册", "分册六"),
|
|
|
+ ("7、生产线管理操作手册.md", "生产线管理操作手册", "分册七"),
|
|
|
+ ("8、PDA操作说明操作手册.md", "PDA操作说明操作手册", "分册八"),
|
|
|
+ ("9、MES系统客户端员工扫码操作手册.md", "MES系统客户端员工扫码操作手册", "分册九"),
|
|
|
+]
|
|
|
+
|
|
|
+
|
|
|
+def esc(s):
|
|
|
+ return htmlmod.escape(s, quote=True)
|
|
|
+
|
|
|
+
|
|
|
+def md_inline_html(text):
|
|
|
+ text = esc(text)
|
|
|
+ text = re.sub(r"\*\*([^*]+)\*\*", r"<b>\1</b>", text)
|
|
|
+ text = re.sub(r"`([^`]+)`", r"<code>\1</code>", text)
|
|
|
+ return text
|
|
|
+
|
|
|
+
|
|
|
+def is_sep_row(cells):
|
|
|
+ return all(re.match(r"^:?-+:?$", c.replace(" ", "")) for c in cells if c != "")
|
|
|
+
|
|
|
+
|
|
|
+def md_to_html_body(md_text, skip_first_h1=False):
|
|
|
+ lines = md_text.splitlines()
|
|
|
+ out = []
|
|
|
+ i = 0
|
|
|
+ skipped = False
|
|
|
+ while i < len(lines):
|
|
|
+ line = lines[i].rstrip()
|
|
|
+ if not line:
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.strip() == "---":
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.startswith("# "):
|
|
|
+ title = line[2:].strip()
|
|
|
+ if skip_first_h1 and not skipped:
|
|
|
+ skipped = True
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ out.append("<h1>%s</h1>" % md_inline_html(title))
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ m = re.match(r"^(#{2,6})\s+(.*)$", line)
|
|
|
+ if m:
|
|
|
+ lv = len(m.group(1))
|
|
|
+ out.append("<h%d>%s</h%d>" % (lv, md_inline_html(m.group(2).strip()), lv))
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.startswith("|"):
|
|
|
+ rows = []
|
|
|
+ while i < len(lines) and lines[i].startswith("|"):
|
|
|
+ cells = [c.strip() for c in lines[i].strip().strip("|").split("|")]
|
|
|
+ if not is_sep_row(cells):
|
|
|
+ rows.append(cells)
|
|
|
+ i += 1
|
|
|
+ if rows:
|
|
|
+ out.append("<table>")
|
|
|
+ for ri, row in enumerate(rows):
|
|
|
+ out.append("<tr>")
|
|
|
+ tag = "th" if ri == 0 else "td"
|
|
|
+ for c in row:
|
|
|
+ out.append("<%s>%s</%s>" % (tag, md_inline_html(c), tag))
|
|
|
+ out.append("</tr>")
|
|
|
+ out.append("</table>")
|
|
|
+ continue
|
|
|
+ if line.startswith("- "):
|
|
|
+ out.append("<ul>")
|
|
|
+ while i < len(lines) and lines[i].startswith("- "):
|
|
|
+ out.append("<li>%s</li>" % md_inline_html(lines[i][2:].strip()))
|
|
|
+ i += 1
|
|
|
+ out.append("</ul>")
|
|
|
+ continue
|
|
|
+ if re.match(r"^\d+\.\s", line):
|
|
|
+ out.append("<ol>")
|
|
|
+ while i < len(lines) and re.match(r"^\d+\.\s", lines[i]):
|
|
|
+ out.append("<li>%s</li>" % md_inline_html(re.sub(r"^\d+\.\s+", "", lines[i])))
|
|
|
+ i += 1
|
|
|
+ out.append("</ol>")
|
|
|
+ continue
|
|
|
+ img = re.match(r"^!\[(.*?)\]\((.*?)\)$", line.strip())
|
|
|
+ if img:
|
|
|
+ cap, src = img.group(1), img.group(2).replace("\\", "/")
|
|
|
+ rel = src.split("images/")[-1] if "images/" in src else src.split("/")[-1]
|
|
|
+ member = rel.replace("/", "__")
|
|
|
+ out.append('<p class="shot"><img src="%s" alt="%s"/></p>' % (esc(member), esc(cap)))
|
|
|
+ out.append('<p class="imgcap">%s</p>' % md_inline_html(cap))
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.strip().startswith("*(界面截图") and "为准" in line:
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ out.append("<p>%s</p>" % md_inline_html(line))
|
|
|
+ i += 1
|
|
|
+ return "\n".join(out)
|
|
|
+
|
|
|
+
|
|
|
+CSS = r"""
|
|
|
+@font-face { font-family: bodyfont; src: url("simhei.ttf"); }
|
|
|
+body { font-family: bodyfont, sans-serif; font-size: 10.5pt; line-height: 1.5; color: #222; }
|
|
|
+h1 { font-size: 18pt; color: #1F4E79; margin: 18pt 0 8pt 0; page-break-before: auto; }
|
|
|
+h2 { font-size: 14pt; color: #2E75B6; margin: 14pt 0 6pt 0; }
|
|
|
+h3 { font-size: 12.5pt; color: #2E75B6; margin: 12pt 0 5pt 0; }
|
|
|
+h4 { font-size: 11.5pt; color: #1F4E79; margin: 10pt 0 4pt 0; }
|
|
|
+h5, h6 { font-size: 11pt; margin: 8pt 0 4pt 0; }
|
|
|
+p { margin: 0 0 6pt 0; }
|
|
|
+ul, ol { margin: 0 0 8pt 18pt; padding: 0; }
|
|
|
+li { margin: 0 0 3pt 0; }
|
|
|
+table { border-collapse: collapse; width: 100%; margin: 6pt 0 10pt 0; }
|
|
|
+th { background: #1F4E79; color: #fff; font-weight: bold; }
|
|
|
+th, td { border: 1px solid #8aa; padding: 4pt 6pt; font-size: 9.5pt; vertical-align: top; }
|
|
|
+code { font-size: 9.5pt; }
|
|
|
+p.shot { text-align: center; margin: 8pt 0 2pt 0; }
|
|
|
+p.shot img { max-width: 100%; max-height: 380pt; }
|
|
|
+p.imgcap { text-align: center; font-size: 9pt; color: #555; margin: 0 0 10pt 0; }
|
|
|
+"""
|
|
|
+
|
|
|
+
|
|
|
+def make_cover(title, subtitle, extra=""):
|
|
|
+ doc = fitz.open()
|
|
|
+ page = doc.new_page(width=A4.width, height=A4.height)
|
|
|
+ page.insert_font(fontname="hei", fontfile=FONT_FILE)
|
|
|
+ cx = A4.width / 2
|
|
|
+ page.draw_rect(fitz.Rect(40, 40, A4.width - 40, A4.height - 40), color=(0.12, 0.31, 0.47), width=1.5)
|
|
|
+ page.draw_rect(fitz.Rect(48, 48, A4.width - 48, A4.height - 48), color=(0.18, 0.46, 0.71), width=0.6)
|
|
|
+ page.insert_text((cx - 120, 180), "安徽大洋 · MES", fontname="hei", fontsize=14, fill=(0.18, 0.46, 0.71))
|
|
|
+ # wrap title
|
|
|
+ y = 250
|
|
|
+ for line in title.split("\n"):
|
|
|
+ w = fitz.get_text_length(line, fontname="hei", fontsize=26, fontfile=FONT_FILE) if False else None
|
|
|
+ tw = fitz.Font(fontfile=FONT_FILE).text_length(line, 26)
|
|
|
+ page.insert_text((cx - tw / 2, y), line, fontname="hei", fontsize=26, fill=(0.12, 0.31, 0.47))
|
|
|
+ y += 40
|
|
|
+ if subtitle:
|
|
|
+ tw = fitz.Font(fontfile=FONT_FILE).text_length(subtitle, 16)
|
|
|
+ page.insert_text((cx - tw / 2, y + 10), subtitle, fontname="hei", fontsize=16, fill=(0.3, 0.3, 0.3))
|
|
|
+ y += 40
|
|
|
+ if extra:
|
|
|
+ tw = fitz.Font(fontfile=FONT_FILE).text_length(extra, 13)
|
|
|
+ page.insert_text((cx - tw / 2, y + 20), extra, fontname="hei", fontsize=13, fill=(0.35, 0.35, 0.35))
|
|
|
+ tw = fitz.Font(fontfile=FONT_FILE).text_length(DATE, 12)
|
|
|
+ page.insert_text((cx - tw / 2, A4.height - 120), DATE, fontname="hei", fontsize=12, fill=(0.2, 0.2, 0.2))
|
|
|
+ return doc
|
|
|
+
|
|
|
+
|
|
|
+def html_to_pdf_pages(html_body, header_text):
|
|
|
+ css = CSS
|
|
|
+ html = "<html><body>%s</body></html>" % html_body
|
|
|
+ import tempfile, shutil
|
|
|
+ img_tmp = tempfile.mkdtemp(prefix="mesimg_")
|
|
|
+ img_root = str(ROOT / "images")
|
|
|
+ if os.path.isdir(img_root):
|
|
|
+ for dirpath, _, files in os.walk(img_root):
|
|
|
+ for fn in files:
|
|
|
+ if fn.lower().endswith((".png", ".jpg", ".jpeg")):
|
|
|
+ full = os.path.join(dirpath, fn)
|
|
|
+ rel = os.path.relpath(full, img_root).replace("\\", "/")
|
|
|
+ member = rel.replace("/", "__")
|
|
|
+ shutil.copy2(full, os.path.join(img_tmp, member))
|
|
|
+ arch = fitz.Archive(os.path.dirname(FONT_FILE))
|
|
|
+ arch.add(img_tmp)
|
|
|
+ story = fitz.Story(html=html, user_css=css, archive=arch)
|
|
|
+ where = fitz.Rect(48, 52, A4.width - 48, A4.height - 48)
|
|
|
+ import tempfile
|
|
|
+ tmp = tempfile.mktemp(suffix=".pdf")
|
|
|
+ writer = fitz.DocumentWriter(tmp)
|
|
|
+ more = 1
|
|
|
+ while more:
|
|
|
+ dev = writer.begin_page(A4)
|
|
|
+ more, _ = story.place(where)
|
|
|
+ story.draw(dev)
|
|
|
+ writer.end_page()
|
|
|
+ writer.close()
|
|
|
+ try:
|
|
|
+ shutil.rmtree(img_tmp, ignore_errors=True)
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+ body = fitz.open(tmp)
|
|
|
+ try:
|
|
|
+ os.remove(tmp)
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+ font = fitz.Font(fontfile=FONT_FILE)
|
|
|
+ for i, page in enumerate(body):
|
|
|
+ page.insert_font(fontname="hei", fontfile=FONT_FILE)
|
|
|
+ page.insert_text((48, 32), header_text, fontname="hei", fontsize=8, fill=(0.4, 0.4, 0.4))
|
|
|
+ pn = str(i + 1)
|
|
|
+ tw = font.text_length(pn, 9)
|
|
|
+ page.insert_text((A4.width - 48 - tw, A4.height - 32), pn, fontname="hei", fontsize=9, fill=(0.3, 0.3, 0.3))
|
|
|
+ page.draw_line(fitz.Point(48, 40), fitz.Point(A4.width - 48, 40), color=(0.75, 0.75, 0.75), width=0.4)
|
|
|
+ page.draw_line(fitz.Point(48, A4.height - 40), fitz.Point(A4.width - 48, A4.height - 40), color=(0.75, 0.75, 0.75), width=0.4)
|
|
|
+ return body
|
|
|
+
|
|
|
+
|
|
|
+def stamp_page_numbers(doc, header_text, start=1):
|
|
|
+ font = fitz.Font(fontfile=FONT_FILE)
|
|
|
+ for i, page in enumerate(doc):
|
|
|
+ # skip cover (first page) header already different
|
|
|
+ page.insert_font(fontname="hei", fontfile=FONT_FILE)
|
|
|
+ if i == 0:
|
|
|
+ continue
|
|
|
+ page.insert_text((48, 32), header_text, fontname="hei", fontsize=8, fill=(0.4, 0.4, 0.4))
|
|
|
+ pn = str(start + i - 1)
|
|
|
+ tw = font.text_length(pn, 9)
|
|
|
+ page.insert_text((A4.width - 48 - tw, A4.height - 32), pn, fontname="hei", fontsize=9, fill=(0.3, 0.3, 0.3))
|
|
|
+ page.draw_line(fitz.Point(48, 40), fitz.Point(A4.width - 48, 40), color=(0.75, 0.75, 0.75), width=0.4)
|
|
|
+ page.draw_line(fitz.Point(48, A4.height - 40), fitz.Point(A4.width - 48, A4.height - 40), color=(0.75, 0.75, 0.75), width=0.4)
|
|
|
+
|
|
|
+
|
|
|
+def build_pdf(cover_title, cover_sub, extra, html_body, header, out_path):
|
|
|
+ cover = make_cover(cover_title, cover_sub, extra)
|
|
|
+ body = html_to_pdf_pages(html_body, header)
|
|
|
+ final = fitz.open()
|
|
|
+ final.insert_pdf(cover)
|
|
|
+ final.insert_pdf(body)
|
|
|
+ # restamp body pages only
|
|
|
+ font = fitz.Font(fontfile=FONT_FILE)
|
|
|
+ for i, page in enumerate(final):
|
|
|
+ if i == 0:
|
|
|
+ continue
|
|
|
+ page.insert_font(fontname="hei", fontfile=FONT_FILE)
|
|
|
+ # html_to_pdf_pages already stamped; cover inserted shifts numbers.
|
|
|
+ final.save(out_path, deflate=True, garbage=4)
|
|
|
+ cover.close()
|
|
|
+ body.close()
|
|
|
+ final.close()
|
|
|
+ print("PDF", out_path.name, "pages ok")
|
|
|
+
|
|
|
+
|
|
|
+# ---- docx (reuse simplified converter) ----
|
|
|
+from importlib.machinery import SourceFileLoader
|
|
|
+# inline converter to avoid path issues
|
|
|
+
|
|
|
+def set_run_font(run, size=11, bold=False, color=None):
|
|
|
+ run.bold = bold
|
|
|
+ run.font.size = Pt(size)
|
|
|
+ run.font.name = "微软雅黑"
|
|
|
+ rPr = run._element.get_or_add_rPr()
|
|
|
+ rFonts = rPr.find(qn("w:rFonts"))
|
|
|
+ if rFonts is None:
|
|
|
+ rFonts = rPr.makeelement(qn("w:rFonts"), {})
|
|
|
+ rPr.append(rFonts)
|
|
|
+ rFonts.set(qn("w:eastAsia"), "微软雅黑")
|
|
|
+ rFonts.set(qn("w:ascii"), "微软雅黑")
|
|
|
+ rFonts.set(qn("w:hAnsi"), "微软雅黑")
|
|
|
+ if color:
|
|
|
+ run.font.color.rgb = RGBColor(*color)
|
|
|
+
|
|
|
+
|
|
|
+def add_runs(p, text, size=11, bold=False):
|
|
|
+ parts = re.split(r"(\*\*[^*]+\*\*)", text)
|
|
|
+ for part in parts:
|
|
|
+ if part.startswith("**") and part.endswith("**"):
|
|
|
+ run = p.add_run(part[2:-2])
|
|
|
+ set_run_font(run, size=size, bold=True)
|
|
|
+ else:
|
|
|
+ run = p.add_run(part)
|
|
|
+ set_run_font(run, size=size, bold=bold)
|
|
|
+
|
|
|
+
|
|
|
+def md_to_docx(md_text, docx_path, heading_shift=0):
|
|
|
+ doc = Document()
|
|
|
+ section = doc.sections[0]
|
|
|
+ section.top_margin = Cm(2)
|
|
|
+ section.bottom_margin = Cm(2)
|
|
|
+ section.left_margin = Cm(2.2)
|
|
|
+ section.right_margin = Cm(2.2)
|
|
|
+ style = doc.styles["Normal"]
|
|
|
+ style.font.name = "微软雅黑"
|
|
|
+ style.font.size = Pt(11)
|
|
|
+ style.element.rPr.rFonts.set(qn("w:eastAsia"), "微软雅黑")
|
|
|
+ lines = md_text.splitlines()
|
|
|
+ i = 0
|
|
|
+ while i < len(lines):
|
|
|
+ line = lines[i].rstrip()
|
|
|
+ if not line or line.strip() == "---":
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.startswith("#"):
|
|
|
+ hashes = len(line) - len(line.lstrip("#"))
|
|
|
+ title = line[hashes:].strip()
|
|
|
+ level = min(max(hashes - 1 + heading_shift, 0), 4)
|
|
|
+ p = doc.add_heading("", level=level)
|
|
|
+ p.clear()
|
|
|
+ run = p.add_run(title)
|
|
|
+ sizes = {0: 18, 1: 16, 2: 14, 3: 12, 4: 12}
|
|
|
+ set_run_font(run, size=sizes.get(level, 12), bold=True, color=(0x1F, 0x4E, 0x79))
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.startswith("|"):
|
|
|
+ rows = []
|
|
|
+ while i < len(lines) and lines[i].startswith("|"):
|
|
|
+ cells = [c.strip() for c in lines[i].strip().strip("|").split("|")]
|
|
|
+ if not is_sep_row(cells):
|
|
|
+ rows.append(cells)
|
|
|
+ i += 1
|
|
|
+ if rows:
|
|
|
+ cols = max(len(r) for r in rows)
|
|
|
+ table = doc.add_table(rows=len(rows), cols=cols)
|
|
|
+ table.style = "Table Grid"
|
|
|
+ table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
|
|
+ for r_idx, row in enumerate(rows):
|
|
|
+ for c_idx in range(cols):
|
|
|
+ cell = table.cell(r_idx, c_idx)
|
|
|
+ cell.text = ""
|
|
|
+ p = cell.paragraphs[0]
|
|
|
+ val = row[c_idx] if c_idx < len(row) else ""
|
|
|
+ add_runs(p, val, size=10, bold=(r_idx == 0))
|
|
|
+ continue
|
|
|
+ if line.startswith("- "):
|
|
|
+ p = doc.add_paragraph(style="List Bullet")
|
|
|
+ p.clear()
|
|
|
+ add_runs(p, line[2:].strip())
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if re.match(r"^\d+\.\s", line):
|
|
|
+ p = doc.add_paragraph(style="List Number")
|
|
|
+ p.clear()
|
|
|
+ add_runs(p, re.sub(r"^\d+\.\s+", "", line))
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ img = re.match(r"^!\[(.*?)\]\((.*?)\)$", line.strip())
|
|
|
+ if img:
|
|
|
+ cap, src = img.group(1), img.group(2).replace("/", os.sep)
|
|
|
+ img_path = src if os.path.isabs(src) else str(ROOT / src)
|
|
|
+ p = doc.add_paragraph()
|
|
|
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
+ if os.path.exists(img_path):
|
|
|
+ run = p.add_run()
|
|
|
+ try:
|
|
|
+ run.add_picture(img_path, width=Cm(14.5))
|
|
|
+ except Exception:
|
|
|
+ add_runs(p, "[图片无法插入] " + cap)
|
|
|
+ cap_p = doc.add_paragraph()
|
|
|
+ cap_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
+ add_runs(cap_p, cap, size=9)
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ if line.strip().startswith("*(界面截图"):
|
|
|
+ i += 1
|
|
|
+ continue
|
|
|
+ p = doc.add_paragraph()
|
|
|
+ add_runs(p, line)
|
|
|
+ i += 1
|
|
|
+ try:
|
|
|
+ doc.save(str(docx_path))
|
|
|
+ except PermissionError:
|
|
|
+ alt = str(docx_path).replace(".docx", "_新.docx")
|
|
|
+ doc.save(alt)
|
|
|
+ print("docx locked, wrote", alt)
|
|
|
+
|
|
|
+
|
|
|
+def strip_toc(md):
|
|
|
+ # drop ## 目录 ... until next ##
|
|
|
+ return re.sub(r"\n## 目录\n[\s\S]*?(?=\n## )", "\n", md, count=1)
|
|
|
+
|
|
|
+
|
|
|
+def remap_headings(md, delta):
|
|
|
+ def repl(m):
|
|
|
+ hashes = m.group(1)
|
|
|
+ rest = m.group(2)
|
|
|
+ n = min(len(hashes) + delta, 6)
|
|
|
+ return "#" * n + " " + rest
|
|
|
+ return re.sub(r"^(#{1,6})\s+(.*)$", repl, md, flags=re.M)
|
|
|
+
|
|
|
+
|
|
|
+def build_combined_md():
|
|
|
+ intro = (ROOT / "_合订版_导言.md").read_text(encoding="utf-8")
|
|
|
+ parts = [intro, ""]
|
|
|
+ mapping = [
|
|
|
+ (FASCICLES[0][0], "#### 3.1.3 系统管理", 3),
|
|
|
+ (FASCICLES[1][0], "#### 3.1.4 设备管理", 3),
|
|
|
+ (FASCICLES[2][0], "#### 3.1.5 物料管理", 3),
|
|
|
+ (FASCICLES[3][0], "#### 3.1.6 质量管理", 3),
|
|
|
+ (FASCICLES[4][0], "#### 3.1.7 生产管理", 3),
|
|
|
+ (FASCICLES[5][0], "#### 3.1.8 ERP 对接", 3),
|
|
|
+ (FASCICLES[6][0], "#### 3.1.9 生产线管理", 3),
|
|
|
+ (FASCICLES[7][0], "### 3.2 PDA 软件操作说明", 2),
|
|
|
+ (FASCICLES[8][0], "### 3.3 工控机软件操作说明", 2),
|
|
|
+ ]
|
|
|
+ for fname, section_title, delta in mapping:
|
|
|
+ raw = (ROOT / fname).read_text(encoding="utf-8")
|
|
|
+ raw = strip_toc(raw)
|
|
|
+ # drop first h1
|
|
|
+ raw = re.sub(r"^# .*\n+", "", raw)
|
|
|
+ raw = re.sub(r"^20\d{2} 年.*\n+", "", raw)
|
|
|
+ raw = remap_headings(raw, delta)
|
|
|
+ parts.append(section_title)
|
|
|
+ parts.append("")
|
|
|
+ parts.append(raw.strip())
|
|
|
+ parts.append("")
|
|
|
+ changelog = (ROOT / "00-修订对照说明.md").read_text(encoding="utf-8")
|
|
|
+ changelog = remap_headings(re.sub(r"^# .*\n+", "## 附录 本次修订对照说明\n\n", changelog, count=1), 0)
|
|
|
+ parts.append(changelog)
|
|
|
+ text = "\n".join(parts)
|
|
|
+ out = ROOT / "大洋交付-操作手册合订版.md"
|
|
|
+ out.write_text(text, encoding="utf-8")
|
|
|
+ return text
|
|
|
+
|
|
|
+
|
|
|
+def fix_toc_numbers(md):
|
|
|
+ """Avoid '4. 常见问题' in TOC being parsed as a new ordered list."""
|
|
|
+ def repl(m):
|
|
|
+ block = m.group(0)
|
|
|
+ block = re.sub(r"^(\d+)\.\s+", r"\1、", block, flags=re.M)
|
|
|
+ return block
|
|
|
+ return re.sub(r"(## 目录\n)([\s\S]*?)(?=\n## )", lambda m: m.group(1) + re.sub(r"^(\d+)\.\s+", r"\1、", m.group(2), flags=re.M), md, count=1)
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ import shutil
|
|
|
+ # 分册 PDF + 刷新 docx
|
|
|
+ for fname, short, extra in FASCICLES:
|
|
|
+ md_path = ROOT / fname
|
|
|
+ md = fix_toc_numbers(md_path.read_text(encoding="utf-8"))
|
|
|
+ md_path.write_text(md, encoding="utf-8")
|
|
|
+ stem = fname[:-3]
|
|
|
+ html_body = md_to_html_body(md, skip_first_h1=False)
|
|
|
+ build_pdf(
|
|
|
+ "大洋MES 生产管理系统",
|
|
|
+ "——" + short + "——",
|
|
|
+ extra,
|
|
|
+ html_body,
|
|
|
+ "大洋MES · " + short + " · 分册",
|
|
|
+ ROOT / (stem + ".pdf"),
|
|
|
+ )
|
|
|
+ md_to_docx(md, ROOT / (stem + ".docx"))
|
|
|
+
|
|
|
+ # 对照说明
|
|
|
+ ch = (ROOT / "00-修订对照说明.md").read_text(encoding="utf-8")
|
|
|
+ build_pdf("大洋MES 操作手册", "修订对照说明", "相对 8.28 版本", md_to_html_body(ch), "大洋MES · 修订对照说明", ROOT / "00-修订对照说明.pdf")
|
|
|
+ md_to_docx(ch, ROOT / "00-修订对照说明.docx")
|
|
|
+
|
|
|
+ combined = build_combined_md()
|
|
|
+ build_pdf(
|
|
|
+ "大洋MES 生产管理系统\n操作手册合订版",
|
|
|
+ "——(MES 后台 + PDA + 工控机)——",
|
|
|
+ "分册合订 · 现行菜单与 PDA 1.0.6",
|
|
|
+ md_to_html_body(combined, skip_first_h1=True),
|
|
|
+ "大洋MES 生产管理系统 · 操作手册合订版",
|
|
|
+ ROOT / "大洋交付-操作手册合订版.pdf",
|
|
|
+ )
|
|
|
+ md_to_docx(combined, ROOT / "大洋交付-操作手册合订版.docx")
|
|
|
+
|
|
|
+ fen = ROOT / "分册"
|
|
|
+ he = ROOT / "合订版"
|
|
|
+ fen.mkdir(exist_ok=True)
|
|
|
+ he.mkdir(exist_ok=True)
|
|
|
+ for fname, short, extra in FASCICLES:
|
|
|
+ stem = fname[:-3]
|
|
|
+ for ext in (".pdf", ".docx", ".md"):
|
|
|
+ src = ROOT / (stem + ext)
|
|
|
+ if src.exists():
|
|
|
+ shutil.copy2(src, fen / src.name)
|
|
|
+ for name in ("大洋交付-操作手册合订版.pdf", "大洋交付-操作手册合订版.docx", "大洋交付-操作手册合订版.md"):
|
|
|
+ src = ROOT / name
|
|
|
+ if src.exists():
|
|
|
+ shutil.copy2(src, he / name)
|
|
|
+ for name in ("00-修订对照说明.pdf", "00-修订对照说明.docx", "00-修订对照说明.md"):
|
|
|
+ src = ROOT / name
|
|
|
+ if src.exists():
|
|
|
+ shutil.copy2(src, fen / name)
|
|
|
+ shutil.copy2(src, he / name)
|
|
|
+ chk = ROOT / "_pdf_check.txt"
|
|
|
+ if chk.exists():
|
|
|
+ chk.unlink()
|
|
|
+ print("done")
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|