Browse Source

chore: 添加.gitignore文件以忽略Python缓存文件

你的名字 1 week ago
parent
commit
13861d01a3
5 changed files with 556 additions and 46 deletions
  1. 2 0
      .gitignore
  2. 66 9
      Dockerfile
  3. 225 4
      README.md
  4. 11 4
      docker-compose.yml
  5. 252 29
      main.py

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+__pycache__/
+*.pyc

+ 66 - 9
Dockerfile

@@ -1,14 +1,71 @@
-# FROM mcr.microsoft.com/dotnet/aspnet:7.0 as build
-FROM ubuntu:18.04 as builder
+# 使用Ubuntu 20.04作为基础镜像,提供更好的兼容性和稳定性
+FROM ubuntu:20.04 AS builder
 
-RUN apt update && apt install -y wget
-RUN wget http://archive.ubuntu.com/ubuntu/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb
-RUN apt install -y ./libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb
-RUN wget https://github.com/pdf2htmlEX/pdf2htmlEX/releases/download/v0.18.8.rc1/pdf2htmlEX-0.18.8.rc1-master-20200630-Ubuntu-bionic-x86_64.deb
-RUN apt install -y ./pdf2htmlEX-0.18.8.rc1-master-20200630-Ubuntu-bionic-x86_64.deb
-RUN apt install -y python3
+# 设置环境变量,避免交互式安装
+ENV DEBIAN_FRONTEND=noninteractive
 
+# 更新软件包列表并安装必要的依赖
+RUN apt-get update && apt-get install -y \
+    wget \
+    dpkg \
+    libpng16-16 \
+    libjpeg-turbo8 \
+    libfontconfig1 \
+    libfreetype6 \
+    libglib2.0-0 \
+    libxrender1 \
+    libxext6 \
+    && rm -rf /var/lib/apt/lists/*
+
+# 下载并安装pdf2htmlEX
+# 注意:pdf2htmlEX官方仓库已停止维护,这里使用的是已知稳定的版本
+WORKDIR /tmp
+
+# 下载pdf2htmlEX依赖包和主程序
+RUN wget -q https://github.com/pdf2htmlEX/pdf2htmlEX/releases/download/v0.18.8.rc1/pdf2htmlEX-0.18.8.rc1-master-20200630-Ubuntu-focal-x86_64.deb
+
+# 安装pdf2htmlEX
+RUN dpkg -i pdf2htmlEX-0.18.8.rc1-master-20200630-Ubuntu-focal-x86_64.deb || apt-get -f install -y
+
+# 清理临时文件
+RUN rm -rf /tmp/*.deb
+
+# 第二阶段:创建运行时镜像
+FROM ubuntu:20.04
+
+# 设置环境变量
+ENV DEBIAN_FRONTEND=noninteractive
+ENV PYTHONUNBUFFERED=1
+
+# 安装运行时依赖
+RUN apt-get update && apt-get install -y \
+    python3 \
+    python3-pip \
+    libpng16-16 \
+    libjpeg-turbo8 \
+    libfontconfig1 \
+    libfreetype6 \
+    libglib2.0-0 \
+    libxrender1 \
+    libxext6 \
+    && rm -rf /var/lib/apt/lists/*
+
+# 从builder阶段复制pdf2htmlEX
+COPY --from=builder /usr/bin/pdf2htmlEX /usr/bin/pdf2htmlEX
+COPY --from=builder /usr/share/pdf2htmlEX /usr/share/pdf2htmlEX
+
+# 创建工作目录
 WORKDIR /app
+
+# 复制Python脚本
 COPY main.py .
+
+# 设置卷,允许挂载外部目录
 VOLUME [ "/app" ]
-ENTRYPOINT ["python3", "main.py"]
+
+# 设置入口点,允许传递命令行参数
+# 使用ENTRYPOINT + CMD组合,允许用户覆盖默认行为
+ENTRYPOINT [ "python3", "/app/main.py" ]
+
+# 默认命令(空参数,使用脚本默认值)
+CMD [ ]

+ 225 - 4
README.md

@@ -1,12 +1,233 @@
-# pdf2html
+# pdf2html - PDF批量转换为HTML工具
 
-pdf批量转为html
+一个简单易用的工具,用于将PDF文件批量转换为HTML格式,基于pdf2htmlEX。
 
+## 功能特性
+
+- **批量转换**:一次性转换目录中所有的PDF文件
+- **递归搜索**:支持递归搜索子目录中的PDF文件
+- **命令行参数**:灵活的命令行参数配置
+- **Docker支持**:提供Docker镜像,简化部署和使用
+- **进度显示**:清晰的转换进度和结果统计
+- **错误处理**:完善的错误处理和超时机制
+
+## 技术栈
+
+- **Python 3**:主要开发语言
+- **pdf2htmlEX**:PDF转HTML的核心工具
+- **Docker**:容器化部署
+
+## 安装和构建
+
+### 本地安装
+
+1. 确保已安装Python 3和pdf2htmlEX
+2. 克隆或下载此项目
+
+### Docker构建
+
+```bash
+# 进入项目目录
+cd pdf2html
+
+# 构建Docker镜像
+docker build -t pdf2html:latest .
 ```
-docker build --pull --rm -f "pdf2html/Dockerfile" -t pdf2html:latest "pdf2html" 
 
-alias pdf2html='docker run --rm -it -v `pwd`:/app pdf2html:latest'
+或者使用docker-compose:
+
+```bash
+docker-compose build
+```
+
+## 使用方法
+
+### 基本用法
+
+#### 本地使用
+
+```bash
+# 转换当前目录下的所有PDF文件
+python main.py
+
+# 显示帮助信息
+python main.py --help
+```
+
+#### Docker使用
+
+```bash
+# 转换当前目录下的所有PDF文件
+docker run --rm -v $(pwd):/app pdf2html:latest
+
+# 显示帮助信息
+docker run --rm pdf2html:latest --help
+```
+
+### 命令行参数
+
+| 参数 | 简写 | 默认值 | 说明 |
+|------|------|--------|------|
+| `--input-dir` | `-i` | `.` (当前目录) | 输入目录,包含PDF文件的目录 |
+| `--output-dir` | `-o` | `htmls` | 输出目录,保存HTML文件的目录 |
+| `--recursive` | `-r` | `True` | 递归搜索子目录中的PDF文件 |
+| `--no-recursive` | - | - | 不递归搜索子目录 |
+| `--zoom` | `-z` | `1.3` | 转换时的缩放比例 |
+| `--help` | `-h` | - | 显示帮助信息 |
+
+### 使用示例
+
+#### 1. 基本转换
+
+转换当前目录下的所有PDF文件到默认的`htmls`目录:
+
+```bash
+# 本地使用
+python main.py
+
+# Docker使用
+docker run --rm -v $(pwd):/app pdf2html:latest
+```
+
+#### 2. 指定输入和输出目录
+
+转换`./pdfs`目录中的PDF文件到`./output`目录:
+
+```bash
+# 本地使用
+python main.py -i ./pdfs -o ./output
+
+# Docker使用
+docker run --rm -v $(pwd):/app pdf2html:latest -i /app/pdfs -o /app/output
+```
+
+#### 3. 不递归搜索子目录
+
+只转换当前目录的PDF文件,不搜索子目录:
+
+```bash
+# 本地使用
+python main.py --no-recursive
+
+# Docker使用
+docker run --rm -v $(pwd):/app pdf2html:latest --no-recursive
+```
+
+#### 4. 自定义缩放比例
+
+使用1.5倍缩放比例转换PDF:
+
+```bash
+# 本地使用
+python main.py --zoom 1.5
+
+# Docker使用
+docker run --rm -v $(pwd):/app pdf2html:latest --zoom 1.5
+```
+
+#### 5. 组合使用参数
+
+组合使用多个参数,实现更精确的控制:
+
+```bash
+# 本地使用
+python main.py -i ./documents -o ./converted --no-recursive --zoom 2.0
+
+# Docker使用
+docker run --rm -v $(pwd):/app pdf2html:latest -i /app/documents -o /app/converted --no-recursive --zoom 2.0
+```
+
+### 创建便捷别名(推荐)
+
+为了简化使用,可以创建一个别名:
+
+```bash
+# Linux/Mac
+alias pdf2html='docker run --rm -v $(pwd):/app pdf2html:latest'
+
+# Windows PowerShell
+function pdf2html { docker run --rm -v ${PWD}:/app pdf2html:latest $args }
+```
+
+然后就可以像使用普通命令一样使用:
+
+```bash
+# 显示帮助
+pdf2html --help
+
+# 转换当前目录
 pdf2html
+
+# 指定参数
+pdf2html -i ./pdfs -o ./htmls
+```
+
+## Docker高级用法
+
+### 使用docker-compose
+
+1. 修改`docker-compose.yml`中的`command`参数(如果需要)
+2. 运行:
+
+```bash
+# 构建并运行
+docker-compose up --build
+
+# 或者先构建,再运行
+docker-compose build
+docker-compose run pdf2html
+```
+
+### 挂载多个目录
+
+如果需要访问多个目录中的PDF文件:
+
+```bash
+docker run --rm \
+  -v /path/to/pdfs1:/app/pdfs1 \
+  -v /path/to/pdfs2:/app/pdfs2 \
+  -v /path/to/output:/app/output \
+  pdf2html:latest \
+  -i /app/pdfs1 \
+  -o /app/output
+```
+
+## 注意事项
+
+1. **文件路径**:在Docker中使用时,所有路径都应该是容器内的路径,通常以`/app`开头
+2. **权限**:确保挂载的目录有适当的读写权限
+3. **大文件**:转换大型PDF文件可能需要较长时间,默认超时时间为5分钟
+4. **依赖项**:pdf2htmlEX可能需要一些系统依赖库,Docker镜像已包含所有必要依赖
+
+## 常见问题
+
+### Q: 如何处理转换失败的文件?
+A: 程序会显示转换失败的文件名称和错误信息。您可以检查这些PDF文件是否损坏,或者尝试手动转换。
+
+### Q: 转换后的HTML文件在哪里?
+A: 默认情况下,HTML文件会保存在当前目录的`htmls`子目录中。您可以使用`--output-dir`参数指定其他位置。
+
+### Q: 如何只转换新添加的PDF文件?
+A: 程序会自动跳过已存在的HTML文件,所以如果您再次运行,只会转换新添加的PDF文件。
+
+### Q: 支持哪些PDF版本?
+A: 支持大多数标准PDF文件,但某些特殊格式(如加密PDF)可能无法转换。
+
+## 项目结构
+
+```
+pdf2html/
+├── main.py              # 主程序文件
+├── Dockerfile           # Docker构建文件
+├── docker-compose.yml   # Docker Compose配置文件
+├── README.md            # 项目说明文档
+└── .dockerignore        # Docker忽略文件
 ```
 
+## 许可证
+
+Copyright © 2017-2024 liuyuqi. All Rights Reserved.
+
+## 联系方式
 
+如有问题或建议,请联系:liuyuqi.gov@msn.cn

+ 11 - 4
docker-compose.yml

@@ -1,10 +1,17 @@
-version: '3.4'
+version: '3.8'
 
 services:
   pdf2html:
-    image: pdf2html
+    image: pdf2html:latest
     build:
       context: .
       dockerfile: ./Dockerfile
-    ports:
-      - 3000:3000
+    volumes:
+      # 挂载当前目录到容器的/app目录,方便转换本地PDF文件
+      - .:/app
+    # 环境变量配置
+    environment:
+      - PYTHONUNBUFFERED=1
+    # 命令行参数示例(可根据需要修改)
+    # command: ["--help"]  # 显示帮助信息
+    # command: ["--input-dir", "/app/pdfs", "--output-dir", "/app/htmls"]  # 指定输入输出目录

+ 252 - 29
main.py

@@ -4,35 +4,258 @@
 @Contact :   liuyuqi.gov@msn.cn
 @Time    :   2023/12/09 14:57:36
 @License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
-@Desc    :   enter point
+@Desc    :   PDF批量转换为HTML工具
 
-recycle read all files in a directory, and find *.pdf files, then convert x.pdf to x.html
+功能说明:
+    递归读取指定目录中的所有PDF文件,将其转换为HTML格式,
+    并保存到指定的输出目录中。
+
+使用方法:
+    python main.py [--input-dir INPUT_DIR] [--output-dir OUTPUT_DIR] [--recursive] [--no-recursive]
+
+参数说明:
+    --input-dir, -i   输入目录,包含PDF文件的目录(默认:当前工作目录)
+    --output-dir, -o  输出目录,保存HTML文件的目录(默认:htmls)
+    --recursive, -r   递归搜索子目录中的PDF文件(默认:True)
+    --no-recursive    不递归搜索子目录
+    --zoom            转换时的缩放比例(默认:1.3)
+    --help, -h        显示帮助信息
 '''
 
-import os,sys,re,shutil
-
-def convert():
-    ''''''
-    current_dir = os.getcwd()
-    if not os.path.exists(os.path.join(current_dir,'htmls')):
-        os.mkdir(os.path.join(current_dir,'htmls'))
-    for root, dirs, files in os.walk(current_dir):
-        for file in files:
-            if file.endswith('.pdf'):
-                try:
-                    os.system('pdf2htmlEX --zoom 1.3 --process-outline 0 --page-filename %s.html %s'%(file,file))
-                    print('convert %s to %s.html'%(file,file))
-                except Exception as e:
-                    print(f'convert failed: {e}')
-    
-    # move all .html to htmls diretory
-    for root, dirs, files in os.walk(current_dir):
-        for file in files:
-            if file.endswith('.html'):
-                try:
-                    shutil.move(os.path.join(root,file),os.path.join(current_dir,'htmls'))
-                except Exception as e:
-                    print(f'move failed: {e}')
-
-if __name__=='__main__':
-    convert()
+import argparse
+import os
+import shutil
+import subprocess
+from pathlib import Path
+
+
+def convert_pdf_to_html(pdf_path: Path, output_dir: Path, zoom: float = 1.3) -> bool:
+    '''
+    转换单个PDF文件为HTML格式
+
+    参数:
+        pdf_path: PDF文件的完整路径
+        output_dir: 输出目录路径
+        zoom: 缩放比例,默认为1.3
+
+    返回:
+        转换成功返回True,失败返回False
+    '''
+    pdf_name = pdf_path.name
+    # 生成临时HTML文件名(不包含路径)
+    html_name = pdf_name.rsplit('.', 1)[0] + '.html'
+    # 最终输出路径
+    final_output_path = output_dir / html_name
+
+    # 检查输出文件是否已存在
+    if final_output_path.exists():
+        print(f'Skipping {pdf_name} - {html_name} already exists in output directory')
+        return True
+
+    # 构建pdf2htmlEX命令
+    cmd = [
+        'pdf2htmlEX',
+        '--zoom', str(zoom),
+        '--process-outline', '0',
+        '--page-filename', html_name,
+        str(pdf_path)
+    ]
+
+    try:
+        # 执行转换命令
+        result = subprocess.run(
+            cmd,
+            capture_output=True,
+            text=True,
+            timeout=300  # 5分钟超时
+        )
+
+        if result.returncode == 0:
+            # 检查生成的HTML文件位置
+            # pdf2htmlEX默认在当前工作目录或PDF目录生成文件
+            generated_html = Path.cwd() / html_name
+            if not generated_html.exists():
+                # 可能在PDF文件所在目录
+                generated_html = pdf_path.parent / html_name
+
+            if generated_html.exists():
+                # 移动到输出目录
+                shutil.move(str(generated_html), str(final_output_path))
+                print(f'Successfully converted {pdf_name} to {html_name}')
+                return True
+            else:
+                print(f'Warning: Conversion command succeeded but {html_name} not found')
+                return False
+        else:
+            print(f'Failed to convert {pdf_name}: {result.stderr}')
+            return False
+
+    except subprocess.TimeoutExpired:
+        print(f'Timeout while converting {pdf_name}')
+        return False
+    except Exception as e:
+        print(f'Error converting {pdf_name}: {str(e)}')
+        return False
+
+
+def find_pdf_files(input_dir: Path, recursive: bool = True) -> list:
+    '''
+    查找指定目录中的所有PDF文件
+
+    参数:
+        input_dir: 输入目录路径
+        recursive: 是否递归搜索子目录,默认为True
+
+    返回:
+        PDF文件路径列表
+    '''
+    pdf_files = []
+
+    if recursive:
+        # 递归搜索所有子目录
+        for root, dirs, files in os.walk(input_dir):
+            for file in files:
+                if file.lower().endswith('.pdf'):
+                    pdf_files.append(Path(root) / file)
+    else:
+        # 仅搜索当前目录
+        for file in input_dir.iterdir():
+            if file.is_file() and file.name.lower().endswith('.pdf'):
+                pdf_files.append(file)
+
+    return pdf_files
+
+
+def main():
+    '''
+    主函数:解析命令行参数并执行转换流程
+    '''
+    # 创建参数解析器
+    parser = argparse.ArgumentParser(
+        description='PDF批量转换为HTML工具',
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        epilog='''
+示例用法:
+  python main.py                          # 使用默认设置转换当前目录下的PDF
+  python main.py -i ./pdfs -o ./output   # 指定输入和输出目录
+  python main.py --no-recursive           # 不搜索子目录
+  python main.py --zoom 1.5               # 使用1.5倍缩放比例
+        '''
+    )
+
+    # 添加命令行参数
+    parser.add_argument(
+        '--input-dir', '-i',
+        default='.',
+        help='输入目录,包含PDF文件的目录(默认:当前工作目录)'
+    )
+
+    parser.add_argument(
+        '--output-dir', '-o',
+        default='htmls',
+        help='输出目录,保存HTML文件的目录(默认:htmls)'
+    )
+
+    # 创建互斥组(递归和非递归只能选一个)
+    recursive_group = parser.add_mutually_exclusive_group()
+    recursive_group.add_argument(
+        '--recursive', '-r',
+        action='store_true',
+        default=True,
+        help='递归搜索子目录中的PDF文件(默认:True)'
+    )
+    recursive_group.add_argument(
+        '--no-recursive',
+        action='store_false',
+        dest='recursive',
+        help='不递归搜索子目录'
+    )
+
+    parser.add_argument(
+        '--zoom', '-z',
+        type=float,
+        default=1.3,
+        help='转换时的缩放比例(默认:1.3)'
+    )
+
+    # 解析命令行参数
+    args = parser.parse_args()
+
+    # 处理路径
+    input_dir = Path(args.input_dir).resolve()
+    output_dir = Path(args.output_dir).resolve()
+
+    # 验证输入目录
+    if not input_dir.exists():
+        print(f'错误: 输入目录不存在: {input_dir}')
+        return 1
+
+    if not input_dir.is_dir():
+        print(f'错误: 输入路径不是目录: {input_dir}')
+        return 1
+
+    # 创建输出目录(如果不存在)
+    try:
+        output_dir.mkdir(parents=True, exist_ok=True)
+    except Exception as e:
+        print(f'错误: 无法创建输出目录 {output_dir}: {str(e)}')
+        return 1
+
+    # 显示配置信息
+    print('=' * 60)
+    print('PDF批量转换为HTML工具')
+    print('=' * 60)
+    print(f'输入目录: {input_dir}')
+    print(f'输出目录: {output_dir}')
+    print(f'递归搜索: {"是" if args.recursive else "否"}')
+    print(f'缩放比例: {args.zoom}')
+    print('=' * 60)
+
+    # 查找PDF文件
+    print(f'\n正在搜索PDF文件...')
+    pdf_files = find_pdf_files(input_dir, args.recursive)
+
+    if not pdf_files:
+        print(f'未找到PDF文件,退出程序。')
+        return 0
+
+    print(f'找到 {len(pdf_files)} 个PDF文件\n')
+
+    # 转换PDF文件
+    success_count = 0
+    failed_count = 0
+    skipped_count = 0
+
+    for i, pdf_path in enumerate(pdf_files, 1):
+        print(f'[{i}/{len(pdf_files)}] 处理: {pdf_path.name}')
+
+        # 转换文件
+        result = convert_pdf_to_html(pdf_path, output_dir, args.zoom)
+
+        if result:
+            # 检查是成功转换还是跳过
+            html_name = pdf_path.name.rsplit('.', 1)[0] + '.html'
+            if (output_dir / html_name).exists():
+                success_count += 1
+            else:
+                skipped_count += 1
+        else:
+            failed_count += 1
+
+    # 显示转换结果
+    print('\n' + '=' * 60)
+    print('转换完成')
+    print('=' * 60)
+    print(f'总PDF文件数: {len(pdf_files)}')
+    print(f'成功转换: {success_count}')
+    print(f'跳过(已存在): {skipped_count}')
+    print(f'转换失败: {failed_count}')
+    print(f'输出目录: {output_dir}')
+    print('=' * 60)
+
+    # 根据失败情况返回退出码
+    return 0 if failed_count == 0 else 1
+
+
+if __name__ == '__main__':
+    exit(main())