问题描述
项目中,需要将PDF文档转为图像,使用Imagick扩展组件进行转换。结果在转换时,因为PDF文档没有嵌入字体,导致一部份文字无法显示出来。如下图片,下划线部份,下划线上面是有文字的,但是转换后,模糊部份的文字正常显示,下划线上面的文字是空白的。
然后就是各种百度、问AI,无论使用何种方法,比如加入字体文档、修复PDF文档、安装第三方组件等等。
最终解决就是用python写一个api服务,因为服务器是centos的,系统python版本是python2,要使用yum所以再加个docker,最终解决问题。
下面是具体的步骤:
- 项目新建一个docker目录,用于存放api服务。
- 创建一个Dockerfile,内容如下:
# 使用CentOS 7作为基础镜像
FROM centos:7
# 将当前目录下的Centos-7.repo复制到镜像内的/etc/yum.repos.d/目录下并覆盖原有的CentOS-Base.repo文件
COPY Centos-7.repo /etc/yum.repos.d/CentOS-Base.repo
# 将当前目录下的AdobeSongStd-Light.otf复制到镜像目录/usr/share/fonts/目录下
COPY AdobeSongStd-Light.otf /usr/share/fonts/AdobeSongStd-Light.otf
# 设置工作目录
WORKDIR /home/convert
# 添加端口映射
EXPOSE 30581
# 添加目录挂载 /www/wwwroot/dockerdemo.com/docker
# 这个目录放置的是转换PDF服务的源码文件
VOLUME ["/home/convert", "/www/wwwroot/dockerdemo.com/docker"]
# 使用yum安装相关依赖包
RUN yum install -y openssl-devel bzip2-devel libffi-devel zlib-devel poppler-utils gcc gcc-c++ make
# 下载Python 3.8.17的源码包并解压
RUN curl -O https://mirrors.huaweicloud.com/python/3.8.17/Python-3.8.17.tgz && \
tar -zxvf Python-3.8.17.tgz && \
cd Python-3.8.17 && \
# 编译安装Python 3.8
./configure --prefix=/usr/local/python3.8 && \
make && \
make install && \
# 备注系统自带的python2.7
mv /usr/bin/python /usr/bin/python.bak && \
# 创建软链接将系统默认的python指向python3.8(此操作需谨慎,可能影响依赖Python 2的系统命令)
ln -s /usr/local/python3.8/bin/python3.8 /usr/bin/python && \
# 创建软链接将pip3.8映射到系统目录便于使用
ln -s /usr/local/python3.8/bin/pip3.8 /usr/bin/pip
# 使用pip安装指定版本的Pillow以及其他依赖库
RUN pip install Pillow==8.4 flask pdf2image PyPDF2 websockets redis
# 设置容器启动时执行的命令为运行autorun.sh文件(需确保该文件存在于挂载的/home/convert目录下)
CMD ["./autorun.sh"]
- 创建autorun.sh文件,内容如下:
#!/bin/bash
# 切换到.py文件所在目录(假设.py文件在/app目录下,根据实际情况修改)
cd /home/convert
# 运行.py文件(假设文件名为main.py,根据实际情况修改)
python main.py
- 创建main.py文件,内容如下:
#!/usr/bin/env python3
from flask import Flask, request, jsonify, send_from_directory
import os
from pdf2image import convert_from_bytes
from PIL import Image
import uuid
from werkzeug.utils import secure_filename
app = Flask(__name__)
# 配置上传和输出目录
UPLOAD_FOLDER = 'upload'
OUTPUT_FOLDER = 'output'
ALLOWED_EXTENSIONS = {'pdf'}
# 确保目录存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def process_pdf(pdf_bytes):
try:
# 转换PDF为图片,提高DPI到300
images = convert_from_bytes(
pdf_bytes,
dpi=300,
fmt='png',
first_page=1,
last_page=7 # 修改为7页以包含第7页
)
# 调整图片宽度为800,高度按比例缩放
target_width = 800
first_images = []
total_height = 0
for img in images:
# 计算缩放比例
ratio = target_width / img.width
new_height = int(img.height * ratio)
# 调整图片大小,使用 ANTIALIAS 替代 Resampling.LANCZOS
resized_img = img.resize((target_width, new_height), Image.ANTIALIAS)
first_images.append(resized_img)
total_height += new_height
# 创建新图片用于拼接
result = Image.new('RGB', (target_width, total_height), 'white')
# 拼接图片
current_height = 0
for img in first_images:
result.paste(img, (0, current_height))
current_height += img.height
# 生成唯一文件名
output_filename = f"{uuid.uuid4()}.png"
output_path = os.path.join(OUTPUT_FOLDER, output_filename)
# 保存结果
result.save(output_path, 'PNG')
# 返回文件的URL
return (
f"/download/{output_filename}",
None
)
except Exception as e:
return None, str(e)
@app.route('/convert', methods=['POST'])
def convert_pdf():
# 添加调试信息
print("files:", request.files)
print("form:", request.form)
print("Content-Type:", request.headers.get('Content-Type'))
if 'file' not in request.files:
return jsonify({
'code': -1,
'message': '没有文件上传'
}), 400
file = request.files['file']
if file.filename == '':
return jsonify({
'code': -1,
'message': '没有选择文件'
}), 400
if not allowed_file(file.filename):
return jsonify({
'code': -1,
'message': '不支持的文件格式'
}), 400
try:
# 读取PDF文件内容
pdf_bytes = file.read()
# 处理PDF
file_url, error = process_pdf(pdf_bytes)
if error:
return jsonify({
'code': -1,
'message': error
}), 400
return jsonify({
'code': 0,
'message': 'SUCCESS',
'data': {
'fileUrl': file_url
}
})
except Exception as e:
return jsonify({
'code': -1,
'message': str(e)
}), 500
@app.route('/download/<filename>')
def download_file(filename):
return send_from_directory(OUTPUT_FOLDER, filename)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=30581, debug=True)
- 构建镜像:
docker build -t convertPdf:latest .
- 运行容器:
docker run -d --name convertPdf -p 30581:30581 \
-v /www/wwwroot/dockerdemo.com/docker:/home/convert \
convertPdf
- 测试编写PHP上传文件代码,调用API接口:
<?php
$client = new Client(['verify' => false]);
$response = $client->request('POST', 'http://127.0.0.1:30581/convert', [
'multipart' => [
[
'name' => 'file',
'contents' => fopen('/path/to/file.pdf', 'r') // 上传文件路径
]
],
'headers' => [
'Accept' =>'application/json'
]
]);
$status_code = $response->getStatusCode();
$body = $response->getBody();
$data = json_decode($body, true);
echo $data['data']['fileUrl']; // 图片URL
-
测试上传文件,查看结果。
-
注意:如果遇到问题,可以先尝试删除容器、镜像、挂载目录,然后重新构建、运行。