Skip to content
Closed
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
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
<!-- TODO: Update badges to point to this 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

Expand Down
8 changes: 5 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ 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'
classpath 'com.android.tools.build:gradle:8.11.1'
}
}

Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
# 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
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
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
49 changes: 26 additions & 23 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand All @@ -40,17 +40,17 @@ android {
}
}
lint {
abortOnError false
abortOnError = false
}
namespace 'com.tom_roush.pdfbox'
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
android.libraryVariants.all { variant ->
android.libraryVariants.configureEach { variant ->
variant.outputs.all { output ->
if (outputFileName.endsWith('.aar')) {
outputFileName = "pdfbox-android-${variant.name}-${defaultConfig.versionName}.aar"
Expand All @@ -63,30 +63,30 @@ 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.enabled = false
reports.junitXml.enabled = false
reports.html.required = false
reports.junitXml.required = false

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
}

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))
}
}
}
Expand All @@ -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'
Expand Down
21 changes: 10 additions & 11 deletions library/maven-publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ apply plugin: 'signing'

ext.isReleaseVersion = !version.endsWith("SNAPSHOT")

task androidJavadocs(type: Javadoc) {
failOnError false
tasks.register('androidJavadocs', Javadoc) {
failOnError = false
source = android.sourceSets.main.java.sourceFiles
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))

Expand All @@ -25,18 +25,17 @@ task androidJavadocs(type: Javadoc) {
exclude '**/R.java'
}

afterEvaluate {
androidJavadocs.classpath += files(android.libraryVariants.collect { variant ->
variant.javaCompileProvider.get().classpath.files
})
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
}
Expand Down Expand Up @@ -84,9 +83,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()
Expand Down
39 changes: 34 additions & 5 deletions library/src/main/java/com/tom_roush/pdfbox/filter/JPXFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
* <a href="https://github.com/ThalesGroup/JP2ForAndroid">JP2ForAndroid</a>.
* 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
Expand Down Expand Up @@ -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");
Expand All @@ -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
Expand All @@ -135,6 +152,7 @@ private Bitmap readJPX(InputStream input, DecodeOptions options, DecodeResult re
}

return image;
*/
}

/**
Expand All @@ -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();
*/
}
}
17 changes: 10 additions & 7 deletions sample/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
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
multiDexEnabled = true
}

buildTypes {
Expand All @@ -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 {
Expand All @@ -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'
}
Loading