From 8dbc734e773444cef54dcdbadb8637152d81d1fb Mon Sep 17 00:00:00 2001 From: Nikolas Padilla Date: Tue, 2 Sep 2025 15:33:49 -0700 Subject: [PATCH 1/6] Update README to clarify maintained fork status --- README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d408870f..8ab63a16 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,21 @@ -PdfBox-Android +PdfBox-Android (Maintained Fork) ============== -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.tom-roush/pdfbox-android/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/com.tom-roush/pdfbox-android/) -[![Build Status](https://github.com/TomRoush/PdfBox-Android/actions/workflows/android-ci.yml/badge.svg?branch=master)](https://github.com/TomRoush/PdfBox-Android/actions) + + + -A port of Apache's PdfBox library to be usable on Android. Most features should be implemented by now. Feature requests can be added to the issue tracker. Stable releases can be added as a Gradle dependency from Maven Central. +This is a maintained fork of [TomRoush/PdfBox-Android](https://github.com/TomRoush/PdfBox-Android), which is a port of Apache's PdfBox library to be usable on Android. The original project has not received updates in over 2 years, so I've taken on maintenance to keep dependencies up to date. + +## Purpose and Maintenance Focus + +I maintain this fork primarily for form filling functionality in mobile applications. My focus areas are: + +- **Dependency updates**: Keeping BouncyCastle and other dependencies current +- **Java version compatibility**: Supporting latest Android/Java versions +- **Form filling features**: Maintaining and improving PDF form manipulation capabilities +- **Basic maintenance**: Bug fixes and compatibility updates + +**Note**: I may not actively monitor or implement other PDF features beyond form filling. Pull requests for additional features are welcome, but my primary focus remains on dependency maintenance and form-related functionality. The main code of this project is licensed under the Apache 2.0 License, found at http://www.apache.org/licenses/LICENSE-2.0.html From f07484d0968fa6221cb1ae80e3ea4c7e1257ffc3 Mon Sep 17 00:00:00 2001 From: Nikolas Padilla Date: Tue, 2 Sep 2025 16:40:08 -0700 Subject: [PATCH 2/6] Fix build errors by removing JCenter dependencies (#2) * Remove obsolete MaxPermSize JVM option * Remove JPX/JPEG-2000 dependency due to JCenter shutdown --- build.gradle | 6 ++- gradle.properties | 4 +- library/build.gradle | 5 ++- .../tom_roush/pdfbox/filter/JPXFilter.java | 39 ++++++++++++++++--- sample/build.gradle | 5 ++- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index bed855e7..aca764bd 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,8 @@ buildscript { repositories { mavenCentral() google() - jcenter() // jp2-android + // TODO: JCenter removed - jp2-android dependency unavailable after JCenter shutdown + // jcenter() // jp2-android } dependencies { classpath 'com.android.tools.build:gradle:7.4.2' @@ -20,7 +21,8 @@ allprojects { // maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/" } mavenCentral() google() - jcenter() + // TODO: JCenter removed - jp2-android dependency unavailable after JCenter shutdown + // jcenter() } // Show more warnings if desired diff --git a/gradle.properties b/gradle.properties index dad4641b..7e6dacfb 100755 --- a/gradle.properties +++ b/gradle.properties @@ -10,8 +10,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m +# org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true VERSION_NAME=2.0.27.1-SNAPSHOT diff --git a/library/build.gradle b/library/build.gradle index c23a2afe..1fc1cef5 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -96,7 +96,10 @@ dependencies { api "org.bouncycastle:bcpkix-jdk15to18:1.73" api "org.bouncycastle:bcutil-jdk15to18:1.73" // for jpeg2000 decode/encode - compileOnly 'com.gemalto.jp2:jp2-android:1.0.3' + // TODO: JPX/JPEG-2000 support removed due to dependency unavailability + // See issue #1 for reimplementation: https://github.com/muddxyii/PdfBox-Android/issues/1 + // Original dependency: com.gemalto.jp2:jp2-android:1.0.3 (JCenter shutdown) + // compileOnly 'com.gemalto.jp2:jp2-android:1.0.3' // Test dependencies testImplementation 'junit:junit:4.13.2' diff --git a/library/src/main/java/com/tom_roush/pdfbox/filter/JPXFilter.java b/library/src/main/java/com/tom_roush/pdfbox/filter/JPXFilter.java index be006da3..3acf1018 100644 --- a/library/src/main/java/com/tom_roush/pdfbox/filter/JPXFilter.java +++ b/library/src/main/java/com/tom_roush/pdfbox/filter/JPXFilter.java @@ -26,8 +26,11 @@ import java.io.InputStream; import java.io.OutputStream; -import com.gemalto.jp2.JP2Decoder; -import com.gemalto.jp2.JP2Encoder; +// TODO: JPX/JPEG-2000 support removed due to dependency unavailability +// See issue #1 for reimplementation: https://github.com/muddxyii/PdfBox-Android/issues/1 +// Original dependency: com.gemalto.jp2:jp2-android:1.0.3 (JCenter shutdown) +// import com.gemalto.jp2.JP2Decoder; +// import com.gemalto.jp2.JP2Encoder; import com.tom_roush.pdfbox.cos.COSDictionary; import com.tom_roush.pdfbox.cos.COSName; import com.tom_roush.pdfbox.io.IOUtils; @@ -37,8 +40,10 @@ * Decompress data encoded using the wavelet-based JPEG 2000 standard, * reproducing the original data. * - * Requires the JP2ForAndroid library to be available from com.gemalto.jp2:jp2-android:1.0.3, see - * JP2ForAndroid. + * NOTE: JPX/JPEG-2000 support temporarily removed due to dependency unavailability. + * The required JP2ForAndroid library (com.gemalto.jp2:jp2-android:1.0.3) is no longer + * available after JCenter shutdown. See issue #1 for reimplementation plans: + * https://github.com/muddxyii/PdfBox-Android/issues/1 * * @author John Hewson * @author Timo Boehme @@ -90,9 +95,19 @@ public DecodeResult decode(InputStream encoded, OutputStream decoded, return decode(encoded, decoded, parameters, index, DecodeOptions.DEFAULT); } - // try to read using JP2ForAndroid + // JPX/JPEG-2000 decoding - graceful degradation private Bitmap readJPX(InputStream input, DecodeOptions options, DecodeResult result) throws IOException { + // TODO: JPX/JPEG-2000 support removed due to dependency unavailability + // See issue #1 for reimplementation: https://github.com/muddxyii/PdfBox-Android/issues/1 + // Original dependency: com.gemalto.jp2:jp2-android:1.0.3 (JCenter shutdown) + throw new MissingImageReaderException( + "JPX/JPEG-2000 image support is temporarily unavailable. " + + "The required JP2ForAndroid library is no longer accessible after JCenter shutdown. " + + "See https://github.com/muddxyii/PdfBox-Android/issues/1 for reimplementation progress."); + + // Original implementation preserved for reference: + /* try { Class.forName("com.gemalto.jp2.JP2Decoder"); @@ -108,7 +123,9 @@ private Bitmap readJPX(InputStream input, DecodeOptions options, DecodeResult re // decoder.setSourceRegion(options.getSourceRegion()); Bitmap image = decoder.decode(); + */ + /* COSDictionary parameters = result.getParameters(); // "If the image stream uses the JPXDecode filter, this entry is optional @@ -135,6 +152,7 @@ private Bitmap readJPX(InputStream input, DecodeOptions options, DecodeResult re } return image; + */ } /** @@ -144,9 +162,20 @@ private Bitmap readJPX(InputStream input, DecodeOptions options, DecodeResult re protected void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { + // TODO: JPX/JPEG-2000 support removed due to dependency unavailability + // See issue #1 for reimplementation: https://github.com/muddxyii/PdfBox-Android/issues/1 + // Original dependency: com.gemalto.jp2:jp2-android:1.0.3 (JCenter shutdown) + throw new IOException( + "JPX/JPEG-2000 image encoding is temporarily unavailable. " + + "The required JP2ForAndroid library is no longer accessible after JCenter shutdown. " + + "See https://github.com/muddxyii/PdfBox-Android/issues/1 for reimplementation progress."); + + // Original implementation preserved for reference: + /* Bitmap bitmap = BitmapFactory.decodeStream(input); byte[] jpeBytes = new JP2Encoder(bitmap).encode(); IOUtils.copy(new ByteArrayInputStream(jpeBytes), encoded); encoded.flush(); + */ } } diff --git a/sample/build.gradle b/sample/build.gradle index abc4e1e6..5e9db051 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -38,5 +38,8 @@ dependencies { // Optional dependencies // Read JPX images - implementation 'com.gemalto.jp2:jp2-android:1.0.3' + // TODO: JPX/JPEG-2000 support removed due to dependency unavailability + // See issue #1 for reimplementation: https://github.com/muddxyii/PdfBox-Android/issues/1 + // Original dependency: com.gemalto.jp2:jp2-android:1.0.3 (JCenter shutdown) + // implementation 'com.gemalto.jp2:jp2-android:1.0.3' } From 4ca0117a2c015ebf0f0724275f451d551a8c1aa0 Mon Sep 17 00:00:00 2001 From: Nikolas Padilla Date: Tue, 2 Sep 2025 17:33:46 -0700 Subject: [PATCH 3/6] Update Gradle and Android SDK versions for compatibility --- build.gradle | 2 +- gradle.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- library/build.gradle | 10 +++++----- sample/build.gradle | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index aca764bd..d2c89fc6 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { // jcenter() // jp2-android } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.11.1' } } diff --git a/gradle.properties b/gradle.properties index 7e6dacfb..4f79a4ce 100755 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ VERSION_NAME=2.0.27.1-SNAPSHOT VERSION_CODE=1 ANDROID_BUILD_MIN_SDK_VERSION=14 -ANDROID_BUILD_TARGET_SDK_VERSION=33 -ANDROID_BUILD_SDK_VERSION=33 +ANDROID_BUILD_TARGET_SDK_VERSION=35 +ANDROID_BUILD_SDK_VERSION=35 android.useAndroidX=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7e62b17a..9bb857a0 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/library/build.gradle b/library/build.gradle index 1fc1cef5..063c5f6c 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -9,13 +9,13 @@ ext { } android { - compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) + compileSdk Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) compileOptions.encoding = 'UTF-8' defaultConfig { testInstrumentationRunnerArguments notAnnotation: 'androidx.test.filters.FlakyTest' - minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) - targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + minSdk Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) + targetSdk Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-proguard-rules.txt' } @@ -66,8 +66,8 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent tasks.withType(Test) { // Performance improvements for tests maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 - reports.html.enabled = false - reports.junitXml.enabled = false + reports.html.required = false + reports.junitXml.required = false testLogging { // set options for log level LIFECYCLE diff --git a/sample/build.gradle b/sample/build.gradle index 5e9db051..1f45c21c 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) + compileSdk Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) defaultConfig { applicationId 'com.tom_roush.pdfbox.sample' - minSdkVersion 14 - targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + minSdk 14 + targetSdk Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) versionName project.VERSION_NAME versionCode Integer.parseInt(project.VERSION_CODE) multiDexEnabled true From 3d3b63080480e8be99db0c6422a987d32b834282 Mon Sep 17 00:00:00 2001 From: Nikolas Padilla Date: Tue, 2 Sep 2025 17:44:24 -0700 Subject: [PATCH 4/6] Update Java compatibility to Java 8 --- library/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 063c5f6c..c5c6b972 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -45,8 +45,8 @@ android { namespace 'com.tom_roush.pdfbox' compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } // Rename the output aars From 72422dd94cd764194cb279d4dce424fc029a7f9c Mon Sep 17 00:00:00 2001 From: Nikolas Padilla Date: Tue, 2 Sep 2025 17:50:56 -0700 Subject: [PATCH 5/6] Fix Gradle deprecation warnings and configuration resolution issues --- library/build.gradle | 18 +++++++++--------- library/maven-publish.gradle | 14 ++++++-------- sample/build.gradle | 12 ++++++------ 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index c5c6b972..5d694ed5 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -9,13 +9,13 @@ ext { } android { - compileSdk Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) + compileSdk = Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) compileOptions.encoding = 'UTF-8' defaultConfig { testInstrumentationRunnerArguments notAnnotation: 'androidx.test.filters.FlakyTest' - minSdk Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) - targetSdk Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + minSdk = Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) + targetSdk = Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-proguard-rules.txt' } @@ -40,9 +40,9 @@ android { } } lint { - abortOnError false + abortOnError = false } - namespace 'com.tom_roush.pdfbox' + namespace = 'com.tom_roush.pdfbox' compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -72,10 +72,10 @@ tasks.withType(Test) { testLogging { // set options for log level LIFECYCLE events TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED, TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR - showExceptions true - exceptionFormat TestExceptionFormat.FULL - showCauses true - showStackTraces true + showExceptions = true + exceptionFormat = TestExceptionFormat.FULL + showCauses = true + showStackTraces = true info.events = debug.events info.exceptionFormat = debug.exceptionFormat diff --git a/library/maven-publish.gradle b/library/maven-publish.gradle index c34745ba..10570e9e 100644 --- a/library/maven-publish.gradle +++ b/library/maven-publish.gradle @@ -4,7 +4,7 @@ apply plugin: 'signing' ext.isReleaseVersion = !version.endsWith("SNAPSHOT") task androidJavadocs(type: Javadoc) { - failOnError false + failOnError = false source = android.sourceSets.main.java.sourceFiles classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) @@ -25,10 +25,8 @@ task androidJavadocs(type: Javadoc) { exclude '**/R.java' } -afterEvaluate { - androidJavadocs.classpath += files(android.libraryVariants.collect { variant -> - variant.javaCompileProvider.get().classpath.files - }) +android.libraryVariants.all { variant -> + androidJavadocs.classpath += variant.javaCompileProvider.get().classpath } task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { @@ -84,9 +82,9 @@ project.afterEvaluate { artifact androidJavadocsJar artifact androidSourcesJar - groupId project.PUBLISH_GROUP_ID - artifactId project.PUBLISH_ARTIFACT_ID - version project.PUBLISH_VERSION + groupId = project.PUBLISH_GROUP_ID + artifactId = project.PUBLISH_ARTIFACT_ID + version = project.PUBLISH_VERSION pom.withXml { def root = asNode() diff --git a/sample/build.gradle b/sample/build.gradle index 1f45c21c..82f70ed8 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdk Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) + compileSdk = Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) defaultConfig { applicationId 'com.tom_roush.pdfbox.sample' - minSdk 14 - targetSdk Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + minSdk = 14 + targetSdk = Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) versionName project.VERSION_NAME versionCode Integer.parseInt(project.VERSION_CODE) - multiDexEnabled true + multiDexEnabled = true } buildTypes { @@ -24,9 +24,9 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } lint { - abortOnError false + abortOnError = false } - namespace 'com.tom_roush.pdfbox.sample' + namespace = 'com.tom_roush.pdfbox.sample' } dependencies { From b33a5cb0b349c9468a8143ce6fbb2c1a107287e1 Mon Sep 17 00:00:00 2001 From: Nikolas Padilla Date: Tue, 2 Sep 2025 17:57:14 -0700 Subject: [PATCH 6/6] Refactor Gradle scripts to address deprecation warnings and improve task configuration --- library/build.gradle | 18 +++++++++--------- library/maven-publish.gradle | 9 +++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 5d694ed5..b526b110 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -50,7 +50,7 @@ android { } // Rename the output aars - android.libraryVariants.all { variant -> + android.libraryVariants.configureEach { variant -> variant.outputs.all { output -> if (outputFileName.endsWith('.aar')) { outputFileName = "pdfbox-android-${variant.name}-${defaultConfig.versionName}.aar" @@ -63,7 +63,7 @@ android { import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent -tasks.withType(Test) { +tasks.withType(Test).configureEach { // Performance improvements for tests maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 reports.html.required = false @@ -79,14 +79,14 @@ tasks.withType(Test) { info.events = debug.events info.exceptionFormat = debug.exceptionFormat + } - afterSuite { desc, result -> - if (!desc.parent) { // will match the outermost suite - def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" - def startItem = '| ', endItem = ' |' - def repeatLength = startItem.length() + output.length() + endItem.length() - println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) - } + afterSuite { desc, result -> + if (!desc.parent) { // will match the outermost suite + def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" + def startItem = '| ', endItem = ' |' + def repeatLength = startItem.length() + output.length() + endItem.length() + println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) } } } diff --git a/library/maven-publish.gradle b/library/maven-publish.gradle index 10570e9e..aee7e62a 100644 --- a/library/maven-publish.gradle +++ b/library/maven-publish.gradle @@ -3,7 +3,7 @@ apply plugin: 'signing' ext.isReleaseVersion = !version.endsWith("SNAPSHOT") -task androidJavadocs(type: Javadoc) { +tasks.register('androidJavadocs', Javadoc) { failOnError = false source = android.sourceSets.main.java.sourceFiles classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) @@ -25,16 +25,17 @@ task androidJavadocs(type: Javadoc) { exclude '**/R.java' } -android.libraryVariants.all { variant -> +android.libraryVariants.configureEach { variant -> androidJavadocs.classpath += variant.javaCompileProvider.get().classpath } -task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { +tasks.register('androidJavadocsJar', Jar) { + dependsOn androidJavadocs archiveClassifier.set('javadoc') from androidJavadocs.destinationDir } -task androidSourcesJar(type: Jar) { +tasks.register('androidSourcesJar', Jar) { archiveClassifier.set('sources') from android.sourceSets.main.java.srcDirs }