Skip to content

Akul-Tyagi/PdfiumAndroid

 
 

Repository files navigation

PdfiumAndroid — 16KB Page Aligned & Modernized

JitPack API 21+ Android 15 Compatible 16KB Page Compliant License

One of the first globally modernized forks of PdfiumAndroid with 16KB page size support and rebuilt 64-bit native libraries to comply with Google Play 2025 policies and Android 15 kernel mandates.

A high-performance native PDF rendering library for Android using PDFium from AOSP. This fork provides the native PDF rendering engine with recompiled C++ libraries enforcing 16KB page-size alignment — critical for forward compatibility with Android 15+ and Google Play Store requirements.


🚀 Why This Fork?

In 2024, Google announced that all apps on the Play Store must support 16KB memory page sizes or face removal. This requirement stems from Android 15's kernel changes supporting devices with 16KB page sizes (vs. traditional 4KB).

At the time, the original PdfiumAndroid library was unmaintained and non-compliant. Rather than risk app removal, this fork was created to:

  • Rebuild all native C++ libraries with 16KB page alignment (-Wl,-z,max-page-size=16384)
  • Modernize the entire build toolchain (AGP 8.12.0, Gradle 8.14.2, Java 17, NDK 27.3.13750724)
  • Update to Android 15 (API 35) compile and target SDK
  • Ensure forward compatibility with Google Play 2025 policies
  • Provide 64-bit only builds (arm64-v8a, x86_64) per Google Play requirements
  • Ship modern Chromium-based PDFium with V8 JavaScript engine support

Real-World Validation

This fork has been validated in production applications serving:

  • 700+ active users across 50+ countries
  • 20,000+ sessions with 99.8% crash-free rate

(Metrics verified via Firebase Analytics & Android Vitals)


📦 Installation

Step 1: Add JitPack Repository

Add JitPack to your project's settings.gradle (recommended) or root build.gradle:

// settings.gradle (Gradle 7.0+)
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Or for older projects using build.gradle:

// root build.gradle
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Step 2: Add Dependency

Add the library to your module's build.gradle:

dependencies {
    implementation 'com.github.Akul-Tyagi:PdfiumAndroid:v1.11.4-16kb'
}

✨ What's New in v1.11.4-16kb

🔧 Critical Modernization

Component Before (1.9.x) After (1.11.4-16kb)
Page Size Support 4KB only 16KB aligned
Compile SDK 31 35 (Android 15)
Target SDK 31 35 (Android 15)
Min SDK 19 21
AGP Version 4.x 8.12.0
Gradle Version 6.x 8.14.2
Java Version 8 17
NDK Version 21.x 27.3.13750724
ABIs Supported 32-bit + 64-bit 64-bit only (arm64-v8a, x86_64)
PDFium Source AOSP 7.1.2 Chromium-based with V8
Jetifier Required Disabled

🛠 Native Library Changes

This fork includes completely rebuilt native libraries with:

  • 16KB page alignment enforced via linker flags:
    -Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384
  • Modern Chromium-based PDFium with full V8 JavaScript engine
  • Optimized native library loading via unified NativeLoader class
  • 64-bit architectures only (arm64-v8a, x86_64) for Google Play compliance

📦 Native Library Components

Each architecture includes the following shared libraries:

Library Purpose
libpdfium.cr.so Main PDFium rendering engine
libv8.cr.so V8 JavaScript engine for PDF forms
libv8_libbase.cr.so V8 base library
libicuuc.cr.so ICU Unicode support
libthird_party_icu_icui18n.cr.so ICU internationalization
libc++_chrome.so C++ standard library (Chrome build)
libchrome_zlib.cr.so Compression library
libthird_party_abseil-cpp_absl.cr.so Abseil C++ library
libbase_allocator_partition_allocator_*.cr.so Memory allocator libraries

🔧 Technical Details

16KB Page Size Compliance

Android 15 introduces support for devices with 16KB memory page sizes. Apps with native libraries must be compiled with proper alignment to run on these devices.

Build Configuration

Application.mk:

APP_STL := c++_static
APP_CPPFLAGS += -fexceptions
APP_PLATFORM := android-21
APP_ABI := arm64-v8a x86_64

# 16KB compatibility flags
APP_LDFLAGS += -Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384

Android.mk:

# Force 16KB page alignment on the JNI library
LOCAL_LDFLAGS += -Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384

This ensures:

  • ✅ Compatibility with 16KB page size devices (Android 15+)
  • ✅ Backward compatibility with 4KB devices
  • ✅ Google Play Store compliance

Build Configuration

// build.gradle
android {
    namespace "com.shockwave.pdfium"
    compileSdk 35
    
    defaultConfig {
        minSdk 21
        targetSdk 35
        versionName "1.11.4-16kb"
        ndk { abiFilters "arm64-v8a", "x86_64" }
    }
    
    ndkVersion "27.3.13750724"
    
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

JitPack Build Configuration

# jitpack.yml
jdk:
  - openjdk21

before_install:
  - sdkmanager --install "ndk;27.3.13750724" >/dev/null
  - yes | sdkmanager --licenses >/dev/null

install:
  - export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.3.13750724"
  - chmod +x gradlew
  - ./gradlew --no-daemon clean :assembleRelease :publishToMavenLocal

📖 Usage

Simple Example

void openPdf() {
    ImageView iv = (ImageView) findViewById(R.id.imageView);
    ParcelFileDescriptor fd = ...;
    int pageNum = 0;
    PdfiumCore pdfiumCore = new PdfiumCore(context);
    try {
        PdfDocument pdfDocument = pdfiumCore.newDocument(fd);

        pdfiumCore.openPage(pdfDocument, pageNum);

        int width = pdfiumCore.getPageWidthPoint(pdfDocument, pageNum);
        int height = pdfiumCore.getPageHeightPoint(pdfDocument, pageNum);

        // ARGB_8888 - best quality, high memory usage, higher possibility of OutOfMemoryError
        // RGB_565 - little worse quality, twice less memory usage
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        pdfiumCore.renderPageBitmap(pdfDocument, bitmap, pageNum, 0, 0, width, height);
        // For annotations and form fields, add 'true' as last param

        iv.setImageBitmap(bitmap);

        pdfiumCore.closeDocument(pdfDocument); // important!
    } catch(IOException ex) {
        ex.printStackTrace();
    }
}

Kotlin Example

fun openPdf() {
    val pdfiumCore = PdfiumCore(context)
    val fd: ParcelFileDescriptor = /* get file descriptor */
    
    try {
        val pdfDocument = pdfiumCore.newDocument(fd)
        pdfiumCore.openPage(pdfDocument, 0)
        
        val width = pdfiumCore.getPageWidthPoint(pdfDocument, 0)
        val height = pdfiumCore.getPageHeightPoint(pdfDocument, 0)
        
        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
        pdfiumCore.renderPageBitmap(pdfDocument, bitmap, 0, 0, 0, width, height)
        
        imageView.setImageBitmap(bitmap)
        
        pdfiumCore.closeDocument(pdfDocument)
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Reading Document Metadata

public void printInfo(PdfiumCore core, PdfDocument doc) {
    PdfDocument.Meta meta = core.getDocumentMeta(doc);
    Log.d(TAG, "title = " + meta.getTitle());
    Log.d(TAG, "author = " + meta.getAuthor());
    Log.d(TAG, "subject = " + meta.getSubject());
    Log.d(TAG, "keywords = " + meta.getKeywords());
    Log.d(TAG, "creator = " + meta.getCreator());
    Log.d(TAG, "producer = " + meta.getProducer());
    Log.d(TAG, "creationDate = " + meta.getCreationDate());
    Log.d(TAG, "modDate = " + meta.getModDate());
}

Reading Table of Contents (Bookmarks)

public void printBookmarksTree(List<PdfDocument.Bookmark> tree, String sep) {
    for (PdfDocument.Bookmark b : tree) {
        Log.d(TAG, String.format("%s %s, p %d", sep, b.getTitle(), b.getPageIdx()));
        if (b.hasChildren()) {
            printBookmarksTree(b.getChildren(), sep + "-");
        }
    }
}

// Usage:
List<PdfDocument.Bookmark> toc = pdfiumCore.getTableOfContents(pdfDocument);
printBookmarksTree(toc, "-");

Reading Links from Page

PdfiumCore core = new PdfiumCore(context);
PdfDocument document = core.newDocument(fd);
int pageIndex = 0;

core.openPage(document, pageIndex);
List<PdfDocument.Link> links = core.getPageLinks(document, pageIndex);

for (PdfDocument.Link link : links) {
    RectF mappedRect = core.mapRectToDevice(document, pageIndex, 
            startX, startY, sizeX, sizeY, 0, link.getBounds());

    if (clickedArea(mappedRect)) {
        String uri = link.getUri();
        if (link.getDestPageIdx() != null) {
            // Jump to page
            int destPage = link.getDestPageIdx();
        } else if (uri != null && !uri.isEmpty()) {
            // Open URI using Intent
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
            startActivity(intent);
        }
    }
}

🎛 API Reference

PdfiumCore Methods

Method Description
newDocument(ParcelFileDescriptor) Create document from file
newDocument(ParcelFileDescriptor, String) Create document from password-protected file
newDocument(byte[]) Create document from byte array
newDocument(byte[], String) Create document from password-protected byte array
getPageCount(PdfDocument) Get total page count
openPage(PdfDocument, int) Open single page
openPage(PdfDocument, int, int) Open range of pages
getPageWidth(PdfDocument, int) Get page width in pixels (requires page open)
getPageHeight(PdfDocument, int) Get page height in pixels (requires page open)
getPageWidthPoint(PdfDocument, int) Get page width in points (1/72")
getPageHeightPoint(PdfDocument, int) Get page height in points (1/72")
getPageSize(PdfDocument, int) Get page size without opening page
renderPage(PdfDocument, Surface, ...) Render to Surface
renderPageBitmap(PdfDocument, Bitmap, ...) Render to Bitmap
getDocumentMeta(PdfDocument) Get document metadata
getTableOfContents(PdfDocument) Get bookmarks tree
getPageLinks(PdfDocument, int) Get links from page
mapPageCoordsToDevice(...) Map page coords to screen
mapRectToDevice(...) Map rect from page to screen
closeDocument(PdfDocument) Close document and release resources

Bitmap Quality

// RGB_565 - Lower memory usage (default recommended)
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

// ARGB_8888 - Higher quality, more memory
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

🔒 ProGuard / R8

Add to your ProGuard rules:

-keep class com.shockwave.**

This keeps the Pdfium native interface classes required for JNI calls.


❓ FAQ

Why is the APK/AAB so large?

This library includes native libraries (~34MB per architecture) for high-fidelity PDF rendering with V8 JavaScript support. Use APK splits or App Bundles to reduce download size:

android {
    bundle {
        abi {
            enableSplit = true
        }
    }
    // Or for APK splits:
    splits {
        abi {
            enable true
            reset()
            include 'arm64-v8a', 'x86_64'
            universalApk false
        }
    }
}

Why only 64-bit architectures?

Google Play requires 64-bit support and is phasing out 32-bit-only apps. This fork focuses on 64-bit architectures (arm64-v8a, x86_64) for optimal compatibility and performance.

What about 32-bit devices?

The src/main/jni/lib directory contains prebuilt libraries for 32-bit architectures (armeabi-v7a, x86) from the original build, but they are not 16KB aligned. For 16KB compliance, only use the libraries in src/main/jniLibs/.

Why use this instead of Android's built-in PdfRenderer?

  • No support for password-protected PDFs
  • No link/bookmark handling or form rendering
  • No JavaScript support for interactive PDFs
  • Limited rendering quality options
  • No document metadata extraction

📚 Related Projects


📄 Changelog

v1.11.4-16kb (2024)

  • 🎯 16KB page size compliance — Critical for Android 15 and Play Store 2025
  • ⬆️ Compile/Target SDK updated to 35 (Android 15)
  • ⬆️ AGP updated to 8.12.0
  • ⬆️ Gradle updated to 8.14.2
  • ⬆️ NDK updated to 27.3.13750724
  • ⬆️ Java 17 support
  • ⬆️ Min SDK raised to 21
  • 🔄 Rebuilt native libraries with Chromium-based PDFium + V8
  • 🔄 New unified NativeLoader for reliable library loading
  • ✅ 64-bit only builds (arm64-v8a, x86_64)
  • ✅ Jetifier disabled

See CHANGELOG.md for full version history from upstream.


🏗 Building from Source

Prerequisites

  • Android Studio with AGP 8.12.0+ support
  • JDK 17+
  • Android NDK 27.3.13750724

Build Steps

# Clone the repository
git clone https://github.com/Akul-Tyagi/PdfiumAndroid.git
cd PdfiumAndroid

# Build the library
./gradlew assembleRelease

# Publish to local Maven
./gradlew publishToMavenLocal

Rebuilding Native Libraries

The prebuilt native libraries are already 16KB aligned. If you need to rebuild:

cd src/main/jni
$ANDROID_NDK_HOME/ndk-build

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Submit a pull request

📜 License

Original work Copyright 2015 Bekket McClane
Modified work Copyright 2016 Bartosz Schiller
Modified work Copyright 2022 Min Hiew
Modified work Copyright 2024 Akul Tyagi (16KB modernization fork)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

---

PDFium License (BSD-style):
Copyright 2014 PDFium Authors. All rights reserved.
See LICENSE file for full terms.

🙏 Acknowledgments


⭐ Star this repo if it helped you comply with Google Play 2025 requirements! ⭐

About

A high-performance native PDF rendering library for Android using PDFium from AOSP. This fork provides the native PDF rendering engine with recompiled C++ libraries enforcing 16KB page-size alignment — critical for forward compatibility with Android 15+ and Google Play Store requirements.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • C 88.2%
  • C++ 6.5%
  • Java 5.0%
  • Makefile 0.3%