172 lines
5.0 KiB
Python
172 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
直接生成软著源程序PDF:严格前30页 + 后30页,每页60行。
|
|
"""
|
|
import os
|
|
import sys
|
|
from fpdf import FPDF
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
LINES_PER_PAGE = 60
|
|
SOFT_NAME = "可视化大屏导航站"
|
|
VERSION = "V1.0"
|
|
|
|
|
|
def add_fonts(pdf):
|
|
candidates = [
|
|
"/System/Library/AssetsV2/com_apple_MobileAsset_Font8/86ba2c91f017a3749571a82f2c6d890ac7ffb2fb.asset/AssetData/PingFang.ttc",
|
|
"/System/Library/Fonts/PingFang.ttc",
|
|
"/System/Library/PrivateFrameworks/FontServices.framework/Versions/A/Resources/Reserved/PingFangUI.ttc",
|
|
"/System/Library/Fonts/STHeiti Medium.ttc",
|
|
"/System/Library/Fonts/Hiragino Sans GB.ttc",
|
|
]
|
|
font_path = next((c for c in candidates if os.path.exists(c)), None)
|
|
if not font_path:
|
|
raise RuntimeError("No Chinese font found")
|
|
pdf.add_font("body", "", font_path, uni=True)
|
|
pdf.add_font("body_bold", "", font_path, uni=True)
|
|
|
|
|
|
class SourcePDF(FPDF):
|
|
def header(self):
|
|
self.set_font("body", "", 9)
|
|
self.set_text_color(80, 80, 80)
|
|
self.cell(0, 8, f"{SOFT_NAME} {VERSION}", border="B", align="C", new_x="LMARGIN", new_y="NEXT")
|
|
|
|
def footer(self):
|
|
self.set_y(-15)
|
|
self.set_font("body", "", 9)
|
|
self.set_text_color(80, 80, 80)
|
|
self.cell(0, 10, f"第 {self.page_no} 页,共 60 页", align="C")
|
|
|
|
|
|
def collect_files():
|
|
src = os.path.join(ROOT, "src")
|
|
files = []
|
|
|
|
def exists(rel):
|
|
return os.path.exists(os.path.join(ROOT, rel))
|
|
|
|
# Priority order same as docx generator
|
|
priority = [
|
|
"src/main.ts",
|
|
"src/App.vue",
|
|
"src/vite-env.d.ts",
|
|
"src/types.ts",
|
|
"index.html",
|
|
"package.json",
|
|
"tsconfig.json",
|
|
"vite.config.ts",
|
|
]
|
|
for f in priority:
|
|
if exists(f):
|
|
files.append(f)
|
|
|
|
def add_dir(d, ext):
|
|
res = []
|
|
if not os.path.isdir(d):
|
|
return res
|
|
for root, _, names in os.walk(d):
|
|
for n in names:
|
|
if n.endswith(ext):
|
|
res.append(os.path.relpath(os.path.join(root, n), ROOT))
|
|
res.sort()
|
|
return res
|
|
|
|
files.extend(add_dir(os.path.join(src, "router"), ".ts"))
|
|
files.extend(add_dir(os.path.join(src, "data"), ".ts"))
|
|
files.extend(add_dir(os.path.join(src, "utils"), ".ts"))
|
|
files.extend(add_dir(os.path.join(src, "components"), ".vue"))
|
|
files.extend(add_dir(os.path.join(src, "views"), ".vue"))
|
|
|
|
dash = os.path.join(src, "dashboards")
|
|
if os.path.isdir(dash):
|
|
cats = sorted(os.listdir(dash))
|
|
for cat in cats:
|
|
cat_dir = os.path.join(dash, cat)
|
|
if not os.path.isdir(cat_dir):
|
|
continue
|
|
files.extend(add_dir(cat_dir, ".vue"))
|
|
files.extend(add_dir(cat_dir, ".ts"))
|
|
|
|
# Remove duplicates while preserving order
|
|
seen = set()
|
|
out = []
|
|
for f in files:
|
|
if f not in seen and exists(f):
|
|
seen.add(f)
|
|
out.append(f)
|
|
return out
|
|
|
|
|
|
def read_lines(rel):
|
|
fp = os.path.join(ROOT, rel)
|
|
try:
|
|
with open(fp, "r", encoding="utf-8") as f:
|
|
return f.read().splitlines()
|
|
except Exception as e:
|
|
print(f"Warning: cannot read {rel}: {e}", file=sys.stderr)
|
|
return []
|
|
|
|
|
|
def build_all_lines(files):
|
|
lines = []
|
|
for rel in files:
|
|
lines.append(f"// ===== FILE: {rel} =====")
|
|
lines.extend(read_lines(rel))
|
|
lines.append("")
|
|
return lines
|
|
|
|
|
|
def sanitize_for_pdf(text):
|
|
# Remove characters that fpdf cannot handle
|
|
return text.replace("\t", " ").replace("\r", "")
|
|
|
|
|
|
def main():
|
|
files = collect_files()
|
|
print(f"Collected {len(files)} source files")
|
|
all_lines = build_all_lines(files)
|
|
print(f"Total lines: {len(all_lines)}")
|
|
|
|
total_pages = (len(all_lines) + LINES_PER_PAGE - 1) // LINES_PER_PAGE
|
|
print(f"Total pages at {LINES_PER_PAGE} lines/page: {total_pages}")
|
|
|
|
# Split into pages
|
|
pages = []
|
|
for i in range(0, len(all_lines), LINES_PER_PAGE):
|
|
pages.append(all_lines[i:i + LINES_PER_PAGE])
|
|
|
|
if total_pages <= 60:
|
|
selected = pages
|
|
else:
|
|
selected = pages[:30] + pages[-30:]
|
|
|
|
print(f"Selected pages: {len(selected)}")
|
|
|
|
pdf = SourcePDF(unit="mm", format="A4")
|
|
add_fonts(pdf)
|
|
pdf.set_auto_page_break(auto=True, margin=18)
|
|
|
|
for idx, page_lines in enumerate(selected, 1):
|
|
pdf.add_page()
|
|
pdf.set_font("body", "", 8)
|
|
pdf.set_text_color(0, 0, 0)
|
|
# Pad short pages
|
|
while len(page_lines) < LINES_PER_PAGE:
|
|
page_lines.append("")
|
|
for line in page_lines:
|
|
safe = sanitize_for_pdf(line)
|
|
# Truncate overly long lines to avoid overflow
|
|
if len(safe) > 140:
|
|
safe = safe[:137] + "..."
|
|
pdf.cell(0, 4.2, safe, new_x="LMARGIN", new_y="NEXT")
|
|
|
|
out = os.path.join(ROOT, "docs", "源程序文档.pdf")
|
|
pdf.output(out)
|
|
print(f"Saved {out}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|