Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weasyprint layer for python3.12 lambda #20

Merged
merged 2 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 36 additions & 84 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,99 +5,51 @@ jobs:
weasyprint-build:
name: Build WeasyPrint
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]') "
steps:
- uses: actions/checkout@v2
- name: Build weasyprint
run: |
make build/weasyprint-layer-python3.8.zip
- uses: actions/checkout@v4
- name: Build Layer
run: make build/weasyprint-layer-python3.12.zip
- name: Test weasyprint
run: |
mkdir output
TEST_FILENAME=output/report.pdf make test.weasyprint
TEST_FILENAME=output/report.png make test.weasyprint
- name: Upload pdf
uses: actions/upload-artifact@v1
with:
name: WeasyPrint test results
make test.start.container &
sleep 1
TEST_FILENAME=output/report.pdf make test.print.report
rm -rf build/opt
- name: Upload Build
uses: actions/upload-artifact@v4
with:
name: WeasyPrint Layer Build
path: build
- name: Upload Test PDF
uses: actions/upload-artifact@v4
with:
name: WeasyPrint Test Results
path: output
- name: Create WeasyPrint Release
id: create_weasyprint_release
if: startsWith(github.ref, 'refs/tags/weasyprint-')
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release WeasyPrint layer
- name: Upload WeasyPrint Layer
if: startsWith(github.ref, 'refs/tags/weasyprint-')
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
upload_url: ${{ steps.create_weasyprint_release.outputs.upload_url }}
asset_path: ./build/weasyprint-layer-python3.8.zip
asset_name: weasyprint-layer-python3.8.zip
asset_content_type: application/zip
files: |
./build/weasyprint-layer-python3.12.zip
./build/weasyprint-layer-python3.12-no-fonts.zip

wkhtmltox-build:
name: Build wkhtmltox
ghostscript-build:
name: Build GhostScript
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]') "
steps:
- uses: actions/checkout@v2
- name: Build wkhtmltox
run: |
make build/wkhtmltox-layer.zip
make build/wkhtmltopdf-layer.zip
make build/wkhtmltoimage-layer.zip
- name: Test wkhtmltox
- uses: actions/checkout@v4
- name: Build Layer
run: |
mkdir output
TEST_FILENAME=output/google.pdf make test.wkhtmltox
TEST_FILENAME=output/google.png make test.wkhtmltox
- name: Upload pdf
uses: actions/upload-artifact@v1
with:
name: wkhtmltox test results
path: output
- name: Create wkhtmltox Release
id: create_wkhtmltox_release
if: startsWith(github.ref, 'refs/tags/wkhtmltox-')
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release wkhtmltox layer
- name: Upload wkhtmltox Layer
if: startsWith(github.ref, 'refs/tags/wkhtmltox-')
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_wkhtmltox_release.outputs.upload_url }}
asset_path: ./build/wkhtmltox-layer.zip
asset_name: wkhtmltox-layer.zip
asset_content_type: application/zip
- name: Upload wkhtmltopdf Layer
if: startsWith(github.ref, 'refs/tags/wkhtmltox-')
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_wkhtmltox_release.outputs.upload_url }}
asset_path: ./build/wkhtmltopdf-layer.zip
asset_name: wkhtmltopdf-layer.zip
asset_content_type: application/zip
- name: Upload wkhtmltoimage Layer
if: startsWith(github.ref, 'refs/tags/wkhtmltox-')
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_wkhtmltox_release.outputs.upload_url }}
asset_path: ./build/wkhtmltoimage-layer.zip
asset_name: wkhtmltoimage-layer.zip
asset_content_type: application/zip
make build/ghostscript-layer.zip
rm -rf build/opt
- name: Upload Build
uses: actions/upload-artifact@v4
with:
name: GhostScript Layer Build
path: build
- name: Create GhostScript Release
if: startsWith(github.ref, 'refs/tags/ghostscript-')
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
files: ./build/build/ghostscript-layer.zip
90 changes: 33 additions & 57 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
RUNTIME ?= python3.8
PLATFORM ?= linux/amd64
RUNTIME ?= 3.12
TEST_FILENAME ?= report.pdf
DOCKER_RUN=docker run --rm --platform=${PLATFORM}

.PHONY: stack.deploy.weasyprint clean test.weasyprint
.PHONY: stack.deploy.weasyprint clean test.start.container test.print.report

all: build/weasyprint-layer-$(RUNTIME).zip build/wkhtmltopdf-layer.zip
all: build/weasyprint-layer-python$(RUNTIME).zip

build/weasyprint-layer-$(RUNTIME).zip: weasyprint/layer_builder.sh \
build/weasyprint-layer-python$(RUNTIME).zip: weasyprint/layer_builder.sh \
build/fonts-layer.zip \
| _build
docker run --rm \
${DOCKER_RUN} \
-v `pwd`/weasyprint:/out \
-t lambci/lambda:build-${RUNTIME} \
bash /out/layer_builder.sh
mv -f ./weasyprint/layer.zip ./build/weasyprint-no-fonts-layer.zip
--entrypoint "/out/layer_builder.sh" \
-t public.ecr.aws/lambda/python:${RUNTIME}
mv -f ./weasyprint/layer.zip ./build/weasyprint-layer-python${RUNTIME}-no-fonts.zip
cd build && rm -rf ./opt && mkdir opt \
&& unzip fonts-layer.zip -d opt \
&& unzip weasyprint-no-fonts-layer.zip -d opt \
&& cd opt && zip -r9 ../weasyprint-layer-${RUNTIME}.zip .
&& unzip weasyprint-layer-python${RUNTIME}-no-fonts.zip -d opt \
&& cd opt && zip -r9 ../weasyprint-layer-python${RUNTIME}.zip .

build/fonts-layer.zip: fonts/layer_builder.sh | _build
docker run --rm \
-e INSTALL_MS_FONTS="${INSTALL_MS_FONTS}" \
${DOCKER_RUN} \
-v `pwd`/fonts:/out \
-t lambci/lambda:build-${RUNTIME} \
bash /out/layer_builder.sh
--entrypoint "/out/layer_builder.sh" \
-t public.ecr.aws/lambda/python:${RUNTIME}
mv -f ./fonts/layer.zip $@

build/ghostscript-layer.zip: ghostscript/layer_builder.sh | _build
${DOCKER_RUN} \
-v `pwd`/ghostscript:/out \
--entrypoint "/out/layer_builder.sh" \
-t public.ecr.aws/lambda/python:${RUNTIME}
mv -f ./ghostscript/layer.zip $@

stack.diff:
cd cdk-stacks && npm install && npm run build
cdk diff --app ./cdk-stacks/bin/app.js --stack PrintStack --parameters uploadBucketName=${BUCKET}
Expand All @@ -34,50 +42,22 @@ stack.deploy:
cd cdk-stacks && npm install && npm run build
cdk deploy --app ./cdk-stacks/bin/app.js --stack PrintStack --parameters uploadBucketName=${BUCKET}

test.weasyprint:
docker run --rm \
test.start.container:
${DOCKER_RUN} \
-e GDK_PIXBUF_MODULE_FILE="/opt/lib/loaders.cache" \
-e FONTCONFIG_PATH="/opt/fonts" \
-e XDG_DATA_DIRS="/opt/lib" \
-v `pwd`/weasyprint:/var/task \
-v `pwd`/build/opt:/opt \
lambci/lambda:${RUNTIME} \
lambda_function.lambda_handler \
'{"url": "https://kotify.github.io/cloud-print-utils/samples/report/", "filename": "${TEST_FILENAME}", "return": "base64"}' \
| tail -1 | jq .body | tr -d '"' | base64 -d > ${TEST_FILENAME}
@echo "Check ./${TEST_FILENAME}, eg.: xdg-open ${TEST_FILENAME}"


build/wkhtmltox-layer.zip: wkhtmltox/layer_builder.sh \
build/fonts-layer.zip \
| _build
docker run --rm \
-v `pwd`/wkhtmltox:/out \
-t lambci/lambda:build-${RUNTIME} \
bash /out/layer_builder.sh
mv -f ./wkhtmltox/layer.zip ./build/wkhtmltox-no-fonts-layer.zip
cd build && rm -rf ./opt && mkdir opt \
&& unzip fonts-layer.zip -d opt \
&& unzip wkhtmltox-no-fonts-layer.zip -d opt \
&& cd opt && zip -r9 ../wkhtmltox-layer.zip .

build/wkhtmltopdf-layer.zip: build/wkhtmltox-layer.zip
cp build/wkhtmltox-layer.zip $@
zip -d $@ "bin/wkhtmltoimage"

build/wkhtmltoimage-layer.zip: build/wkhtmltox-layer.zip
cp build/wkhtmltox-layer.zip $@
zip -d $@ "bin/wkhtmltopdf"

test.wkhtmltox:
docker run --rm \
-e FONTCONFIG_PATH="/opt/fonts" \
-v `pwd`/wkhtmltox:/var/task \
-v `pwd`/build/opt:/opt \
lambci/lambda:${RUNTIME} \
lambda_function.lambda_handler \
'{"args": "https://google.com", "filename": "${TEST_FILENAME}", "return": "base64"}' \
| tail -1 | jq .body | tr -d '"' | base64 -d > ${TEST_FILENAME}
-p 9000:8080 \
public.ecr.aws/lambda/python:${RUNTIME} \
lambda_function.lambda_handler

test.print.report:
which jq
curl --fail -s -S -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" \
-d '{"return": "base64", "filename": "${TEST_FILENAME}", "url": "https://kotify.github.io/cloud-print-utils/samples/report/"}' \
| tail -1 | jq .body | tr -d '"' | base64 -d > ${TEST_FILENAME}
@echo "Check ./${TEST_FILENAME}, eg.: xdg-open ${TEST_FILENAME}"


Expand All @@ -86,7 +66,3 @@ _build:

clean:
rm -rf ./build

fonts.list:
docker run --rm lambci/lambda:build-${RUNTIME} \
bash -c "yum search font | grep noarch | grep -v texlive"
20 changes: 7 additions & 13 deletions fonts/layer_builder.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,21 @@
# don't fortget to set FONTCONFIG_PATH="/opt/fonts" in your lambda
set -e

yum upgrade -y ca-certificates
yum install -y yum-utils rpmdevtools
dnf install -y rpmdevtools

cd /tmp
# download fonts
yumdownloader \
dejavu-sans-fonts \
dejavu-fonts-common
dnf download dejavu-sans-fonts
dnf download dejavu-serif-fonts
dnf download dejavu-sans-mono-fonts

rpmdev-extract -- *rpm

mkdir /opt/fonts
# dnf download urw-base35-nimbus-roman-fonts
# find /tmp/*/usr/share/fonts -name '*.afm' -delete -o -name '*.t1' -delete
cp -P -r /tmp/*/usr/share/fonts/* /opt/fonts

if [ "$INSTALL_MS_FONTS" = "yes" ]; then
PYTHON=python2 amazon-linux-extras install epel -y
yum install -y fontconfig xorg-x11-font-utils cabextract
curl -L -O https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm
rpm -i msttcore-fonts-installer-2.6-1.noarch.rpm
cp -P -r /usr/share/fonts/msttcore /opt/fonts/
fi

cat > /opt/fonts/fonts.conf <<EOF
<?xml version="1.0" ?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
Expand Down
12 changes: 12 additions & 0 deletions ghostscript/layer_builder.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
export VERSION="10.02.1"

dnf install -y gcc tar
cd /tmp/
curl -L -s https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${VERSION//./}/ghostscript-${VERSION}.tar.gz | tar zxf -
cd ghostscript-*
./configure --prefix=/opt
make && make install
cd /opt
zip -r9 /out/layer.zip bin/gs
17 changes: 4 additions & 13 deletions weasyprint/lambda_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,10 @@ def lambda_handler(event, context):
filename = event["filename"]
basename = os.path.basename(filename)
tmpfile = f"/tmp/{basename}"
ext = os.path.splitext(basename)[1]
if ext == ".pdf":
content_type = "application/pdf"
method = "write_pdf"
elif ext == ".png":
content_type = "image/png"
method = "write_png"
else:
raise ValueError("File extension {ext} is not supported.")
if "url" in event:
getattr(HTML(url=event["url"]), method)(target=tmpfile)
HTML(url=event["url"]).write_pdf(target=tmpfile)
else:
getattr(HTML(string=event["html"]), method)(
HTML(string=event["html"]).write_pdf(
target=tmpfile,
stylesheets=[CSS(string=event["css"])] if "css" in event else None,
)
Expand All @@ -31,7 +22,7 @@ def lambda_handler(event, context):
return {
"statusCode": 200,
"headers": {
"Content-type": content_type,
"Content-type": "application/pdf",
"Content-Disposition": f"attachment;filename={basename}",
},
"isBase64Encoded": True,
Expand All @@ -47,7 +38,7 @@ def lambda_handler(event, context):
open(tmpfile, "rb"),
bucket,
filename,
ExtraArgs={"ContentType": content_type},
ExtraArgs={"ContentType": "application/pdf"},
)
url = s3.generate_presigned_url(
ClientMethod="get_object",
Expand Down
Loading