使用python将markdown文件生成pdf文件

文章目录

  • [Python 将 Markdown 文档转换为 PDF 的多种方法](#Python 将 Markdown 文档转换为 PDF 的多种方法)
    • [一、使用 markdown + weasyprint(推荐)](#一、使用 markdown + weasyprint(推荐))
      • [1. 安装依赖](#1. 安装依赖)
      • [2. 基础转换](#2. 基础转换)
      • [2. 基础使用](#2. 基础使用)
    • [三、使用 python-markdown-pdf(命令行工具)](#三、使用 python-markdown-pdf(命令行工具))
      • [1. 安装](#1. 安装)
      • [2. Python调用](#2. Python调用)
    • [四、使用 pypandoc(通用文档转换)](#四、使用 pypandoc(通用文档转换))
      • [1. 安装](#1. 安装)
      • [2. Python代码](#2. Python代码)
      • [3. 创建自定义LaTeX模板](#3. 创建自定义LaTeX模板)
    • [五、使用 ReportLab(完全自定义PDF)](#五、使用 ReportLab(完全自定义PDF))
      • [1. 安装](#1. 安装)
      • [2. 自定义PDF生成器](#2. 自定义PDF生成器)
    • 六、完整的命令行工具
    • 七、批量转换工具
    • 八、Web服务版本(Flask)
    • 九、选择建议
    • 十、快速开始(最简方案)

Python 将 Markdown 文档转换为 PDF 的多种方法

一、使用 markdown + weasyprint(推荐)

1. 安装依赖

bash 复制代码
pip install markdown weasyprint jinja2

2. 基础转换

python 复制代码
import markdown
from weasyprint import HTML
import tempfile

def md_to_pdf_weasyprint(md_content, output_pdf):
    """将Markdown转换为PDF - 基础版本"""
    # 将Markdown转换为HTML
    html_content = markdown.markdown(md_content)
    
    # 添加基本CSS样式
    full_html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <style>
            body {{ font-family: Arial, sans-serif; line-height: 1.6; margin: 40px; }}
            h1, h2, h3 {{ color: #333; }}
            code {{ background-color: #f4f4f4; padding: 2px 4px; border-radius: 3px; }}
            pre {{ background-color: #f4f4f4; padding: 10px; border-radius: 5px; overflow: auto; }}
            blockquote {{ border-left: 4px solid #ccc; margin-left: 0; padding-left: 20px; color: #666; }}
            table {{ border-collapse: collapse; width: 100%; }}
            th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
            th {{ background-color: #f4f4f4; }}
            img {{ max-width: 100%; height: auto; }}
        </style>
    </head>
    <body>
        {html_content}
    </body>
    </html>
    """
    
    # 生成PDF
    HTML(string=full_html).write_pdf(output_pdf)
    print(f"PDF已生成: {output_pdf}")

# 使用示例
md_content = """
# 标题
这是一个段落。

## 子标题
- 列表项1
- 列表项2

```python
print("Hello World")

这是一个引用

粗体斜体

"""

md_to_pdf_weasyprint(md_content, "output.pdf")

复制代码
### 3. 增强版(支持更多特性)
```python
import markdown
from weasyprint import HTML
import os
from pathlib import Path

class MarkdownToPDF:
    def __init__(self, style="default"):
        """初始化转换器"""
        self.styles = {
            "default": self._default_style(),
            "academic": self._academic_style(),
            "modern": self._modern_style(),
            "minimal": self._minimal_style()
        }
        self.style = self.styles.get(style, self.styles["default"])
    
    def _default_style(self):
        """默认样式"""
        return """
        body {
            font-family: 'Helvetica Neue', Arial, sans-serif;
            line-height: 1.8;
            margin: 50px;
            color: #333;
            font-size: 12pt;
        }
        h1 {
            color: #2c3e50;
            border-bottom: 3px solid #3498db;
            padding-bottom: 10px;
            margin-top: 40px;
        }
        h2 {
            color: #34495e;
            border-left: 5px solid #3498db;
            padding-left: 15px;
            margin-top: 30px;
        }
        h3 {
            color: #7f8c8d;
            margin-top: 25px;
        }
        p {
            margin: 15px 0;
            text-align: justify;
        }
        ul, ol {
            margin: 15px 0;
            padding-left: 30px;
        }
        li {
            margin: 8px 0;
        }
        code {
            font-family: 'Courier New', monospace;
            background-color: #ecf0f1;
            padding: 2px 6px;
            border-radius: 3px;
            font-size: 0.9em;
        }
        pre {
            background-color: #2c3e50;
            color: #ecf0f1;
            padding: 15px;
            border-radius: 5px;
            overflow: auto;
            margin: 20px 0;
        }
        pre code {
            background-color: transparent;
            padding: 0;
        }
        blockquote {
            background-color: #f8f9fa;
            border-left: 5px solid #95a5a6;
            margin: 20px 0;
            padding: 15px 25px;
            font-style: italic;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 20px 0;
        }
        th {
            background-color: #3498db;
            color: white;
            padding: 12px;
            text-align: left;
        }
        td {
            padding: 10px;
            border-bottom: 1px solid #ddd;
        }
        tr:nth-child(even) {
            background-color: #f2f2f2;
        }
        img {
            max-width: 100%;
            height: auto;
            margin: 20px 0;
            border-radius: 5px;
        }
        hr {
            border: none;
            height: 1px;
            background-color: #ddd;
            margin: 30px 0;
        }
        a {
            color: #2980b9;
            text-decoration: none;
        }
        a:hover {
            text-decoration: underline;
        }
        .page-break {
            page-break-after: always;
        }
        .header {
            text-align: center;
            margin-bottom: 40px;
            border-bottom: 2px solid #3498db;
            padding-bottom: 20px;
        }
        .footer {
            text-align: center;
            margin-top: 40px;
            color: #7f8c8d;
            font-size: 0.9em;
        }
        """
    
    def _academic_style(self):
        """学术风格"""
        return self._default_style() + """
        body {
            font-family: 'Times New Roman', serif;
            line-height: 1.5;
        }
        h1, h2, h3 {
            font-family: 'Georgia', serif;
        }
        """
    
    def convert_file(self, md_file_path, pdf_file_path=None, 
                     title=None, author=None, date=None):
        """转换Markdown文件为PDF"""
        if not pdf_file_path:
            pdf_file_path = Path(md_file_path).with_suffix('.pdf')
        
        # 读取Markdown文件
        with open(md_file_path, 'r', encoding='utf-8') as f:
            md_content = f.read()
        
        return self.convert_text(md_content, pdf_file_path, title, author, date)
    
    def convert_text(self, md_text, pdf_file_path, 
                    title=None, author=None, date=None):
        """转换Markdown文本为PDF"""
        # 使用扩展支持更多Markdown语法
        md_extensions = [
            'extra',          # 支持表格、缩写等
            'codehilite',     # 代码高亮
            'toc',            # 目录
            'tables',         # 表格
            'fenced_code',    # 围栏代码块
            'footnotes',      # 脚注
            'attr_list',      # 属性列表
            'def_list',       # 定义列表
            'meta',           # 元数据
        ]
        
        # 转换Markdown为HTML
        html_body = markdown.markdown(md_text, extensions=md_extensions)
        
        # 构建完整的HTML文档
        full_html = self._build_html_document(html_body, title, author, date)
        
        # 生成PDF
        HTML(string=full_html).write_pdf(pdf_file_path)
        print(f"✅ PDF已生成: {pdf_file_path}")
        
        return pdf_file_path
    
    def _build_html_document(self, body_content, title, author, date):
        """构建完整的HTML文档"""
        header_html = ""
        if title:
            header_html = f"""
            <div class="header">
                <h1>{title}</h1>
                {f'<p class="author">作者: {author}</p>' if author else ''}
                {f'<p class="date">日期: {date}</p>' if date else ''}
            </div>
            <hr>
            """
        
        footer_html = """
        <div class="footer">
            <p>第 <span class="page-number"></span> 页</p>
        </div>
        """
        
        full_html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="utf-8">
            <style>
                @page {{
                    size: A4;
                    margin: 2cm;
                    
                    @top-center {{
                        content: "{title if title else '文档'}";
                        font-size: 10pt;
                        color: #666;
                    }}
                    @bottom-center {{
                        content: "第 " counter(page) " 页";
                        font-size: 10pt;
                        color: #666;
                    }}
                }}
                
                {self.style}
                
                /* 打印样式 */
                @media print {{
                    .page-break {{
                        page-break-after: always;
                    }}
                }}
            </style>
        </head>
        <body>
            {header_html}
            {body_content}
            {footer_html}
            
            <script>
                // 更新页码
                document.addEventListener('DOMContentLoaded', function() {{
                    const pageNumbers = document.querySelectorAll('.page-number');
                    pageNumbers.forEach(el => {{
                        el.textContent = document.querySelectorAll('.page').length || 1;
                    }});
                }});
            </script>
        </body>
        </html>
        """
        
        return full_html

# 使用示例
converter = MarkdownToPDF(style="modern")

# 从文件转换
converter.convert_file(
    "document.md",
    "output.pdf",
    title="技术文档",
    author="张三",
    date="2024-01-20"
)

# 从文本转换
md_text = """
# 项目报告

## 概述
这是一个示例文档...

### 特性
1. 功能一
2. 功能二
3. 功能三

```python
def hello():
    print("Hello, World!")
姓名 年龄 职位
张三 30 工程师
李四 28 设计师
"""

converter.convert_text(md_text, "report.pdf", title="项目报告")

复制代码
## 二、使用 md2pdf 库

### 1. 安装
```bash
pip install md2pdf

2. 基础使用

python 复制代码
from md2pdf.core import md2pdf

def convert_md_to_pdf(md_file, pdf_file):
    """使用md2pdf库转换"""
    
    # 读取Markdown文件
    with open(md_file, 'r', encoding='utf-8') as f:
        md_content = f.read()
    
    # 自定义CSS样式
    css = """
    body {
        font-family: 'Arial', sans-serif;
        line-height: 1.6;
        margin: 40px;
    }
    h1 {
        color: #2c3e50;
        border-bottom: 2px solid #3498db;
    }
    code {
        background-color: #f8f9fa;
        padding: 2px 4px;
        border-radius: 3px;
    }
    """
    
    # 转换PDF
    md2pdf(
        pdf_file,
        md_content=md_content,
        css_file_path=None,
        base_url=None,
        css=css
    )
    
    print(f"PDF已生成: {pdf_file}")

# 使用
convert_md_to_pdf("input.md", "output.pdf")

三、使用 python-markdown-pdf(命令行工具)

1. 安装

bash 复制代码
pip install markdown-pdf

2. Python调用

python 复制代码
import subprocess
import os

def convert_with_markdown_pdf(md_file, pdf_file, options=None):
    """
    使用markdown-pdf命令行工具转换
    
    可选参数:
    --paper-format A4
    --paper-orientation portrait
    --css styles.css
    """
    
    cmd = ["markdown-pdf", md_file, "-o", pdf_file]
    
    if options:
        cmd.extend(options)
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        print(f"转换成功: {pdf_file}")
        return True
    except subprocess.CalledProcessError as e:
        print(f"转换失败: {e.stderr}")
        return False

# 使用示例
convert_with_markdown_pdf(
    "document.md",
    "document.pdf",
    ["--paper-format", "A4", "--paper-orientation", "portrait"]
)

四、使用 pypandoc(通用文档转换)

1. 安装

bash 复制代码
# 安装pypandoc
pip install pypandoc

# Windows还需要安装pandoc
# 从 https://github.com/jgm/pandoc/releases 下载安装

# Linux安装pandoc
# sudo apt-get install pandoc  # Ubuntu/Debian
# sudo yum install pandoc      # CentOS/RHEL

2. Python代码

python 复制代码
import pypandoc
import os

def convert_with_pandoc(md_file, pdf_file, template=None):
    """使用pandoc转换(功能最强大)"""
    
    # 可选的模板文件
    extra_args = []
    if template and os.path.exists(template):
        extra_args = ['--template', template]
    
    # 转换参数
    output = pypandoc.convert_file(
        md_file,
        'pdf',
        outputfile=pdf_file,
        extra_args=extra_args + [
            '--pdf-engine=xelatex',  # 使用xelatex引擎,支持中文
            '--variable', 'mainfont="Microsoft YaHei"',  # 中文字体
            '--variable', 'fontsize=12pt',
            '--variable', 'papersize=a4',
            '--variable', 'geometry:margin=2cm',
            '--table-of-contents',  # 生成目录
            '--number-sections',  # 章节编号
        ]
    )
    
    print(f"PDF已生成: {pdf_file}")
    return output

# 使用示例
convert_with_pandoc("input.md", "output.pdf")

# 使用自定义模板
convert_with_pandoc("input.md", "output.pdf", template="template.tex")

3. 创建自定义LaTeX模板

latex 复制代码
% template.tex
\documentclass[12pt,a4paper]{article}
\usepackage{xeCJK}
\usepackage[margin=2cm]{geometry}
\usepackage{hyperref}
\usepackage{listings}
\usepackage{xcolor}

% 中文字体设置
\setCJKmainfont{Microsoft YaHei}
\setCJKsansfont{SimHei}
\setCJKmonofont{FangSong}

% 代码高亮设置
\lstset{
    basicstyle=\ttfamily\small,
    keywordstyle=\color{blue},
    commentstyle=\color{green},
    stringstyle=\color{red},
    breaklines=true,
    frame=single,
    numbers=left,
    numberstyle=\tiny\color{gray}
}

\title{$title$}
\author{$author$}
\date{$date$}

\begin{document}

\maketitle
\tableofcontents
\newpage

$body$

\end{document}

五、使用 ReportLab(完全自定义PDF)

1. 安装

bash 复制代码
pip install reportlab markdown

2. 自定义PDF生成器

python 复制代码
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
import markdown
from html.parser import HTMLParser
import re

class MarkdownToPDFReportLab:
    """使用ReportLab完全自定义PDF生成"""
    
    def __init__(self):
        self.styles = getSampleStyleSheet()
        self._customize_styles()
        
    def _customize_styles(self):
        """自定义样式"""
        # 标题样式
        self.styles.add(ParagraphStyle(
            name='CustomHeading1',
            parent=self.styles['Heading1'],
            fontSize=24,
            spaceAfter=30,
            textColor=colors.HexColor('#2c3e50'),
            fontName='Helvetica-Bold'
        ))
        
        self.styles.add(ParagraphStyle(
            name='CustomHeading2',
            parent=self.styles['Heading2'],
            fontSize=18,
            spaceAfter=20,
            textColor=colors.HexColor('#34495e')
        ))
        
        # 代码样式
        self.styles.add(ParagraphStyle(
            name='Code',
            parent=self.styles['Code'],
            fontName='Courier',
            fontSize=10,
            backColor=colors.HexColor('#f8f9fa'),
            borderColor=colors.gray,
            borderWidth=1,
            borderPadding=5,
            leftIndent=20,
            spaceBefore=10,
            spaceAfter=10
        ))
        
        # 正文样式
        self.styles.add(ParagraphStyle(
            name='NormalCustom',
            parent=self.styles['Normal'],
            fontSize=12,
            leading=16,
            spaceAfter=12
        ))
        
        # 引用样式
        self.styles.add(ParagraphStyle(
            name='Blockquote',
            parent=self.styles['Normal'],
            leftIndent=20,
            fontName='Helvetica-Oblique',
            textColor=colors.gray,
            borderLeftColor=colors.gray,
            borderLeftWidth=3,
            borderLeftPadding=10,
            spaceBefore=10,
            spaceAfter=10
        ))
    
    def convert(self, md_file, pdf_file):
        """转换Markdown文件"""
        # 读取Markdown
        with open(md_file, 'r', encoding='utf-8') as f:
            md_content = f.read()
        
        # 转换为HTML
        html_content = markdown.markdown(md_content)
        
        # 解析HTML并构建PDF元素
        elements = self._parse_html(html_content)
        
        # 创建PDF文档
        doc = SimpleDocTemplate(
            pdf_file,
            pagesize=A4,
            rightMargin=72,
            leftMargin=72,
            topMargin=72,
            bottomMargin=72
        )
        
        # 构建PDF
        doc.build(elements)
        print(f"PDF已生成: {pdf_file}")
    
    def _parse_html(self, html_content):
        """解析HTML并转换为ReportLab元素"""
        elements = []
        
        # 简单的HTML解析(实际项目应使用BeautifulSoup)
        lines = html_content.split('\n')
        
        for line in lines:
            line = line.strip()
            if not line:
                continue
            
            # 处理标题
            if line.startswith('<h1>'):
                text = line[4:-5]
                elements.append(Paragraph(text, self.styles['CustomHeading1']))
            
            elif line.startswith('<h2>'):
                text = line[4:-5]
                elements.append(Paragraph(text, self.styles['CustomHeading2']))
            
            # 处理段落
            elif line.startswith('<p>'):
                text = line[3:-4]
                elements.append(Paragraph(text, self.styles['NormalCustom']))
                elements.append(Spacer(1, 12))
            
            # 处理代码块
            elif line.startswith('<pre><code>'):
                text = line[11:-12]
                elements.append(Paragraph(text, self.styles['Code']))
            
            # 处理引用
            elif line.startswith('<blockquote>'):
                text = line[12:-13]
                elements.append(Paragraph(text, self.styles['Blockquote']))
            
            # 处理水平线
            elif line == '<hr/>':
                elements.append(Spacer(1, 24))
            
            # 处理列表(简化版)
            elif line.startswith('<li>'):
                text = line[4:-5]
                elements.append(Paragraph(f"• {text}", self.styles['NormalCustom']))
        
        return elements

# 使用
converter = MarkdownToPDFReportLab()
converter.convert("input.md", "output.pdf")

六、完整的命令行工具

python 复制代码
#!/usr/bin/env python3
"""
Markdown转PDF命令行工具
用法: python md2pdf.py input.md [output.pdf] [选项]
"""

import argparse
import sys
from pathlib import Path
from datetime import datetime

def main():
    parser = argparse.ArgumentParser(
        description='将Markdown文件转换为PDF',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  %(prog)s document.md                       # 转换单个文件
  %(prog)s document.md -o output.pdf         # 指定输出文件
  %(prog)s *.md                              # 批量转换
  %(prog)s input.md -t "我的文档" -a "作者"  # 添加标题和作者
        """
    )
    
    parser.add_argument('input', nargs='+', help='输入的Markdown文件')
    parser.add_argument('-o', '--output', help='输出PDF文件路径')
    parser.add_argument('-t', '--title', help='文档标题')
    parser.add_argument('-a', '--author', help='作者')
    parser.add_argument('-d', '--date', help='日期')
    parser.add_argument('-s', '--style', 
                       choices=['default', 'academic', 'modern', 'minimal'],
                       default='default',
                       help='样式主题')
    parser.add_argument('--toc', action='store_true', help='生成目录')
    parser.add_argument('--page-numbers', action='store_true', 
                       help='添加页码')
    parser.add_argument('--verbose', action='store_true', help='显示详细信息')
    
    args = parser.parse_args()
    
    # 导入转换器(确保已安装依赖)
    try:
        from markdown_to_pdf import MarkdownToPDF  # 假设这是我们的转换器类
    except ImportError:
        print("错误: 请先安装依赖")
        print("pip install markdown weasyprint jinja2")
        sys.exit(1)
    
    # 处理每个输入文件
    for input_file in args.input:
        if not Path(input_file).exists():
            print(f"警告: 文件不存在 - {input_file}")
            continue
        
        # 确定输出文件名
        if args.output:
            if len(args.input) > 1:
                print("警告: 批量转换时不能使用--output参数")
                output_file = Path(input_file).with_suffix('.pdf')
            else:
                output_file = args.output
        else:
            output_file = Path(input_file).with_suffix('.pdf')
        
        # 执行转换
        try:
            if args.verbose:
                print(f"正在转换: {input_file} -> {output_file}")
            
            converter = MarkdownToPDF(style=args.style)
            
            # 如果没有指定标题,使用文件名
            title = args.title or Path(input_file).stem
            
            # 如果没有指定日期,使用当前日期
            date = args.date or datetime.now().strftime('%Y-%m-%d')
            
            converter.convert_file(
                input_file,
                output_file,
                title=title,
                author=args.author,
                date=date
            )
            
            if args.verbose:
                print(f"✅ 转换完成: {output_file}")
            
        except Exception as e:
            print(f"❌ 转换失败 {input_file}: {e}")
            if args.verbose:
                import traceback
                traceback.print_exc()

if __name__ == '__main__':
    main()

七、批量转换工具

python 复制代码
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import time

class BatchMarkdownToPDF:
    """批量转换工具"""
    
    def __init__(self, input_dir, output_dir=None, style='default', max_workers=4):
        self.input_dir = Path(input_dir)
        
        if output_dir:
            self.output_dir = Path(output_dir)
            self.output_dir.mkdir(parents=True, exist_ok=True)
        else:
            self.output_dir = self.input_dir / 'pdf_output'
            self.output_dir.mkdir(exist_ok=True)
        
        self.style = style
        self.max_workers = max_workers
        
    def find_markdown_files(self):
        """查找所有Markdown文件"""
        patterns = ['*.md', '*.markdown', '*.mdown']
        files = []
        
        for pattern in patterns:
            files.extend(self.input_dir.rglob(pattern))
        
        return files
    
    def convert_single(self, md_file):
        """转换单个文件"""
        try:
            relative_path = md_file.relative_to(self.input_dir)
            output_path = self.output_dir / relative_path.with_suffix('.pdf')
            
            # 创建输出目录结构
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 导入转换器
            from markdown_to_pdf import MarkdownToPDF
            
            converter = MarkdownToPDF(style=self.style)
            converter.convert_file(
                str(md_file),
                str(output_path),
                title=md_file.stem
            )
            
            return (True, md_file, output_path)
            
        except Exception as e:
            return (False, md_file, str(e))
    
    def convert_all(self):
        """批量转换所有文件"""
        md_files = self.find_markdown_files()
        
        if not md_files:
            print(f"在 {self.input_dir} 中没有找到Markdown文件")
            return
        
        print(f"找到 {len(md_files)} 个Markdown文件")
        print(f"输出目录: {self.output_dir}")
        
        start_time = time.time()
        success_count = 0
        
        # 使用多线程加速
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            results = list(executor.map(self.convert_single, md_files))
        
        # 统计结果
        for success, input_file, result in results:
            if success:
                print(f"✅ 成功: {input_file} -> {result}")
                success_count += 1
            else:
                print(f"❌ 失败: {input_file} - {result}")
        
        elapsed_time = time.time() - start_time
        
        print(f"\n转换完成!")
        print(f"成功: {success_count}/{len(md_files)}")
        print(f"耗时: {elapsed_time:.2f}秒")
        
        return success_count

# 使用示例
if __name__ == '__main__':
    # 批量转换整个目录
    batch_converter = BatchMarkdownToPDF(
        input_dir='./docs',
        output_dir='./pdf_docs',
        style='modern',
        max_workers=4
    )
    
    batch_converter.convert_all()

八、Web服务版本(Flask)

python 复制代码
from flask import Flask, request, send_file, render_template_string
import tempfile
import os
from markdown_to_pdf import MarkdownToPDF  # 使用前面的转换器类

app = Flask(__name__)

HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <title>Markdown转PDF在线工具</title>
    <style>
        body { font-family: Arial; max-width: 800px; margin: 0 auto; padding: 20px; }
        .container { display: flex; gap: 20px; }
        .editor { flex: 1; }
        .preview { flex: 1; }
        textarea { width: 100%; height: 400px; font-family: monospace; }
        button { padding: 10px 20px; margin: 10px 0; }
        .options { margin: 20px 0; }
        label { display: block; margin: 5px 0; }
    </style>
</head>
<body>
    <h1>Markdown转PDF在线工具</h1>
    
    <form method="post">
        <div class="container">
            <div class="editor">
                <h3>编辑Markdown</h3>
                <textarea name="markdown" placeholder="输入Markdown内容...">{{ markdown }}</textarea>
            </div>
            
            <div class="preview">
                <h3>实时预览</h3>
                <div id="preview">{{ preview|safe }}</div>
            </div>
        </div>
        
        <div class="options">
            <label>标题: <input type="text" name="title" value="{{ title }}"></label>
            <label>作者: <input type="text" name="author" value="{{ author }}"></label>
            <label>样式: 
                <select name="style">
                    <option value="default" {{ 'selected' if style=='default' }}>默认</option>
                    <option value="modern" {{ 'selected' if style=='modern' }}>现代</option>
                    <option value="academic" {{ 'selected' if style=='academic' }}>学术</option>
                </select>
            </label>
        </div>
        
        <button type="submit" name="action" value="preview">预览</button>
        <button type="submit" name="action" value="download">下载PDF</button>
    </form>
    
    <script>
        // 实时预览
        document.querySelector('textarea').addEventListener('input', function() {
            // 这里可以添加实时预览的JavaScript代码
        });
    </script>
</body>
</html>
"""

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        markdown = request.form.get('markdown', '')
        title = request.form.get('title', '文档')
        author = request.form.get('author', '')
        style = request.form.get('style', 'default')
        action = request.form.get('action', 'preview')
        
        if action == 'preview':
            # 预览模式
            import markdown as md
            preview_html = md.markdown(markdown)
            
            return render_template_string(
                HTML_TEMPLATE,
                markdown=markdown,
                preview=preview_html,
                title=title,
                author=author,
                style=style
            )
        
        elif action == 'download':
            # 生成PDF
            try:
                with tempfile.NamedTemporaryFile(
                    suffix='.pdf', 
                    delete=False
                ) as tmp_file:
                    pdf_path = tmp_file.name
                
                converter = MarkdownToPDF(style=style)
                converter.convert_text(
                    markdown, 
                    pdf_path,
                    title=title,
                    author=author
                )
                
                return send_file(
                    pdf_path,
                    as_attachment=True,
                    download_name=f'{title}.pdf'
                )
                
            except Exception as e:
                return f"错误: {e}"
    
    # GET请求显示空表单
    return render_template_string(
        HTML_TEMPLATE,
        markdown='# 标题\n\n这里是内容...',
        preview='<h1>标题</h1><p>这里是内容...</p>',
        title='文档',
        author='',
        style='default'
    )

if __name__ == '__main__':
    app.run(debug=True, port=5000)

九、选择建议

方法 优点 缺点 适用场景
weasyprint + markdown 简单易用,样式灵活,支持CSS 需要安装依赖较多 大多数场景,推荐
pypandoc 功能最强大,支持多种格式 需要安装pandoc,配置复杂 需要高级格式控制
md2pdf库 专门为此设计,简单 功能相对简单 快速简单转换
ReportLab 完全控制PDF生成 代码复杂,需要手动解析 需要高度自定义PDF
markdown-pdf 命令行工具,方便集成 需要在系统安装 脚本或CI/CD环境

十、快速开始(最简方案)

python 复制代码
# requirements.txt
markdown
weasyprint

# simple_converter.py
import markdown
from weasyprint import HTML

def quick_convert(md_text, output_file):
    """最简单快速的转换"""
    html = markdown.markdown(md_text)
    full_html = f"<html><body>{html}</body></html>"
    HTML(string=full_html).write_pdf(output_file)
    print(f"PDF已保存: {output_file}")

# 使用
with open("README.md", "r", encoding="utf-8") as f:
    quick_convert(f.read(), "README.pdf")

推荐方案 :使用 weasyprint + markdown,功能完善,样式灵活,社区活跃,适合大多数需求。


结束语

Flutter是一个由Google开发的开源UI工具包,它可以让您在不同平台上创建高质量、美观的应用程序,而无需编写大量平台特定的代码。我将学习和深入研究Flutter的方方面面。从基础知识到高级技巧,从UI设计到性能优化,欢饮关注一起讨论学习,共同进入Flutter的精彩世界!

相关推荐
凯_kyle2 小时前
Python 算法竞赛 —— 基础篇(更新ing)
笔记·python·算法
j_xxx404_2 小时前
C++算法入门:二分查找合集(二分查找|在排序数组中查找元素的第一个和最后一个位置)
开发语言·c++
天远Date Lab2 小时前
Java微服务实战:聚合型“全能小微企业报告”接口的调用与数据清洗
java·大数据·python·微服务
ss2732 小时前
阻塞队列:ArrayBlockingQueue如何用Lock与Condition实现高效并发控制
开发语言·python
CodeCraft Studio2 小时前
Vaadin 25 正式发布:回归标准Java Web,让企业级开发更简单、更高效
java·开发语言·前端·vaadin·java web 框架·纯java前端框架·企业级java ui框架
Shirley~~2 小时前
PPTist 幻灯片工具栏Toolbar部分
开发语言·前端·javascript
|晴 天|2 小时前
Promise 与 async/await 错误处理最佳实践指南
开发语言·前端·javascript
_OP_CHEN2 小时前
【Python基础】(三)Python 语法基础进阶:条件循环 + 实战案例,从入门到精通的核心跳板
开发语言·python·python入门·条件语句·循环语句·python基础语法
苹果电脑的鑫鑫2 小时前
.eslintrc.js这个文件作用
开发语言·javascript·ecmascript