| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /**
- * PDF转图片模块
- * 步骤:
- * 1. 创建start()函数
- * 2. 创建变量pdfPath用来储存外部传入的pdf路径
- * 3. 创建outPut 变量用来储存输出png的目录
- * 4. 调用pdf2image.js将pdf转换为png图片
- * 5. 保存结果
- */
- import fs from 'fs';
- import path from 'path';
- import { execSync } from 'child_process';
- import { fileURLToPath } from 'url';
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = path.dirname(__filename);
- /**
- * 获取项目根目录
- */
- function getProjectRoot() {
- return path.join(__dirname, '..');
- }
- /**
- * PDF转图片主函数
- * @param {string} pdfPath - PDF文件路径
- * @param {string} outputDir - 输出PNG图片的目录
- * @param {Object} options - 可选参数
- * @param {number} options.dpi - 图片分辨率(默认200)
- * @param {string} options.format - 输出格式(默认'png')
- * @param {number} options.firstPage - 起始页码(可选)
- * @param {number} options.lastPage - 结束页码(可选)
- * @param {string} projectRoot - 项目根目录(可选)
- * @returns {Promise<Object>} 转换结果
- */
- async function start(pdfPath, outputDir, options = {}, projectRoot = null) {
- try {
- // 步骤1: 创建start()函数
- if (!projectRoot) {
- projectRoot = getProjectRoot();
- }
- // 步骤2: 创建变量pdfPath用来储存外部传入的pdf路径
- if (!pdfPath || !fs.existsSync(pdfPath)) {
- throw new Error(`PDF文件不存在: ${pdfPath}`);
- }
- // 步骤3: 创建outPut 变量用来储存输出png的目录
- if (!outputDir) {
- const pdfDir = path.dirname(pdfPath);
- const pdfName = path.basename(pdfPath, path.extname(pdfPath));
- outputDir = path.join(pdfDir, `${pdfName}_images`);
- }
- // 确保输出目录存在
- if (!fs.existsSync(outputDir)) {
- fs.mkdirSync(outputDir, { recursive: true });
- }
- console.log('='.repeat(60));
- console.log('📄 PDF转图片');
- console.log('='.repeat(60));
- console.log(`📁 PDF文件: ${pdfPath}`);
- console.log(`📂 输出目录: ${outputDir}`);
- // 步骤4: 调用Python脚本将pdf转换为png图片
- const pythonEnv = path.join(projectRoot, 'python', 'venv', 'Scripts', 'python.exe');
- const pythonScript = path.join(projectRoot, 'python', 'generate-anim', 'pdf_to_images.py');
- // 检查Python脚本是否存在,如果不存在则创建
- if (!fs.existsSync(pythonScript)) {
- console.log('⚠️ Python脚本不存在,正在创建...');
- createPdfToImageScript(pythonScript, projectRoot);
- }
- const {
- dpi = 200,
- format = 'png',
- firstPage = null,
- lastPage = null
- } = options;
- // 构建命令参数
- let command = `"${pythonEnv}" "${pythonScript}" "${pdfPath}" "${outputDir}" --dpi ${dpi} --format ${format}`;
-
- if (firstPage !== null) {
- command += ` --first-page ${firstPage}`;
- }
-
- if (lastPage !== null) {
- command += ` --last-page ${lastPage}`;
- }
- console.log(`\n🔄 开始转换PDF...`);
- console.log(` 分辨率: ${dpi} DPI`);
- console.log(` 格式: ${format.toUpperCase()}`);
- // 执行Python脚本
- execSync(command, {
- encoding: 'utf-8',
- stdio: 'inherit',
- cwd: projectRoot,
- env: { ...process.env, PYTHONIOENCODING: 'utf-8' }
- });
- // 步骤5: 保存结果 - 读取生成的图片文件
- const imageFiles = fs.readdirSync(outputDir)
- .filter(file => {
- const ext = path.extname(file).toLowerCase();
- return ['.png', '.jpg', '.jpeg'].includes(ext);
- })
- .map(file => path.join(outputDir, file))
- .sort((a, b) => {
- // 按文件名排序(页码顺序)
- const nameA = path.basename(a);
- const nameB = path.basename(b);
- return nameA.localeCompare(nameB, undefined, { numeric: true, sensitivity: 'base' });
- });
- if (imageFiles.length === 0) {
- throw new Error('未生成任何图片文件');
- }
- console.log(`\n✅ 转换完成!`);
- console.log(` 生成图片数: ${imageFiles.length}`);
- console.log(` 输出目录: ${outputDir}`);
- return {
- success: true,
- pdfPath: pdfPath,
- outputDir: outputDir,
- imageFiles: imageFiles,
- count: imageFiles.length
- };
- } catch (error) {
- console.error(`\n❌ PDF转图片失败: ${error.message}`);
- if (error.stack) {
- console.error(error.stack);
- }
- throw error;
- }
- }
- /**
- * 创建PDF转图片的Python脚本
- * @param {string} scriptPath - Python脚本路径
- * @param {string} projectRoot - 项目根目录
- */
- function createPdfToImageScript(scriptPath, projectRoot) {
- const scriptDir = path.dirname(scriptPath);
- if (!fs.existsSync(scriptDir)) {
- fs.mkdirSync(scriptDir, { recursive: true });
- }
- const scriptContent = `# -*- coding: utf-8 -*-
- """
- PDF转图片脚本
- 使用pdf2image库将PDF转换为PNG图片
- """
- import sys
- import os
- import argparse
- from pathlib import Path
- try:
- from pdf2image import convert_from_path
- from pdf2image.exceptions import (
- PDFInfoNotInstalledError,
- PDFPageCountError,
- PDFSyntaxError
- )
- except ImportError:
- print("错误: 未安装pdf2image库,请运行: pip install pdf2image")
- print("注意: Windows系统还需要安装poppler,请参考: https://github.com/Belval/pdf2image")
- sys.exit(1)
- def pdf_to_images(pdf_path, output_dir, dpi=200, fmt='png', first_page=None, last_page=None):
- """
- 将PDF转换为图片
-
- Args:
- pdf_path: PDF文件路径
- output_dir: 输出目录
- dpi: 图片分辨率(默认200)
- fmt: 输出格式(默认png)
- first_page: 起始页码(从1开始,可选)
- last_page: 结束页码(可选)
- """
- pdf_path = Path(pdf_path)
- output_dir = Path(output_dir)
-
- if not pdf_path.exists():
- raise FileNotFoundError(f"PDF文件不存在: {pdf_path}")
-
- # 确保输出目录存在
- output_dir.mkdir(parents=True, exist_ok=True)
-
- print(f"正在转换PDF: {pdf_path.name}")
- print(f"输出目录: {output_dir}")
- print(f"分辨率: {dpi} DPI")
- print(f"格式: {fmt.upper()}")
-
- try:
- # 转换PDF为图片
- images = convert_from_path(
- str(pdf_path),
- dpi=dpi,
- fmt=fmt,
- first_page=first_page,
- last_page=last_page,
- output_folder=str(output_dir),
- output_file='page'
- )
-
- # 如果使用output_folder,文件已经保存,images是文件路径列表
- if isinstance(images[0], str):
- image_files = images
- else:
- # 否则需要手动保存
- image_files = []
- pdf_name = pdf_path.stem
- for i, image in enumerate(images, start=1):
- page_num = first_page + i - 1 if first_page else i
- output_file = output_dir / f"{pdf_name}_page{page_num:04d}.{fmt}"
- image.save(output_file, fmt.upper())
- image_files.append(str(output_file))
- print(f" 已保存: {output_file.name}")
-
- print(f"\\n转换完成!共生成 {len(image_files)} 张图片")
- return image_files
-
- except PDFInfoNotInstalledError:
- print("错误: 未安装poppler工具")
- print("Windows: 下载并安装poppler,或使用conda: conda install -c conda-forge poppler")
- sys.exit(1)
- except PDFPageCountError:
- print("错误: 无法获取PDF页数")
- sys.exit(1)
- except PDFSyntaxError:
- print("错误: PDF文件格式错误")
- sys.exit(1)
- except Exception as e:
- print(f"错误: {str(e)}")
- sys.exit(1)
- if __name__ == '__main__':
- parser = argparse.ArgumentParser(description='将PDF转换为图片')
- parser.add_argument('pdf_path', help='PDF文件路径')
- parser.add_argument('output_dir', help='输出目录')
- parser.add_argument('--dpi', type=int, default=200, help='图片分辨率(默认200)')
- parser.add_argument('--format', default='png', choices=['png', 'jpg', 'jpeg'], help='输出格式(默认png)')
- parser.add_argument('--first-page', type=int, help='起始页码(从1开始)')
- parser.add_argument('--last-page', type=int, help='结束页码')
-
- args = parser.parse_args()
-
- pdf_to_images(
- args.pdf_path,
- args.output_dir,
- dpi=args.dpi,
- fmt=args.format,
- first_page=args.first_page,
- last_page=args.last_page
- )
- `;
- fs.writeFileSync(scriptPath, scriptContent, 'utf-8');
- console.log(`✅ 已创建Python脚本: ${scriptPath}`);
- }
- /**
- * 导出函数
- */
- export { start };
- // 如果直接运行此文件,执行测试
- if (import.meta.url === `file://${path.resolve(process.argv[1])}` ||
- process.argv[1]?.endsWith('pdf-to-image.js')) {
- // 测试代码
- const testPdfPath = path.join(getProjectRoot(), 'static', '漫画', 'pdf', 'test.pdf');
- if (fs.existsSync(testPdfPath)) {
- start(testPdfPath, null, { dpi: 200, format: 'png' })
- .then(result => {
- console.log('\n测试完成!');
- console.log(`生成图片: ${result.count} 张`);
- })
- .catch(error => {
- console.error('测试失败:', error.message);
- process.exit(1);
- });
- } else {
- console.log('测试PDF文件不存在,跳过测试');
- console.log(`请提供PDF文件路径: ${testPdfPath}`);
- }
- }
|