From fa63b019dc19be9efaeee36493ae22c42995232b Mon Sep 17 00:00:00 2001 From: Kanstantsin Valeitsenak <59480976+ainceborn@users.noreply.github.com> Date: Mon, 15 Jun 2026 19:13:59 +0300 Subject: [PATCH 1/2] 3.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: restore clipping path support in PageDrawer The Note box text in certain PDFs (e.g. WoW manual) was invisible because the decorative frame image was painted over the text without the intended clipping restriction. Root cause 1 — W/W* operators were silently ignored clip() only stored clipWindingRule but never called intersectClippingPath(). The clipping path in the graphics state was therefore never updated, so images were drawn over the full page instead of just the frame/border area. Fixed by immediately calling intersectClippingPath(linePath) inside clip(), matching the behaviour of the original Apache PDFBox. Root cause 2 — drawImage() never applied the current clipping path Even after fixing clip(), images were drawn without respecting the active clip because drawImage() never called setClip() before painting. Fixed by adding setClip() at the start of drawImage(). Regression fix — image flipping on pages with tiling patterns After the above fixes, images on pages that use a Pattern fill (/P1 scn) were rendered upside-down. The cause was drawTilingPattern(): it switches to a temporary Canvas for the pattern, calls setClip() which updates clipSaveCount to a save-level valid only for that temporary Canvas, then returns without restoring clipSaveCount. The next setClip() call on the main Canvas called canvas.restoreToCount() with a stale value, rolling the main Canvas back past the page y-flip transform, which flipped every subsequent image. Fixed by saving and restoring clipSaveCount around the tiling pattern rendering, the same way lastClip is already handled. --- README.md | 2 +- gradle.properties | 2 +- .../pdfbox/rendering/PageDrawer.java | 20 +++++++++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6a01619a..57889bf5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ repositories { } ... dependencies { - implementation 'com.github.ainceborn:PdfBox-Android-3:3.0.1' + implementation 'com.github.ainceborn:PdfBox-Android-3:3.0.2' } ``` diff --git a/gradle.properties b/gradle.properties index 03a73c84..e8e5b388 100755 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ org.gradle.jvmargs=-Xmx4096M -XX:MaxMetaspaceSize=1024m -Dkotlin.daemon.jvm.opti org.gradle.parallel=true org.gradle.caching=true -VERSION_NAME=3.0.1 +VERSION_NAME=3.0.2 VERSION_CODE=1 ANDROID_BUILD_MIN_SDK_VERSION=26 diff --git a/library/src/main/java/com/ainceborn/pdfbox/rendering/PageDrawer.java b/library/src/main/java/com/ainceborn/pdfbox/rendering/PageDrawer.java index cc0280a1..2f0bb937 100644 --- a/library/src/main/java/com/ainceborn/pdfbox/rendering/PageDrawer.java +++ b/library/src/main/java/com/ainceborn/pdfbox/rendering/PageDrawer.java @@ -281,6 +281,7 @@ public void drawTilingPattern(Canvas canvas, Path savedLinePath = this.linePath; Path.FillType savedClipFillType = this.clipWindingRule; Region savedLastClip = this.lastClip; + int savedClipSaveCount = this.clipSaveCount; // must save/restore to avoid corrupting main canvas state Path savedInitialClip = this.initialClip; boolean savedFlipTG = this.flipTG; @@ -288,6 +289,7 @@ public void drawTilingPattern(Canvas canvas, this.linePath = new Path(); this.clipWindingRule = Path.FillType.WINDING; this.lastClip = null; + this.clipSaveCount = 0; // reset for the tiling pattern's canvas this.initialClip = null; this.flipTG = true; setRenderingHints(); @@ -298,6 +300,7 @@ public void drawTilingPattern(Canvas canvas, this.canvas = savedCanvas; this.linePath = savedLinePath; this.lastClip = savedLastClip; + this.clipSaveCount = savedClipSaveCount; // restore main canvas clip state this.initialClip = savedInitialClip; this.clipWindingRule = savedClipFillType; } @@ -761,8 +764,16 @@ public void fillAndStrokePath(Path.FillType windingRule) throws IOException @Override public void clip(Path.FillType windingRule) { - // the clipping path will not be updated until the succeeding painting operator is called - clipWindingRule = windingRule; + // Apply clip immediately like original PDFBox (not deferred to endPath) + // PDFBOX-4949: don't clip if path is empty ("W n" only, no actual path) + if (!linePath.isEmpty()) + { + linePath.setFillType(windingRule); + getGraphicsState().intersectClippingPath(linePath); + } + // PDFBOX-3836: reset lastClip so setClip() re-evaluates on next draw call + lastClip = null; + clipWindingRule = null; } @Override @@ -855,6 +866,9 @@ public void drawImage(PDImage pdImage) throws IOException } } + // Apply current clipping path before drawing image (matches original PDFBox behaviour) + setClip(); + if (pdImage.isStencil()) { if (graphicsState.getNonStrokingColor().getColorSpace() instanceof PDPattern) @@ -1010,8 +1024,6 @@ else if (scaleX != 0 && scaleY != 0) // the setRenderingHint method, so we re-set all hints, see PDFBOX-2302 setRenderingHints(); } - - canvas.save(); } private void drawBufferedImageV2(PDImage pdImage, Bitmap image, AffineTransform at, Canvas canvas) throws IOException From b006229eb06338bd0f052838ecd499178cb5e616 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 03:13:30 +0000 Subject: [PATCH 2/2] Bump actions/checkout from 4 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/android-ci.yml | 2 +- .github/workflows/gradle-wrapper-validation.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index c433ddb6..a70f4e96 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Fetch Source - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup Java uses: actions/setup-java@v3.11.0 diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 91a86b3a..41d82543 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -6,5 +6,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: gradle/wrapper-validation-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index adba4f3d..2bd6bf56 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Configure JDK uses: actions/setup-java@v3.11.0