| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- import re
- from reportlab.lib.pagesizes import A4
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
- from reportlab.lib import colors
- from reportlab.pdfbase import pdfmetrics
- from reportlab.pdfbase.cidfonts import UnicodeCIDFont
- from config.settings import SERVER_HOST, REPORT_OUTPUT_DIR
- # 注册中文字体
- pdfmetrics.registerFont(UnicodeCIDFont('STSong-Light'))
- FONT_CN = 'STSong-Light'
- TABLE_BASE_STYLE = [
- ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
- ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
- ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
- ('TOPPADDING', (0, 0), (-1, -1), 6),
- ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
- ('LEFTPADDING', (0, 0), (-1, -1), 4),
- ('RIGHTPADDING', (0, 0), (-1, -1), 4),
- ('GRID', (0, 0), (-1, -1), 1, colors.black),
- ]
- def convert_bold(text: str) -> str:
- return re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', text)
- def markdown_to_pdf(md_content: str, save_path: str) -> None:
- """Markdown转PDF,支持四级标题、加粗、表格"""
- doc = SimpleDocTemplate(
- save_path,
- pagesize=A4,
- rightMargin=30, leftMargin=30, topMargin=30, bottomMargin=30
- )
- story = []
- styles = getSampleStyleSheet()
- # 自定义样式
- style_h1 = ParagraphStyle('CustomH1', parent=styles['Heading1'], fontSize=16, spaceAfter=12, fontName=FONT_CN)
- style_h2 = ParagraphStyle('CustomH2', parent=styles['Heading2'], fontSize=14, spaceAfter=10, fontName=FONT_CN)
- style_h3 = ParagraphStyle('CustomH3', parent=styles['Heading3'], fontSize=12, spaceAfter=8, fontName=FONT_CN)
- style_h4 = ParagraphStyle('CustomH4', parent=styles['Heading3'], fontSize=11, spaceAfter=6, fontName=FONT_CN)
- style_normal = ParagraphStyle('CustomNormal', parent=styles['Normal'], fontSize=11, leading=18, fontName=FONT_CN)
- table_text_style = ParagraphStyle('TableText', parent=styles['Normal'], fontSize=10, fontName=FONT_CN, leading=14)
- lines = md_content.splitlines()
- table_rows = []
- in_table = False
- for raw_line in lines:
- line = raw_line.strip()
- if not line:
- continue
- # 四级标题
- if line.startswith("#### "):
- txt = convert_bold(line.lstrip("#### ").strip())
- story.append(Paragraph(txt, style_h4))
- story.append(Spacer(1, 4))
- continue
- elif line.startswith("### "):
- txt = convert_bold(line.lstrip("### ").strip())
- story.append(Paragraph(txt, style_h3))
- story.append(Spacer(1, 4))
- continue
- elif line.startswith("## "):
- txt = convert_bold(line.lstrip("## ").strip())
- story.append(Paragraph(txt, style_h2))
- story.append(Spacer(1, 5))
- continue
- elif line.startswith("# "):
- txt = convert_bold(line.lstrip("# ").strip())
- story.append(Paragraph(txt, style_h1))
- story.append(Spacer(1, 6))
- continue
- # 表格分隔符
- if all(c in "-| " for c in line):
- continue
- # 表格行
- if line.startswith("|") and line.endswith("|"):
- in_table = True
- cells = [cell.strip() for cell in line.split("|")[1:-1]]
- paras = [Paragraph(convert_bold(c), table_text_style) for c in cells]
- table_rows.append(paras)
- continue
- # 结束表格渲染
- if in_table:
- in_table = False
- if table_rows:
- col_cnt = len(table_rows[0])
- col_w = [530 / col_cnt] * col_cnt
- tbl = Table(table_rows, colWidths=col_w, repeatRows=1)
- tbl.setStyle(TableStyle(TABLE_BASE_STYLE))
- story.append(tbl)
- story.append(Spacer(1, 10))
- table_rows = []
- # 普通正文
- fmt_line = convert_bold(line)
- story.append(Paragraph(fmt_line, style_normal))
- story.append(Spacer(1, 3))
- # 剩余表格
- if in_table and table_rows:
- col_cnt = len(table_rows[0])
- col_w = [530 / col_cnt] * col_cnt
- tbl = Table(table_rows, colWidths=col_w, repeatRows=1)
- tbl.setStyle(TableStyle(TABLE_BASE_STYLE))
- story.append(tbl)
- story.append(Spacer(1, 10))
- doc.build(story)
- def get_pdf_download_url(pdf_filename: str) -> str:
- return f"{SERVER_HOST}/static/report_pdf/{pdf_filename}"
|