在 docker (FastAPI) 中使用 WeasyPrint 导出的 PDF 中的字体
Posted
技术标签:
【中文标题】在 docker (FastAPI) 中使用 WeasyPrint 导出的 PDF 中的字体【英文标题】:Fonts in PDF exported using WeasyPrint inside docker (FastAPI) 【发布时间】:2021-10-20 06:03:36 【问题描述】:我有一个在 docker 容器中运行的应用程序。该服务使用 FastAPI 框架编写,用于生成报告。我们首先生成一个 html 报告,然后使用 Weasyprint 库将其转换为 PDF。当我在笔记本电脑上执行它时(我使用的是 Manjaro Linux),一切正常。但是,当我在 docker 容器中使用 API 时,我注意到生成的 PDF 中的字体不同。在 CSS 文件中,我们使用 font-family Helvetica, sans-serif。因此,当在容器内生成报告时,无衬线字体不起作用。
此外,我已将我的字体文件夹(从我的笔记本电脑)复制到 docker 容器中,以检查它是否缺少某些字体;但是,sans-serif 仍然不起作用。
这是我的 Dockerfile:
FROM tiangolo/uvicorn-gunicorn-fastapi
RUN apt-get update && apt-get install -y build-essential unzip vim git curl locales orca
RUN apt install -y python3-cffi libcairo2 libcairo2-dev libpango-1.0-0 libpango1.0-dev libpangocairo-1.0-0 libgdk-pixbuf2.0-0 \
libgdk-pixbuf2.0-dev libffi-dev shared-mime-info libffi-dev fonts-font-awesome
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ARG RELEASE=main
RUN git clone <my_repo>
RUN cd ./<repo> && pip install -r requirements.txt
EXPOSE 80
WORKDIR /app/<repo>/app
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80", "--workers", "4"]
以及我安装的 python 库(在 requirements.txt 中):
numpy==1.20.3
pandas==1.2.4
plotly==4.14.3
kaleido==0.2.1
weasyprint==52.5
Jinja2==3.0.1
fastapi==0.65.2
uvicorn==0.14.0
pydantic==1.8.2
psutil==5.8.0
python-multipart==0.0.5
aiofiles==0.7.0
SQLAlchemy==1.4.22
async-exit-stack==1.0.1
async-generator==1.10
mysqlclient==2.0.3
由于我可以在我的笔记本电脑上很好地生成它,我假设缺少某些库(我也尝试安装 fonts-open-sans 和 libglib2.0-dev,但没有任何改变)或者存在某个版本的问题。例如,我查看了 WeasyPrint 文档,pango 应该是 1.44.0 版本或更高版本,而在 Debian 10(这是 docker 中的操作系统)中,软件包 libpango-1.0-0 使用 pango 1.42.0 版本。这可能是为什么 sans-serif 不起作用或对我缺少什么有任何想法的问题吗?
【问题讨论】:
您能否提供一些简单的app.py
应用程序代码示例以便能够重现行为?
@rzivmp app.py 没有多大作用。它执行一个后台任务,我们使用 jinja2 和一个 HTML 模板。在模板中,我们只是有一些表格(用 python 和 pandas 生成)和一些 plotly 的图(只是 png,我们不使用 plotly 生成的 div 和 javascript)。由于它是公司的私有代码,我无法共享它,我不知道我提到的复制它需要多少这些代码 - 需要创建一个脚本来复制这些步骤。但我认为你只需要一个带有字体系列 Helvetica、sans-serif 的简单 html,并在 docker 内生成一个带有 weasyprint 的 PDF。
【参考方案1】:
好的,我为你做了一些工作;)
我使用以下方法构建映像并启动 docker 容器:
FROM tiangolo/uvicorn-gunicorn-fastapi
RUN apt-get update && apt-get install -y build-essential unzip vim git curl locales orca
RUN apt install -y python3-cffi libcairo2 libcairo2-dev libpango-1.0-0 libpango1.0-dev libpangocairo-1.0-0 libgdk-pixbuf2.0-0 \
libgdk-pixbuf2.0-dev libffi-dev shared-mime-info libffi-dev fonts-font-awesome
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
COPY app.py .
COPY template .
COPY requirements.txt .
RUN pip install -r requirements.txt
EXPOSE 80
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80", "--workers", "4"]
其中requirements.txt
和你的一样,template
是直接从here复制过来的。
而app.py
是
from fastapi import FastAPI
from fastapi.responses import HTMLResponse, Response
from weasyprint import HTML
app = FastAPI(title="name")
@app.get("/", response_class=HTMLResponse)
async def read_root():
with open('template', 'r') as f:
contents = f.read()
return contents
@app.get("/pdf")
async def read_root_pdf():
with open('template', 'r') as f:
contents = f.read()
pdf_content = HTML(string=contents).write_pdf()
return Response(content=pdf_content, media_type="application/pdf")
if __name__ == "__main__":
import uvicorn
uvicorn.run('app:app', host="0.0.0.0", port=80)
正如您在下面看到的(我将 pdf 重命名为 png 以便能够附加)所有字体都是不同的。你的容器没有问题。
【讨论】:
首先,非常感谢您的努力!!我不明白它是如何为您工作的...我检查了 masterpdfeditor 属性中的字体。通过上面的 Dockerfile,生成的字体是 DejaVuSans。因此,似乎无衬线字体有效,但 Helvetica 无效。所以我刚刚下载并复制了 Helvetica ttf 文件。现在,生成的 pdf 确实具有 Helvetica 作为字体。以上是关于在 docker (FastAPI) 中使用 WeasyPrint 导出的 PDF 中的字体的主要内容,如果未能解决你的问题,请参考以下文章
docker中的Nginx,fastapi和streamlit - 反向代理不适用于streamlit
我在将 docker mysql 与 fastapi 连接时遇到问题
何时/何地在 FastAPI 中使用正文/路径/查询/字段?