name: Build and test
on: [push, pull_request]
jobs:
check-no-default-features:
name: Check and test (no default features)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --no-default-features --verbose
- run: cargo test --no-default-features --verbose
check-feature-combinations:
name: Check feature combinations
runs-on: ubuntu-latest
strategy:
matrix:
features:
- "text_layout"
- "images"
- "images,png"
- "images,jpeg"
- "images,gif"
- "images,tiff"
- "images,bmp"
- "images,webp"
- "images,png,jpeg,gif,tiff,bmp,webp"
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --no-default-features --features "${{ matrix.features }}" --verbose
check-and-test-unix:
name: Cargo check and test on Unix systems
strategy:
matrix:
channel: [stable]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install ${{ matrix.channel }} toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.channel }}
- run: cargo check --verbose --examples
- run: cargo check --verbose
- run: cargo test --verbose
- run: cargo test --verbose --features svg --test svg
check-and-test-windows:
name: Cargo check and test on Windows
strategy:
matrix:
target-sys: ["i686-pc-windows", "x86_64-pc-windows"]
target-abi: ["msvc"]
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Install toolchain for ${{ matrix.target-sys }}-${{ matrix.target-abi }}
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target-sys }}-${{ matrix.target-abi }}
- run: cargo check --verbose --target ${{ matrix.target-sys }}-${{ matrix.target-abi }} --examples
- run: cargo check --verbose --target ${{ matrix.target-sys }}-${{ matrix.target-abi }}
- run: cargo test --verbose --target ${{ matrix.target-sys }}-${{ matrix.target-abi }}
check-wasm32:
name: Cargo check on wasm32
strategy:
matrix:
channel: [stable]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install toolchain using wasm32-unknown-unknown target
uses: dtolnay/rust-toolchain@master
with:
target: wasm32-unknown-unknown
toolchain: ${{ matrix.channel }}
- run: rustup target add wasm32-wasip1
- run: rustup target add wasm32-wasip2
- run: rustup target add wasm32-unknown-unknown
- run: cargo check --verbose --target wasm32-unknown-unknown --examples
- run: cargo check --verbose --target wasm32-unknown-unknown
- run: cargo check --verbose --target wasm32-wasip1 --examples
- run: cargo check --verbose --target wasm32-wasip1
- run: cargo check --verbose --target wasm32-wasip2 --examples
- run: cargo check --verbose --target wasm32-wasip2
- run: cargo check --verbose --target wasm32-wasip2 --no-default-features
- run: cargo check --verbose --target wasm32-wasip2 --features "gif,jpeg,png,pnm,tiff,bmp,ico,tga,hdr,dds,webp"
- run: cargo check --verbose --target wasm32-unknown-unknown --features js-sys
- run: cargo check --verbose --target wasm32-wasip2 --features js-sys
- run: cargo check --verbose --target wasm32-unknown-unknown --features svg
check-all-features-native:
name: Check --all-features on the docs.rs target
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --all-features --verbose
font-verification-external:
name: Verify fonts with external tools (poppler)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install poppler-utils
run: sudo apt-get update && sudo apt-get install -y poppler-utils
- run: pdffonts -v && pdftotext -v
- run: cargo test --features html --test external_tools --test font_embedding --test font_embedding_mock --test font -- --nocapture
font-verification-viewer-semantics:
name: Verify Identity-H codes with fontTools (Acrobat semantics, runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install fontTools
run: pip install fonttools
- name: Build the otf-font example PDFs (full embed + subset)
run: |
cargo run --example otf-font
cargo run --example otf-font-subset
- name: Verify code/charset/W/ToUnicode consistency against the source text
run: |
python3 scripts/verify_pdf_font.py font.pdf --expect scripts/otf_font_expected.txt
python3 scripts/verify_pdf_font.py font_subset.pdf --expect scripts/otf_font_expected.txt
- name: Mock fonts regenerate byte-identically (defined-metrics oracle stays intact)
run: |
python3 scripts/gen_mock_fonts.py
git diff --exit-code tests/assets/fonts/mock/
consumer-resolution:
name: Fresh consumer resolution (ignore committed lockfile)
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- { args: "" }
- { args: "--no-default-features" }
- { args: "--no-default-features --features jpeg", regression279: true }
- { args: "--no-default-features --features svg" }
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Build as a consumer would (fresh lockfile, registry dependencies)
env:
ADD_ARGS: ${{ matrix.args }}
run: |
set -euo pipefail
cargo new "$RUNNER_TEMP/consumer"
cd "$RUNNER_TEMP/consumer"
# $ADD_ARGS is intentionally word-split.
cargo add printpdf --path "$GITHUB_WORKSPACE" $ADD_ARGS
cargo check
- name: "Regression: known-bad old pins in the consumer lockfile must still build"
if: matrix.regression279 == true
run: |
set -euo pipefail
cd "$RUNNER_TEMP/consumer"
# Both pins are printpdf's declared dependency FLOORS. A floor that does not
# compile is exactly how #279 shipped (lopdf's `time` feature broke on every
# time <= 0.3.47; pinning our 0.3.36 floor replays that scenario and more):
# - time 0.3.36: declared floor; 0.3.25–0.3.35 fail inference on new rustc.
# - image 0.25.2: declared floor; `image::ImageReader` first exists there.
# Pin failures (cargo refuses because the graph's floors moved) are skips,
# not errors — the scenario has become inexpressible, which is fine.
PINNED=false
if cargo update -p time --precise 0.3.36; then PINNED=true; else
echo "::notice::time 0.3.36 no longer pinnable in this graph — skipping that scenario."
fi
if cargo update -p image --precise 0.25.2; then PINNED=true; else
echo "::notice::image 0.25.2 no longer pinnable in this graph — skipping that scenario."
fi
if [ "$PINNED" = true ]; then
cargo check
fi
publish-safety:
name: No [patch], and the published dep graph passes the font suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install poppler-utils
run: sudo apt-get update && sudo apt-get install -y poppler-utils
- name: Refuse to build with a [patch.crates-io] section
run: |
if grep -q '^\[patch\.' Cargo.toml; then
echo "::error::Cargo.toml has a [patch] section. cargo publish strips it, so the"
echo "::error::published crate would be built against untested dependencies."
echo "::error::Publish the patched dependency to crates.io and pin it instead."
grep -n '^\[patch\.' -A5 Cargo.toml
exit 1
fi
echo "no [patch] section — what we build is what we publish"
- name: Package size stays under the crates.io cap
run: |
set -euo pipefail
# Everything under examples/assets is include_bytes!'d by tests/examples and
# must ship, which parks the package at ~87% of crates.io's 10 MiB limit. One
# carelessly added asset and `cargo publish` refuses mid-release. --no-verify:
# this step only measures; the jobs around it do the building.
cargo package --no-verify
CRATE=$(ls target/package/*.crate)
SIZE=$(stat -c%s "$CRATE")
LIMIT=10485760
echo "package: $CRATE = $SIZE bytes ($(( SIZE * 100 / LIMIT ))% of the crates.io limit)"
if [ "$SIZE" -gt $(( LIMIT * 95 / 100 )) ]; then
echo "::error::.crate is $SIZE bytes — over 95% of crates.io's $LIMIT-byte limit. Remove or shrink assets before releasing."
exit 1
fi
- name: Dependency versions a published printpdf resolves to
run: cargo tree --features html -i azul-layout || true
- run: cargo test --features html --test external_tools --test font_embedding --test font --test no_text_layout || true
- run: cargo test --features html --test external_tools --test font_embedding --test font
- run: cargo test --no-default-features --test no_text_layout
generate-pdf-artifacts:
name: Generate PDF artifacts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Run no-feature examples
run: |
for ex in advanced bookmarks customfont graphics layers multipage text otf-font; do
echo "=== Running $ex ==="
cargo run --example "$ex" || true
done
- name: Run image example
run: cargo run --example image --features "images,png" || true
- name: Run svg example
run: cargo run --example svg --features svg || true
- name: Run HTML examples
run: |
for ex in html css_shapes html_full html_full_debug shape pagination_test \
baseline_alignment table_debug debug_pagination debug_displaylist \
pagination_boundary_test margin_collapse margin_collapse_inline \
margin_collapse_border_test margin_collapse_comprehensive; do
echo "=== Running $ex ==="
cargo run --example "$ex" || true
done
- name: Collect PDFs
run: |
mkdir -p pdf-artifacts
find . -maxdepth 1 -name '*.pdf' -exec cp {} pdf-artifacts/ \;
ls -la pdf-artifacts/
- name: Upload PDF artifacts
uses: actions/upload-artifact@v4
with:
name: pdf-output-${{ github.sha }}
path: pdf-artifacts/
retention-days: 30