Initial commit

This commit is contained in:
Aryan 2026-02-24 17:37:40 +05:30
commit 6072b2ba29
844 changed files with 220532 additions and 0 deletions

1
pdfiumandroid/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

1
pdfiumandroid/arrow/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

193
pdfiumandroid/arrow/build.gradle.kts vendored Normal file
View file

@ -0,0 +1,193 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jreleaser.model.Active
import org.jreleaser.model.Signing
plugins {
id("com.android.library")
alias(libs.plugins.kotlin.android)
alias(libs.plugins.detekt)
alias(libs.plugins.kover)
alias(libs.plugins.ktlint)
alias(libs.plugins.jreleaser)
`maven-publish`
signing
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
android {
namespace = "io.legere.pdfiumandroid.arrow"
compileSdk = 35
defaultConfig {
minSdk = 23
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
publishing {
singleVariant("release") {
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
compileOptions {
sourceCompatibility(JavaVersion.VERSION_17)
targetCompatibility(JavaVersion.VERSION_17)
}
}
dependencies {
implementation(project(":pdfiumandroid"))
implementation(libs.kotlinx.coroutines.android)
implementation(libs.arrow.core)
implementation(libs.androidx.runner)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.kotlinx.coroutines.test)
androidTestImplementation(libs.androidx.core.testing)
androidTestImplementation(libs.arrow.fx.coroutines)
}
fun isReleaseBuild(): Boolean = !findProject("VERSION_NAME").toString().contains("SNAPSHOT")
fun getReleaseRepositoryUrl(): String =
if (rootProject.hasProperty("RELEASE_REPOSITORY_URL")) {
rootProject.properties["RELEASE_REPOSITORY_URL"] as String
} else {
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
fun getSnapshotRepositoryUrl(): String =
if (rootProject.hasProperty("SNAPSHOT_REPOSITORY_URL")) {
rootProject.properties["SNAPSHOT_REPOSITORY_URL"] as String
} else {
"https://oss.sonatype.org/content/repositories/snapshots/"
}
fun getRepositoryUrl(): String = if (isReleaseBuild()) getReleaseRepositoryUrl() else getSnapshotRepositoryUrl()
fun getRepositoryUsername(): String =
if (rootProject.hasProperty("JRELEASER_MAVENCENTRAL_USERNAME")) {
rootProject.properties["JRELEASER_MAVENCENTRAL_USERNAME"] as String
} else {
""
}
fun getRepositoryPassword(): String =
if (rootProject.hasProperty("JRELEASER_MAVENCENTRAL_TOKEN")) {
rootProject.properties["JRELEASER_MAVENCENTRAL_TOKEN"] as String
} else {
""
}
publishing {
publications {
create<MavenPublication>("maven") {
groupId = "io.legere"
artifactId = "pdfium-android-kt-arrow"
version = project.property("VERSION_NAME") as String
pom {
name.set("pdfiumandroid.arrow")
description.set("Arrow support for PdfiumAndroid")
url.set(rootProject.properties["POM_URL"] as String)
licenses {
license {
name.set(rootProject.properties["POM_LICENCE_NAME"] as String)
url.set(rootProject.properties["POM_LICENCE_URL"] as String)
distribution.set(rootProject.properties["POM_LICENCE_DIST"] as String)
}
}
developers {
developer {
id.set(rootProject.properties["POM_DEVELOPER_ID"] as String)
name.set(rootProject.properties["POM_DEVELOPER_NAME"] as String)
}
}
scm {
connection.set(rootProject.properties["POM_SCM_CONNECTION"] as String)
developerConnection.set(rootProject.properties["POM_SCM_DEV_CONNECTION"] as String)
url.set(rootProject.properties["POM_SCM_URL"] as String)
}
}
afterEvaluate {
from(components["release"])
}
}
}
repositories {
maven {
url =
uri(layout.buildDirectory.dir("target/staging-deploy"))
}
}
}
jreleaser {
project {
inceptionYear = "2023"
author("@johngray1965")
description = "Arrow support for PdfiumAndroid"
version = rootProject.properties["VERSION_NAME"] as String
license = "http://www.apache.org/licenses/LICENSE-2.0.txt"
links {
homepage = "https://github.com/johngray1965/PdfiumAndroidKt"
license = "http://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
gitRootSearch = true
signing {
active = Active.ALWAYS
mode = Signing.Mode.COMMAND
armored = true
verify = false
command {
executable = "gpg"
keyName = "4BBF8FAB"
publicKeyring = "/Users/gray/.gnupg/secring.gpg"
}
}
release {
github {
skipRelease = true
}
}
// distributions {
// create("pdfiumandroid.arrow") {
// artifact {
// path = file("build/distributions/{{distributionName}}-{{projectVersion}}.zip")
// }
// }
// }
deploy {
maven {
mavenCentral.create("sonatype") {
active = Active.ALWAYS
verifyPom = false
url = "https://central.sonatype.com/api/v1/publisher"
stagingRepository(
layout.buildDirectory
.dir("target/staging-deploy")
.get()
.toString(),
)
username = getRepositoryUsername()
}
}
}
}

View file

4
pdfiumandroid/arrow/gradle.properties vendored Normal file
View file

@ -0,0 +1,4 @@
POM_NAME=pdfiumandroid
POM_ARTIFACT_ID=pdfiumandroid
POM_PACKAGING=aar

21
pdfiumandroid/arrow/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,135 @@
package io.legere.pdfiumandroid.arrow
import androidx.test.ext.junit.runners.AndroidJUnit4
import arrow.core.raise.either
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.PdfWriteCallback
import io.legere.pdfiumandroid.arrow.base.BasePDFTest
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfDocumentKtFTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKtF
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCoreKtF(Dispatchers.Unconfined).newDocument(pdfBytes).getOrNull()!!
}
@After
fun tearDown() =
runTest {
pdfDocument.close()
}
@Test
fun getPageCount() =
runTest {
either {
val pageCount = pdfDocument.getPageCount().bind()
assertThat(pageCount).isEqualTo(4)
}
}
@Test
fun openPage() =
runTest {
either {
val page = pdfDocument.openPage(0).bind()
assertThat(page).isNotNull()
}
}
@Test
fun openPages() =
runTest {
either {
val page = pdfDocument.openPages(0, 3).bind()
assertThat(page.size).isEqualTo(4)
}
}
@Test
fun getDocumentMeta() =
runTest {
either {
val meta = pdfDocument.getDocumentMeta().bind()
assertThat(meta).isNotNull()
}
}
@Test
fun getTableOfContents() =
runTest {
either {
// I don't think this test document has a table of contents
val toc = pdfDocument.getTableOfContents().bind()
TestCase.assertNotNull(toc)
assertThat(toc.size).isEqualTo(0)
}
}
@Test
fun openTextPage() =
runTest {
either {
val page = pdfDocument.openPage(0).bind()
val textPage = page.openTextPage().bind()
assertThat(textPage).isNotNull()
}
}
@Test
fun openTextPages() =
runTest {
either {
val textPages = pdfDocument.openTextPages(0, 3).bind()
assertThat(textPages.size).isEqualTo(4)
}
}
@Test
fun saveAsCopy() =
runTest {
pdfDocument.saveAsCopy(
object : PdfWriteCallback {
override fun WriteBlock(data: ByteArray?): Int {
// Truth.assertThat(data?.size).isEqualTo(pdfBytes?.size)
// Truth.assertThat(data).isEqualTo(pdfBytes)
return data?.size ?: 0
}
},
)
}
fun close() =
runTest {
either {
var documentAfterClose: PdfDocumentKtF?
PdfiumCoreKtF(Dispatchers.Unconfined).newDocument(pdfBytes).bind().use {
documentAfterClose = it
}
documentAfterClose?.openPage(0)
}.mapLeft {
assertThat(it).isInstanceOf(PdfiumKtFErrors.AlreadyClosed::class.java)
}
}
}

View file

@ -0,0 +1,316 @@
package io.legere.pdfiumandroid.arrow
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import arrow.core.raise.either
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.arrow.base.BasePDFTest
import io.legere.pdfiumandroid.util.Size
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfPageKtFTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKtF
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCoreKtF(Dispatchers.Unconfined).newDocument(pdfBytes).getOrNull()!!
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun getPageWidth() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val pageWidth = page.getPageWidth(72).bind()
assertThat(pageWidth).isEqualTo(612) // 8.5 inches * 72 dpi
}
}
}
@Test
fun getPageHeight() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val pageWidth = page.getPageHeight(72).bind()
assertThat(pageWidth).isEqualTo(792) // 11 inches * 72 dpi
}
}
}
@Test
fun getPageWidthPoint() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val pageWidth = page.getPageWidthPoint().bind()
assertThat(pageWidth).isEqualTo(612) // 11 inches * 72 dpi
}
}
}
@Test
fun getPageHeightPoint() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val pageWidth = page.getPageHeightPoint().bind()
assertThat(pageWidth).isEqualTo(792) // 11 inches * 72 dpi
}
}
}
@Test
fun getPageCropBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val cropBox = page.getPageCropBox().bind()
assertThat(cropBox).isEqualTo(noResultRect)
}
}
}
@Test
fun getPageMediaBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val mediaBox = page.getPageMediaBox().bind()
assertThat(mediaBox).isEqualTo(RectF(0.0f, 0.0f, 612.0f, 792.0f))
}
}
}
@Test
fun getPageBleedBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val bleedBox = page.getPageBleedBox().bind()
assertThat(bleedBox).isEqualTo(noResultRect)
}
}
}
@Test
fun getPageTrimBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val trimBox = page.getPageTrimBox().bind()
assertThat(trimBox).isEqualTo(noResultRect)
}
}
}
@Test
fun getPageArtBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val artBox = page.getPageArtBox().bind()
assertThat(artBox).isEqualTo(noResultRect)
}
}
}
@Test
fun getPageBoundingBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val artBox = page.getPageBoundingBox().bind()
assertThat(artBox).isEqualTo(RectF(0f, 792f, 612f, 0f))
}
}
}
@Test
fun getPageSize() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val size = page.getPageSize(72).bind()
assertThat(size).isEqualTo(Size(612, 792))
}
}
}
@Test
fun renderPage() =
runTest {
// I really don't know how to test it
}
@Test
fun testRenderPage() =
runTest {
// I really don't know how to test it
}
@Test
fun renderPageBitmap() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val conf = Bitmap.Config.RGB_565 // see other conf types
val bmp = Bitmap.createBitmap(612, 792, conf) // this creates a MUTABLE bitmap
page.renderPageBitmap(bmp, 0, 0, 612, 792)
// How to verify that it's correct?
// Even if we don't verify the bitmap, we can check that it doesn't crash
}
}
}
@Test
fun testRenderPageBitmap() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val conf = Bitmap.Config.RGB_565 // see other conf types
val bmp = Bitmap.createBitmap(612, 792, conf) // this creates a MUTABLE bitmap
page.renderPageBitmap(bmp, 0, 0, 612, 792, renderAnnot = true, textMask = true)
// How to verify that it's correct?
// Even if we don't verify the bitmap, we can check that it doesn't crash
}
}
}
@Test
fun getPageLinks() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val links = page.getPageLinks().bind()
assertThat(links.size).isEqualTo(0) // The test doc doesn't have links
}
}
}
@Test
fun mapPageCoordsToDevice() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val devicePt = page.mapPageCoordsToDevice(0, 0, 100, 100, 0, 0.0, 0.0).bind()
assertThat(devicePt).isEqualTo(Point(0, 100))
}
}
}
@Test
fun mapDeviceCoordsToPage() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val devicePt = page.mapDeviceCoordsToPage(0, 0, 100, 100, 0, 0, 0).bind()
assertThat(devicePt).isEqualTo(PointF(0f, 792.00006f))
}
}
}
@Test
fun mapRectToDevice() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val devicePt = page.mapRectToDevice(0, 0, 100, 100, 0, RectF(0f, 0f, 100f, 100f)).bind()
assertThat(devicePt).isEqualTo(
Rect(
// 0f in coords to 0f in device
0,
// 0f in corrds in at the bottom, the bottom of the device is 100f
100,
// 100f in coords = 100f/(8.5*72) * 100f = 16f
16,
// 100f in coords = 100 - 100f/(11*72) * 100f = 87f
87,
),
)
}
}
}
@Test
fun mapRectToPage() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
val devicePt = page.mapRectToPage(0, 0, 100, 100, 0, Rect(0, 0, 100, 100)).bind()
assertThat(devicePt).isEqualTo(
RectF(0.0f, 792.00006f, 612.0f, 0.0f),
)
}
}
}
fun close() =
runTest {
either {
var pageAfterClose: PdfPageKtF?
pdfDocument.openPage(0).bind().use { page ->
pageAfterClose = page
}
pageAfterClose!!.getPageWidth(72)
}.mapLeft {
assertThat(it).isInstanceOf(PdfiumKtFErrors.AlreadyClosed::class.java)
}
}
@Test
fun getPage() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
assertThat(page).isNotNull()
}
}
}
}

View file

@ -0,0 +1,114 @@
package io.legere.pdfiumandroid.arrow
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import arrow.core.raise.either
import com.google.common.truth.Truth
import io.legere.pdfiumandroid.arrow.base.BasePDFTest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfPageLinkKtFTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKtF
private lateinit var pdfPage: PdfPageKtF
private lateinit var pdfTextPage: PdfTextPageKtF
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("pdf-test.pdf")
Truth.assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCoreKtF(Dispatchers.Unconfined).newDocument(pdfBytes).getOrNull()!!
pdfPage = pdfDocument.openPage(0).getOrNull()!!
pdfTextPage = pdfPage.openTextPage().getOrNull()!!
}
@After
fun tearDown() {
pdfTextPage.close()
pdfPage.close()
pdfDocument.close()
}
@Test
fun testLink(): TestResult =
runTest {
either {
pdfTextPage.loadWebLink().bind().use { links ->
Truth.assertThat(links).isNotNull()
}
}
}
@Test
fun testCountWebLinks(): TestResult =
runTest {
either {
pdfTextPage.loadWebLink().bind().use { links ->
Truth.assertThat(links).isNotNull()
Truth.assertThat(links.countWebLinks().bind()).isEqualTo(1)
}
}
}
@Test
fun testGetTextRange(): TestResult =
runTest {
either {
pdfTextPage.loadWebLink().bind().use { links ->
Truth.assertThat(links).isNotNull()
Truth.assertThat(links.getTextRange(0).bind()).isEqualTo(Pair(351, 31))
}
}
}
@Test
fun testGetUrl(): TestResult =
runTest {
either {
pdfTextPage.loadWebLink().bind().use { links ->
Truth.assertThat(links).isNotNull()
val (_, count) = links.getTextRange(0).bind()
Truth
.assertThat(links.getURL(0, count).bind())
.isEqualTo("http://www.education.gov.yk.ca/")
}
}
}
@Test
fun testCountRects(): TestResult =
runTest {
either {
pdfTextPage.loadWebLink().bind().use { links ->
Truth.assertThat(links).isNotNull()
val count = links.countRects(0).bind()
Truth.assertThat(count).isEqualTo(1)
}
}
}
@Test
fun testGetRect(): TestResult =
runTest {
either {
pdfTextPage.loadWebLink().bind().use { links ->
Truth.assertThat(links).isNotNull()
val count = links.getRect(0, 0).bind()
Truth
.assertThat(count)
.isEqualTo(RectF(221.46f, 480.624f, 389.66394f, 469.152f))
}
}
}
}

View file

@ -0,0 +1,235 @@
package io.legere.pdfiumandroid.arrow
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import arrow.core.raise.either
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.arrow.base.BasePDFTest
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfTextPageKtTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKtF
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("f01.pdf")
TestCase.assertNotNull(pdfBytes)
pdfDocument = PdfiumCoreKtF(Dispatchers.Unconfined).newDocument(pdfBytes).getOrNull()!!
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun textPageCountChars() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val charCount = textPage.textPageCountChars().bind()
assertThat(charCount).isEqualTo(3468)
}
}
}
}
@Test
fun textPageGetText() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val text = textPage.textPageGetText(0, 100).bind()
assertThat(text?.length).isEqualTo(100)
}
}
}
}
@Test
fun textPageGetUnicode() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val char = textPage.textPageGetUnicode(0).bind()
assertThat(char).isEqualTo('T')
}
}
}
}
@Test
fun textPageGetCharBox() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val rect = textPage.textPageGetCharBox(0).bind()
assertThat(rect)
.isEqualTo(RectF(90.314415f, 715.3187f, 103.44171f, 699.1206f))
}
}
}
}
@Test
fun textPageGetCharIndexAtPos() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val characterToLookup = 0
val rect = textPage.textPageGetCharBox(characterToLookup).bind()
val pos =
textPage
.textPageGetCharIndexAtPos(
rect?.centerX()?.toDouble() ?: 0.0,
rect?.centerY()?.toDouble() ?: 0.0,
// Shouldn't need much since we're in the middle of the rect
1.0,
1.0,
).bind()
assertThat(pos).isEqualTo(characterToLookup)
}
}
}
}
@Test
fun textPageCountRects() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val rectCount = textPage.textPageCountRects(0, 100).bind()
assertThat(rectCount).isEqualTo(4)
}
}
}
}
@Test
fun textPageGetRect() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val rect = textPage.textPageGetRect(0).bind()
assertThat(rect).isEqualTo(RectF(0f, 0f, 0f, 0f))
}
}
}
}
@Test
fun textPageGetBoundedText() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val text = textPage.textPageGetBoundedText(RectF(0f, 97f, 100f, 100f), 100).bind()
assertThat(text).isEqualTo("Do")
}
}
}
}
@Test
fun getFontSize() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val fontSize = textPage.getFontSize(0).bind()
assertThat(fontSize).isEqualTo(22.559999465942383)
}
}
}
}
@Test
fun findStart(): TestResult =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
val findWhat = "children's"
val startIndex = 0
textPage.findStart(findWhat, emptySet(), startIndex).bind().use { findHandle ->
var result = findHandle.findNext()
assertThat(result.bind()).isTrue()
var index = findHandle.getSchResultIndex().bind()
var count = findHandle.getSchCount().bind()
var text = textPage.textPageGetText(index, count).bind()
assertThat(index).isEqualTo(1525)
assertThat(count).isEqualTo(10)
assertThat(text).isEqualTo(findWhat)
result = findHandle.findNext()
assertThat(result.bind()).isTrue()
index = findHandle.getSchResultIndex().bind()
count = findHandle.getSchCount().bind()
text = textPage.textPageGetText(index, count).bind()
assertThat(index).isEqualTo(2761)
assertThat(count).isEqualTo(10)
assertThat(text).isEqualTo(findWhat)
result = findHandle.findNext()
assertThat(result.bind()).isFalse()
}
}
}
}
}
fun close() =
runTest {
either {
var pageAfterClose: PdfTextPageKtF?
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
pageAfterClose = textPage
}
}
pageAfterClose!!.textPageCountChars()
}.mapLeft {
assertThat(it).isInstanceOf(PdfiumKtFErrors.AlreadyClosed::class.java)
}
}
@Test
fun getPage() =
runTest {
either {
pdfDocument.openPage(0).bind().use { page ->
page.openTextPage().bind().use { textPage ->
assertThat(textPage.page).isNotNull()
}
}
}
}
}

View file

@ -0,0 +1,39 @@
package io.legere.pdfiumandroid.arrow
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.arrow.base.BasePDFTest
import io.legere.pdfiumandroid.arrow.base.ByteArrayPdfiumSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfiumCoreKtFTest : BasePDFTest() {
@Test
fun newDocument() =
runTest {
val pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
val pdfiumCore = PdfiumCoreKtF(Dispatchers.Unconfined)
val pdfDocument = pdfiumCore.newDocument(pdfBytes)
assertThat(pdfDocument).isNotNull()
}
@Test
fun newDocumentWithCustomSource() =
runTest {
val pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
val pdfiumCore = PdfiumCoreKtF(Dispatchers.Unconfined)
val pdfDocument = pdfiumCore.newDocument(ByteArrayPdfiumSource(pdfBytes!!))
assertThat(pdfDocument).isNotNull()
}
}

View file

@ -0,0 +1,27 @@
package io.legere.pdfiumandroid.arrow.base
import android.graphics.RectF
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
@Suppress("unused")
open class BasePDFTest {
// set to true to skip tests that are not implemented yet
// set to false to force unimplemented tests to fail
val notImplementedAssetValue = false
val noResultRect = RectF(-1f, -1f, -1f, -1f)
fun getPdfBytes(filename: String): ByteArray? {
val appContext = InstrumentationRegistry.getInstrumentation().context
val assetManager = appContext.assets
try {
val input = assetManager.open(filename)
return input.readBytes()
} catch (e: Exception) {
Log.e(BasePDFTest::class.simpleName, "Ugh", e)
}
assetManager.close()
return null
}
}

View file

@ -0,0 +1,28 @@
package io.legere.pdfiumandroid.arrow.base
import io.legere.pdfiumandroid.PdfiumSource
class ByteArrayPdfiumSource(
private val array: ByteArray,
) : PdfiumSource {
override val length: Long
get() = array.size.toLong()
override fun read(
position: Long,
buffer: ByteArray,
size: Int,
): Int {
array.copyInto(
destination = buffer,
destinationOffset = 0,
startIndex = position.toInt(),
endIndex = position.toInt() + size,
)
return size
}
override fun close() {
// nothing to close
}
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest/>

View file

@ -0,0 +1,42 @@
package io.legere.pdfiumandroid.arrow
import arrow.core.Either
import io.legere.pdfiumandroid.FindResult
import kotlinx.coroutines.CoroutineDispatcher
import java.io.Closeable
@Suppress("unused")
class FindResultKtF(
private val findResult: FindResult,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
suspend fun findNext(): Either<PdfiumKtFErrors, Boolean> =
wrapEither(dispatcher) {
findResult.findNext()
}
suspend fun findPrev(): Either<PdfiumKtFErrors, Boolean> =
wrapEither(dispatcher) {
findResult.findPrev()
}
suspend fun getSchResultIndex(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
findResult.getSchResultIndex()
}
suspend fun getSchCount(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
findResult.getSchCount()
}
suspend fun closeFind() {
wrapEither(dispatcher) {
findResult.closeFind()
}
}
override fun close() {
findResult.closeFind()
}
}

View file

@ -0,0 +1,150 @@
@file:Suppress("unused", "CanBeVal")
package io.legere.pdfiumandroid.arrow
import android.graphics.Matrix
import android.graphics.RectF
import android.view.Surface
import arrow.core.Either
import io.legere.pdfiumandroid.PdfDocument
import io.legere.pdfiumandroid.PdfWriteCallback
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.Closeable
/**
* PdfDocumentKtF represents a PDF file and allows you to load pages from it.
* @property document the [PdfDocument] to wrap
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
* @constructor create a [PdfDocumentKtF] from a [PdfDocument]
*/
@Suppress("TooManyFunctions", "CanBeVal")
class PdfDocumentKtF(
val document: PdfDocument,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
/**
* suspend version of [PdfDocument.getPageCount]
*/
suspend fun getPageCount(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
document.getPageCount()
}
/**
* suspend version of [PdfDocument.getPageCharCounts]
*/
suspend fun getPageCharCounts(): Either<PdfiumKtFErrors, IntArray> =
wrapEither(dispatcher) {
document.getPageCharCounts()
}
/**
* suspend version of [PdfDocument.openPage]
*/
suspend fun openPage(pageIndex: Int): Either<PdfiumKtFErrors, PdfPageKtF> =
wrapEither(dispatcher) {
PdfPageKtF(document.openPage(pageIndex), dispatcher)
}
/**
* suspend version of [PdfDocument.openPages]
*/
suspend fun openPages(
fromIndex: Int,
toIndex: Int,
): Either<PdfiumKtFErrors, List<PdfPageKtF>> =
wrapEither(dispatcher) {
document.openPages(fromIndex, toIndex).map { PdfPageKtF(it, dispatcher) }
}
/**
* suspend version of [PdfDocument.renderPages]
*/
@Suppress("LongParameterList", "ComplexMethod", "ComplexCondition")
suspend fun renderPages(
surface: Surface?,
pages: List<PdfPageKtF>,
matrices: List<Matrix>,
clipRects: List<RectF>,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
renderCoroutinesDispatcher: CoroutineDispatcher,
): Boolean {
return withContext(renderCoroutinesDispatcher) {
return@withContext surface
?.let {
document.renderPages(
surface,
pages.map { page -> page.page },
matrices,
clipRects,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
} ?: false
}
}
/**
* suspend version of [PdfDocument.deletePage]
*/
suspend fun deletePage(pageIndex: Int): Unit =
withContext(dispatcher) {
document.deletePage(pageIndex)
}
/**
* suspend version of [PdfDocument.getDocumentMeta]
*/
suspend fun getDocumentMeta(): Either<PdfiumKtFErrors, PdfDocument.Meta> =
wrapEither(dispatcher) {
document.getDocumentMeta()
}
/**
* suspend version of [PdfDocument.getTableOfContents]
*/
suspend fun getTableOfContents(): Either<PdfiumKtFErrors, List<PdfDocument.Bookmark>> =
wrapEither(dispatcher) {
document.getTableOfContents()
}
/**
* suspend version of [PdfDocument.openTextPages]
*/
suspend fun openTextPages(
fromIndex: Int,
toIndex: Int,
): Either<PdfiumKtFErrors, List<PdfTextPageKtF>> =
wrapEither(dispatcher) {
document.openTextPages(fromIndex, toIndex).map { PdfTextPageKtF(it, dispatcher) }
}
/**
* suspend version of [PdfDocument.saveAsCopy]
*/
suspend fun saveAsCopy(callback: PdfWriteCallback): Either<PdfiumKtFErrors, Boolean> =
wrapEither(dispatcher) {
document.saveAsCopy(callback)
}
/**
* Close the document
* @throws IllegalArgumentException if document is closed
*/
override fun close() {
document.close()
}
fun safeClose(): Either<PdfiumKtFErrors, Boolean> =
Either
.catch {
document.close()
true
}.mapLeft { exceptionToPdfiumKtFError(it) }
}

View file

@ -0,0 +1,384 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.arrow
import android.graphics.Bitmap
import android.graphics.Matrix
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.view.Surface
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import io.legere.pdfiumandroid.Logger
import io.legere.pdfiumandroid.PdfDocument
import io.legere.pdfiumandroid.PdfPage
import io.legere.pdfiumandroid.PdfiumCore
import io.legere.pdfiumandroid.util.Size
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.Closeable
/**
* PdfPageKtF represents a single page of a PDF file.
* @property page the [PdfPage] to wrap
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
*/
@Suppress("TooManyFunctions")
class PdfPageKtF(
val page: PdfPage,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
/**
* Open a text page
* @throws IllegalArgumentException if document is closed or the page cannot be loaded
*/
suspend fun openTextPage(): Either<PdfiumKtFErrors, PdfTextPageKtF> =
wrapEither(dispatcher) {
PdfTextPageKtF(page.openTextPage(), dispatcher)
}
/**
* suspend version of [PdfPage.getPageWidth]
*/
suspend fun getPageWidth(screenDpi: Int): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.getPageWidth(screenDpi)
}
/**
* suspend version of [PdfPage.getPageHeight]
*/
suspend fun getPageHeight(screenDpi: Int): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.getPageHeight(screenDpi)
}
/**
* suspend version of [PdfPage.getPageWidthPoint]
*/
suspend fun getPageWidthPoint(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.getPageWidthPoint()
}
/**
* suspend version of [PdfPage.getPageHeightPoint]
*/
suspend fun getPageHeightPoint(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.getPageHeightPoint()
}
/**
* suspend version of [PdfPage.getPageMatrix]
*/
suspend fun getPageMatrix(): Either<PdfiumKtFErrors, Matrix> =
wrapEither(dispatcher) {
page.getPageMatrix() ?: error("Page matrix is null")
}
/**
* suspend version of [PdfPage.getPageRotation]
*/
suspend fun getPageRotation(): Either<PdfiumKtFErrors, Int> =
withContext(dispatcher) {
Either
.catch {
val rotation = page.getPageRotation()
if (rotation < 0) {
error("Invalid rotation: $rotation")
}
rotation
}.mapLeft {
exceptionToPdfiumKtFError(it)
}
}
/**
* suspend version of [PdfPage.getPageCropBox]
*/
suspend fun getPageCropBox(): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.getPageCropBox()
}
/**
* suspend version of [PdfPage.getPageMediaBox]
*/
suspend fun getPageMediaBox(): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.getPageMediaBox()
}
/**
* suspend version of [PdfPage.getPageBleedBox]
*/
suspend fun getPageBleedBox(): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.getPageBleedBox()
}
/**
* suspend version of [PdfPage.getPageTrimBox]
*/
suspend fun getPageTrimBox(): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.getPageTrimBox()
}
/**
* suspend version of [PdfPage.getPageArtBox]
*/
suspend fun getPageArtBox(): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.getPageArtBox()
}
/**
* suspend version of [PdfPage.getPageBoundingBox]
*/
suspend fun getPageBoundingBox(): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.getPageBoundingBox()
}
/**
* suspend version of [PdfPage.getPageSize]
*/
suspend fun getPageSize(screenDpi: Int): Either<PdfiumKtFErrors, Size> =
wrapEither(dispatcher) {
page.getPageSize(screenDpi)
}
/**
* suspend version of [PdfPage.renderPage]
*/
@Suppress("LongParameterList", "ComplexCondition")
suspend fun renderPage(
surface: Surface?,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
renderCoroutinesDispatcher: CoroutineDispatcher,
): Either<PdfiumKtFErrors, Boolean> {
val sizes = IntArray(2)
val pointers = LongArray(2)
return withContext(renderCoroutinesDispatcher) {
surface?.let {
PdfPage.lockSurface(
it,
sizes,
pointers,
)
val nativeWindow = pointers[0]
val bufferPtr = pointers[1]
val surfaceWidth = sizes[0]
val surfaceHeight = sizes[1]
Logger.d(
"PdfPageKtF",
"page: ${page.pageIndex}, surfaceWidth: $surfaceWidth, " +
"surfaceHeight: $surfaceHeight, nativeWindow: $nativeWindow, " +
"bufferPtr: $bufferPtr, nativeWindow: $nativeWindow",
)
if (bufferPtr == 0L || bufferPtr == -1L || nativeWindow == 0L || nativeWindow == -1L) {
PdfiumKtFErrors.ConstraintError.left()
}
val result =
page.renderPage(
bufferPtr,
startX,
startY,
drawSizeX,
drawSizeY,
canvasColor = canvasColor,
pageBackgroundColor = pageBackgroundColor,
)
PdfPage.unlockSurface(longArrayOf(nativeWindow, bufferPtr))
if (!result) {
PdfiumKtFErrors.ConstraintError.left()
}
true.right()
} ?: PdfiumKtFErrors.ConstraintError.left()
}
}
/**
* suspend version of [PdfPage.renderPage]
*/
@Suppress("LongParameterList")
suspend fun renderPage(
surface: Surface?,
matrix: Matrix,
clipRect: RectF,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
renderCoroutinesDispatcher: CoroutineDispatcher,
): Either<PdfiumKtFErrors, Boolean> {
return PdfiumCore.surfaceMutex.withLock {
withContext(renderCoroutinesDispatcher) {
return@withContext surface?.let {
val retValue =
page.renderPage(
surface,
matrix,
clipRect,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
if (!retValue) {
PdfiumKtFErrors.ConstraintError.left()
} else {
true.right()
}
} ?: PdfiumKtFErrors.ConstraintError.left()
}
}
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.renderPageBitmap]
*/
suspend fun renderPageBitmap(
bitmap: Bitmap,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
): Either<PdfiumKtFErrors, Boolean> =
wrapEither(dispatcher) {
page.renderPageBitmap(
bitmap,
startX,
startY,
drawSizeX,
drawSizeY,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
true
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.renderPageBitmap]
*/
suspend fun renderPageBitmap(
bitmap: Bitmap?,
matrix: Matrix,
clipRect: RectF,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
): Either<PdfiumKtFErrors, Boolean> =
wrapEither(dispatcher) {
page.renderPageBitmap(bitmap, matrix, clipRect, renderAnnot, textMask, canvasColor, pageBackgroundColor)
true
}
/**
* suspend version of [PdfPage.getPageLinks]
*/
suspend fun getPageLinks(): Either<PdfiumKtFErrors, List<PdfDocument.Link>> =
wrapEither(dispatcher) {
page.getPageLinks()
}
/**
* suspend version of [PdfPage.mapPageCoordsToDevice]
*/
@Suppress("LongParameterList")
suspend fun mapPageCoordsToDevice(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
pageX: Double,
pageY: Double,
): Either<PdfiumKtFErrors, Point> =
wrapEither(dispatcher) {
page.mapPageCoordsToDevice(startX, startY, sizeX, sizeY, rotate, pageX, pageY)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.mapDeviceCoordsToPage]
*/
suspend fun mapDeviceCoordsToPage(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
deviceX: Int,
deviceY: Int,
): Either<PdfiumKtFErrors, PointF> =
wrapEither(dispatcher) {
page.mapDeviceCoordsToPage(startX, startY, sizeX, sizeY, rotate, deviceX, deviceY)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.mapRectToDevice]
*/
suspend fun mapRectToDevice(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
coords: RectF,
): Either<PdfiumKtFErrors, Rect> =
wrapEither(dispatcher) {
page.mapRectToDevice(startX, startY, sizeX, sizeY, rotate, coords)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.mapRectToPage]
*/
suspend fun mapRectToPage(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
coords: Rect,
): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
page.mapRectToPage(startX, startY, sizeX, sizeY, rotate, coords)
}
/**
* Closes the page
*/
override fun close() {
page.close()
}
fun safeClose(): Either<PdfiumKtFErrors, Boolean> =
Either
.catch {
page.close()
true
}.mapLeft { exceptionToPdfiumKtFError(it) }
}

View file

@ -0,0 +1,47 @@
package io.legere.pdfiumandroid.arrow
import android.graphics.RectF
import arrow.core.Either
import io.legere.pdfiumandroid.PdfPageLink
import kotlinx.coroutines.CoroutineDispatcher
import java.io.Closeable
class PdfPageLinkKtF(
val pageLink: PdfPageLink,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
suspend fun countWebLinks(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
pageLink.countWebLinks()
}
suspend fun getURL(
index: Int,
length: Int,
): Either<PdfiumKtFErrors, String?> =
wrapEither(dispatcher) {
pageLink.getURL(index, length)
}
suspend fun countRects(index: Int): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
pageLink.countRects(index)
}
suspend fun getRect(
linkIndex: Int,
rectIndex: Int,
): Either<PdfiumKtFErrors, RectF> =
wrapEither(dispatcher) {
pageLink.getRect(linkIndex, rectIndex)
}
suspend fun getTextRange(index: Int): Either<PdfiumKtFErrors, Pair<Int, Int>> =
wrapEither(dispatcher) {
pageLink.getTextRange(index)
}
override fun close() {
pageLink.close()
}
}

View file

@ -0,0 +1,149 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.arrow
import android.graphics.RectF
import arrow.core.Either
import io.legere.pdfiumandroid.FindFlags
import io.legere.pdfiumandroid.PdfTextPage
import io.legere.pdfiumandroid.WordRangeRect
import kotlinx.coroutines.CoroutineDispatcher
import java.io.Closeable
/**
* PdfTextPageKtF represents a single text page of a PDF file.
* @property page the [PdfTextPage] to wrap
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
*/
@Suppress("TooManyFunctions")
class PdfTextPageKtF(
val page: PdfTextPage,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
/**
* suspend version of [PdfTextPage.textPageCountChars]
*/
suspend fun textPageCountChars(): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.textPageCountChars()
}
/**
* suspend version of [PdfTextPage.textPageGetText]
*/
suspend fun textPageGetText(
startIndex: Int,
length: Int,
): Either<PdfiumKtFErrors, String?> =
wrapEither(dispatcher) {
page.textPageGetText(startIndex, length)
}
/**
* suspend version of [PdfTextPage.textPageGetUnicode]
*/
suspend fun textPageGetUnicode(index: Int): Either<PdfiumKtFErrors, Char> =
wrapEither(dispatcher) {
page.textPageGetUnicode(index)
}
/**
* suspend version of [PdfTextPage.textPageGetCharBox]
*/
suspend fun textPageGetCharBox(index: Int): Either<PdfiumKtFErrors, RectF?> =
wrapEither(dispatcher) {
page.textPageGetCharBox(index)
}
/**
* suspend version of [PdfTextPage.textPageGetCharIndexAtPos]
*/
suspend fun textPageGetCharIndexAtPos(
x: Double,
y: Double,
xTolerance: Double,
yTolerance: Double,
): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.textPageGetCharIndexAtPos(x, y, xTolerance, yTolerance)
}
/**
* suspend version of [PdfTextPage.textPageCountRects]
*/
suspend fun textPageCountRects(
startIndex: Int,
count: Int,
): Either<PdfiumKtFErrors, Int> =
wrapEither(dispatcher) {
page.textPageCountRects(startIndex, count)
}
/**
* suspend version of [PdfTextPage.textPageGetRect]
*/
suspend fun textPageGetRect(rectIndex: Int): Either<PdfiumKtFErrors, RectF?> =
wrapEither(dispatcher) {
page.textPageGetRect(rectIndex)
}
/**
* suspend version of [PdfTextPage.textPageGetRectsForRanges]
*/
suspend fun textPageGetRectsForRanges(wordRanges: IntArray): Either<PdfiumKtFErrors, List<WordRangeRect>?> =
wrapEither(dispatcher) {
page.textPageGetRectsForRanges(wordRanges)
}
/**
* suspend version of [PdfTextPage.textPageGetBoundedText]
*/
suspend fun textPageGetBoundedText(
rect: RectF,
length: Int,
): Either<PdfiumKtFErrors, String?> =
wrapEither(dispatcher) {
page.textPageGetBoundedText(rect, length)
}
/**
* suspend version of [PdfTextPage.getFontSize]
*/
suspend fun getFontSize(charIndex: Int): Either<PdfiumKtFErrors, Double> =
wrapEither(dispatcher) {
page.getFontSize(charIndex)
}
suspend fun findStart(
findWhat: String,
flags: Set<FindFlags>,
startIndex: Int,
): Either<PdfiumKtFErrors, FindResultKtF> =
wrapEither(dispatcher) {
val findResult = page.findStart(findWhat, flags, startIndex)
if (findResult == null) {
error("findResult is null")
} else {
FindResultKtF(findResult, dispatcher)
}
}
suspend fun loadWebLink(): Either<PdfiumKtFErrors, PdfPageLinkKtF> =
wrapEither(dispatcher) {
PdfPageLinkKtF(page.loadWebLink(), dispatcher)
}
/**
* Close the page and free all resources.
*/
override fun close() {
page.close()
}
fun safeClose(): Either<PdfiumKtFErrors, Boolean> =
Either
.catch {
page.close()
true
}.mapLeft { exceptionToPdfiumKtFError(it) }
}

View file

@ -0,0 +1,18 @@
package io.legere.pdfiumandroid.arrow
import arrow.core.Either
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
suspend inline fun <reified T> wrapEither(
dispatcher: CoroutineDispatcher,
crossinline block: () -> T,
): Either<PdfiumKtFErrors, T> =
withContext(dispatcher) {
Either
.catch {
block()
}.mapLeft {
exceptionToPdfiumKtFError(it)
}
}

View file

@ -0,0 +1,79 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.arrow
import android.os.ParcelFileDescriptor
import arrow.core.Either
import io.legere.pdfiumandroid.PdfiumCore
import io.legere.pdfiumandroid.PdfiumSource
import io.legere.pdfiumandroid.util.Config
import kotlinx.coroutines.CoroutineDispatcher
/**
* PdfiumCoreKtF is the main entry-point for access to the PDFium API.
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
* @constructor create a [PdfiumCoreKtF] from a [PdfiumCore]
*/
class PdfiumCoreKtF(
private val dispatcher: CoroutineDispatcher,
config: Config = Config(),
) {
private val coreInternal = PdfiumCore(config = config)
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(fd: ParcelFileDescriptor): Either<PdfiumKtFErrors, PdfDocumentKtF> =
wrapEither(dispatcher) {
PdfDocumentKtF(coreInternal.newDocument(fd), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(
fd: ParcelFileDescriptor,
password: String?,
): Either<PdfiumKtFErrors, PdfDocumentKtF> =
wrapEither(dispatcher) {
PdfDocumentKtF(coreInternal.newDocument(fd, password), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(data: ByteArray?): Either<PdfiumKtFErrors, PdfDocumentKtF> =
wrapEither(dispatcher) {
PdfDocumentKtF(coreInternal.newDocument(data), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(
data: ByteArray?,
password: String?,
): Either<PdfiumKtFErrors, PdfDocumentKtF> =
wrapEither(dispatcher) {
PdfDocumentKtF(coreInternal.newDocument(data, password), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(data: PdfiumSource): Either<PdfiumKtFErrors, PdfDocumentKtF> =
wrapEither(dispatcher) {
PdfDocumentKtF(coreInternal.newDocument(data), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(
data: PdfiumSource,
password: String?,
): Either<PdfiumKtFErrors, PdfDocumentKtF> =
wrapEither(dispatcher) {
PdfDocumentKtF(coreInternal.newDocument(data, password), dispatcher)
}
}

View file

@ -0,0 +1,20 @@
package io.legere.pdfiumandroid.arrow
sealed class PdfiumKtFErrors {
data class RuntimeException(
val message: String,
) : PdfiumKtFErrors()
data class AlreadyClosed(
val message: String,
) : PdfiumKtFErrors()
data object ConstraintError : PdfiumKtFErrors()
}
fun exceptionToPdfiumKtFError(e: Throwable): PdfiumKtFErrors =
if (e is IllegalStateException && e.message?.contains("Already closed") == true) {
PdfiumKtFErrors.AlreadyClosed(e.message ?: "Unknown error")
} else {
PdfiumKtFErrors.RuntimeException(e.message ?: "Unknown error")
}

224
pdfiumandroid/build.gradle.kts vendored Normal file
View file

@ -0,0 +1,224 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jreleaser.model.Active
import org.jreleaser.model.Signing
plugins {
id("com.android.library")
alias(libs.plugins.kotlin.android)
alias(libs.plugins.detekt)
alias(libs.plugins.kover)
alias(libs.plugins.ktlint)
alias(libs.plugins.jreleaser)
`maven-publish`
signing
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
freeCompilerArgs.add("-Xstring-concat=inline")
}
}
android {
namespace = "io.legere.pdfiumandroid"
compileSdk = 35
ndkVersion = "28.0.12674087"
defaultConfig {
minSdk = 23
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
@Suppress("UnstableApiUsage")
externalNativeBuild {
cmake {
cppFlags("")
}
}
}
buildFeatures {
buildConfig = true
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
// maybeCreate("qa")
// getByName("qa") {
// matchingFallbacks += listOf("release")
// isMinifyEnabled = true
// signingConfig = signingConfigs.getByName("debug")
// }
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
compileOptions {
sourceCompatibility(JavaVersion.VERSION_17)
targetCompatibility(JavaVersion.VERSION_17)
}
publishing {
singleVariant("release") {
// if you don't want sources/javadoc, remove these lines
withSourcesJar()
withJavadocJar()
}
}
}
dependencies {
implementation(libs.kotlinx.coroutines.android)
implementation(libs.androidx.annotation.jvm)
testImplementation(libs.junit)
testImplementation(libs.androidx.junit)
testImplementation(libs.androidx.espresso.core)
testImplementation(libs.truth)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.androidx.core.testing)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.kotlinx.coroutines.test)
androidTestImplementation(libs.androidx.core.testing)
}
fun isReleaseBuild(): Boolean = !findProject("VERSION_NAME").toString().contains("SNAPSHOT")
fun getReleaseRepositoryUrl(): String =
if (rootProject.hasProperty("RELEASE_REPOSITORY_URL")) {
rootProject.properties["RELEASE_REPOSITORY_URL"] as String
} else {
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
fun getSnapshotRepositoryUrl(): String =
if (rootProject.hasProperty("SNAPSHOT_REPOSITORY_URL")) {
rootProject.properties["SNAPSHOT_REPOSITORY_URL"] as String
} else {
"https://oss.sonatype.org/content/repositories/snapshots/"
}
fun getRepositoryUrl(): String = if (isReleaseBuild()) getReleaseRepositoryUrl() else getSnapshotRepositoryUrl()
fun getRepositoryUsername(): String =
if (rootProject.hasProperty("JRELEASER_MAVENCENTRAL_USERNAME")) {
rootProject.properties["JRELEASER_MAVENCENTRAL_USERNAME"] as String
} else {
""
}
fun getRepositoryPassword(): String =
if (rootProject.hasProperty("JRELEASER_MAVENCENTRAL_TOKEN")) {
rootProject.properties["JRELEASER_MAVENCENTRAL_TOKEN"] as String
} else {
""
}
publishing {
publications {
create<MavenPublication>("maven") {
groupId = "io.legere"
artifactId = "pdfiumandroid"
version = project.property("VERSION_NAME") as String
pom {
name.set("pdfiumandroid")
// packaging = rootProject.properties["POM_PACKAGING"] as String
description = rootProject.properties["POM_DESCRIPTION"] as String
url.set(rootProject.properties["POM_URL"] as String)
licenses {
license {
name.set(rootProject.properties["POM_LICENCE_NAME"] as String)
url.set(rootProject.properties["POM_LICENCE_URL"] as String)
distribution.set(rootProject.properties["POM_LICENCE_DIST"] as String)
}
}
developers {
developer {
id.set(rootProject.properties["POM_DEVELOPER_ID"] as String)
name.set(rootProject.properties["POM_DEVELOPER_NAME"] as String)
}
}
scm {
connection.set(rootProject.properties["POM_SCM_CONNECTION"] as String)
developerConnection.set(rootProject.properties["POM_SCM_DEV_CONNECTION"] as String)
url.set(rootProject.properties["POM_SCM_URL"] as String)
}
}
afterEvaluate {
from(components["release"])
}
}
}
repositories {
maven {
url =
uri(layout.buildDirectory.dir("target/staging-deploy"))
}
}
}
jreleaser {
project {
inceptionYear = "2023"
author("@johngray1965")
description = rootProject.properties["POM_DESCRIPTION"] as String
version = rootProject.properties["VERSION_NAME"] as String
}
gitRootSearch = true
signing {
active = Active.ALWAYS
mode = Signing.Mode.COMMAND
armored = true
verify = false
command {
executable = "gpg"
keyName = "4BBF8FAB"
publicKeyring = "/Users/gray/.gnupg/secring.gpg"
}
}
release {
github {
skipRelease = true
}
}
// distributions {
// create("zip") {
// artifacts {
// add(
// layout.buildDirectory
// .dir("libs")
// .map {
// it.file("pdfiumandroid.zip")
// }
// )
// }
// }
// }
deploy {
maven {
mavenCentral.create("sonatype") {
active = Active.ALWAYS
verifyPom = false
url = "https://central.sonatype.com/api/v1/publisher"
stagingRepository(
layout.buildDirectory
.dir("target/staging-deploy")
.get()
.toString(),
)
username = getRepositoryUsername()
}
}
}
}

45
pdfiumandroid/consumer-rules.pro vendored Normal file
View file

@ -0,0 +1,45 @@
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class io.legere.pdfiumandroid.** { *; }
-keep interface io.legere.pdfiumandroid.** { public *; }
-keepclasseswithmembernames class io.legere.pdfiumandroid.** {
public <methods>;
}
-keep class * extends io.legere.pdfiumandroid.LoggerInterface { *; }
-keep class io.legere.pdfiumandroid.suspend.PdfDocumentKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfDocumentKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.suspend.PdfPageKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfPageKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.suspend.PdfTextPageKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfTextPageKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.suspend.PdfiumCoreKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfiumCoreKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.util.AlreadyClosedBehavior { *; }
-keepclassmembers public class io.legere.pdfiumandroid.util.AlreadyClosedBehavior {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.util.Config { *; }
-keepclassmembers public class io.legere.pdfiumandroid.util.Config {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.util.Size { *; }
-keepclassmembers public class io.legere.pdfiumandroid.util.Size {
public <init>(...);
}

4
pdfiumandroid/gradle.properties vendored Normal file
View file

@ -0,0 +1,4 @@
POM_NAME=pdfiumandroid
POM_ARTIFACT_ID=pdfiumandroid
POM_PACKAGING=aar

59
pdfiumandroid/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,59 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class io.legere.pdfiumandroid.** { *; }
-keep interface io.legere.pdfiumandroid.** { public *; }
-keep class * extends io.legere.pdfiumandroid.LoggerInterface { *; }
-dontwarn java.lang.invoke.StringConcatFactory
-keepclasseswithmembernames class io.legere.pdfiumandroid.** {
public <methods>;
}
-keep class io.legere.pdfiumandroid.suspend.PdfDocumentKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfDocumentKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.suspend.PdfPageKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfPageKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.suspend.PdfTextPageKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfTextPageKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.suspend.PdfiumCoreKt { *; }
-keepclassmembers public class io.legere.pdfiumandroid.suspend.PdfiumCoreKt {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.util.AlreadyClosedBehavior { *; }
-keepclassmembers public class io.legere.pdfiumandroid.util.AlreadyClosedBehavior {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.util.Config { *; }
-keepclassmembers public class io.legere.pdfiumandroid.util.Config {
public <init>(...);
}
-keep class io.legere.pdfiumandroid.util.Size { *; }
-keepclassmembers public class io.legere.pdfiumandroid.util.Size {
public <init>(...);
}

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,447 @@
package io.legere.pdfiumandroid
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import io.legere.pdfiumandroid.util.Size
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.system.measureNanoTime
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.measureTime
@RunWith(AndroidJUnit4::class)
class FastNativeTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocument
private var pdfBytes: ByteArray? = null
@Before
fun setUp() {
pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCore().newDocument(pdfBytes)
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun getPagAttributesOpenEveryPass() {
val time =
measureTime {
repeat(10_000) {
pdfDocument.openPage(0).use { page ->
testPageAttributes(page)
}
}
}
val averageDuration = (time / 10_000)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPagAttributesSingleOpen() {
val time =
measureTime {
pdfDocument.openPage(0).use { page ->
repeat(10_000) {
testPageAttributes(page)
}
}
}
val averageDuration = (time / 10_000)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPagAttributesTimeAttributesOnly() {
pdfDocument.openPage(0).use { page ->
val time =
measureTime {
repeat(10_000) {
testPageAttributes(page)
}
}
val averageDuration = (time / 10_000)
println("Total Time: $time, Average Time: $averageDuration")
}
}
@Test
fun getTextPagAttributesOpenEveryPass() {
val time =
measureTime {
repeat(10_000) {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
testTextPageAttributes(textPage)
}
}
}
}
val averageDuration = (time / 10_000)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPTextPagAttributesSingleOpen() {
val time =
measureTime {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
repeat(10_000) {
testTextPageAttributes(textPage)
}
}
}
}
val averageDuration = (time / 10_000)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getTextPagAttributesTimeAttributesOnly() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val time =
measureTime {
repeat(10_000) {
testTextPageAttributes(textPage)
}
}
val averageDuration = (time / 10_000)
println("Total Time: $time, Average Time: $averageDuration")
}
}
}
@Test
fun getPagBitmapOpenEveryPass() {
val iterations = 1_000
val bitmap = Bitmap.createBitmap(612, 792, Bitmap.Config.RGB_565)
val time =
measureTime {
repeat(iterations) {
pdfDocument.openPage(0).use { page ->
page.renderPageBitmap(
bitmap,
0,
0,
612,
792,
)
}
}
}
val averageDuration = time / iterations
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPagBitmapSingleOpen() {
val iterations = 1_000
val bitmap = Bitmap.createBitmap(612, 792, Bitmap.Config.RGB_565)
val time =
measureTime {
pdfDocument.openPage(0).use { page ->
repeat(iterations) {
page.renderPageBitmap(
bitmap,
0,
0,
612,
792,
)
}
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPagBitmapViaMatrixOpenEveryPass() {
val iterations = 1_000
val bitmap = Bitmap.createBitmap(612, 792, Bitmap.Config.RGB_565)
val rect = RectF(0f, 0f, 612f, 792f)
val matrix = Matrix()
val time =
measureNanoTime {
repeat(iterations) {
pdfDocument.openPage(0).use { page ->
page.renderPageBitmap(
bitmap,
matrix,
rect,
)
}
}
}
val totalDuration = time.nanoseconds
val averageDuration = (time / iterations).nanoseconds
println("Total Time: $totalDuration, Average Time: $averageDuration")
}
@Test
fun getPagBitmapViaMatrixSingleOpen() {
val iterations = 1_000
val bitmap = Bitmap.createBitmap(612, 792, Bitmap.Config.RGB_565)
val rect = RectF(0f, 0f, 612f, 792f)
val matrix = Matrix()
val time =
measureTime {
pdfDocument.openPage(0).use { page ->
repeat(iterations) {
page.renderPageBitmap(
bitmap,
matrix,
rect,
)
}
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPagBitmapViaMatrixSingleOpen8x() {
val iterations = 100
val (bitmap, rect, matrix) = commonParams8X(Bitmap.Config.RGB_565)
val time =
measureTime {
pdfDocument.openPage(0).use { page ->
repeat(iterations) {
page.renderPageBitmap(
bitmap,
matrix,
rect,
)
}
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
@Test
fun getPagBitmapViaMatrixSingleOpen8xARGB_8888() {
val iterations = 100
val (bitmap, rect, matrix) = commonParams8X(Bitmap.Config.ARGB_8888)
val time =
measureTime {
pdfDocument.openPage(0).use { page ->
repeat(iterations) {
page.renderPageBitmap(
bitmap,
matrix,
rect,
)
}
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
fun findWordRanges(text: String): List<Pair<Int, Int>> {
val boundaries = Regex("\\b").findAll(text).map { it.range.first }.toMutableList()
boundaries.add(text.length)
return boundaries.zipWithNext { start, end -> Pair(start, end - start) }.filter { it.first < text.length }
}
@Test
fun gettextPageGetRectsForRanges() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val textCharCount =
textPage.textPageCountChars()
if (textCharCount > 0) {
val pageText =
textPage.textPageGetText(
0,
textCharCount,
)
?: ""
val wordBoundaries = findWordRanges(pageText)
val wordRangesArray =
wordBoundaries
.flatMap { listOf(it.first, it.second) }
.toIntArray()
val iterations = 100
val time =
measureTime {
repeat(iterations) {
val result = textPage.textPageGetRectsForRanges(wordRangesArray)
assertThat(result).isNotNull()
assertThat(result?.size).isEqualTo(1238)
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
}
}
}
@Test
fun gettextPageGetRects() {
val iterations = 100
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val textCharCount =
textPage.textPageCountChars()
if (textCharCount > 0) {
val pageText =
textPage.textPageGetText(
0,
textCharCount,
)
?: ""
val wordBoundaries = findWordRanges(pageText)
val time =
measureTime {
repeat(iterations) {
val list = mutableListOf<RectF>()
wordBoundaries.forEach {
val count = textPage.textPageCountRects(it.first, it.second)
repeat(count) {
textPage.textPageGetRect(it)?.let { rect ->
list.add(rect)
}
}
// println("list: ${it.first} ${it.second} $list")
}
assertThat(list).isNotNull()
assertThat(list.size).isEqualTo(1238)
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
}
}
}
@Test
fun getPagBitmapViaMatrixSingleOpen8xARGB_8888ReadFromDisk() {
val iterations = 100
val (bitmap, rect, matrix) = commonParams8X(Bitmap.Config.ARGB_8888)
pdfDocument.openPage(0).use { page ->
repeat(iterations) {
page.renderPageBitmap(
bitmap,
matrix,
rect,
)
}
}
val targetCtx: Context = InstrumentationRegistry.getInstrumentation().targetContext
targetCtx.openFileOutput("test.png", Context.MODE_PRIVATE).use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}
val bitmapOptios =
BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_8888
inJustDecodeBounds = false
inBitmap = bitmap
inSampleSize = 1
}
val time =
measureTime {
repeat(iterations) {
val bitmapFromDisk = BitmapFactory.decodeFile(targetCtx.filesDir.path + "/test.png", bitmapOptios)
assertThat(bitmapFromDisk).isNotNull()
}
}
val averageDuration = (time / iterations)
println("Total Time: $time, Average Time: $averageDuration")
}
private fun commonParams8X(bitmapConfig: Bitmap.Config): Triple<Bitmap, RectF, Matrix> {
val scaleFactor = (1080f / 612) * 8
val width = 1080 * 3
val height = 2280 * 3
val bitmap =
Bitmap.createBitmap(
width,
height,
bitmapConfig,
)
val rect = RectF(0f, 0f, width.toFloat(), height.toFloat())
val matrix = Matrix()
matrix.postScale(scaleFactor, scaleFactor)
return Triple(bitmap, rect, matrix)
}
private fun testTextPageAttributes(page: PdfTextPage) {
val textPageCountChars = page.textPageCountChars()
val textPageCountRects = page.textPageCountRects(0, textPageCountChars)
assertThat(textPageCountChars).isEqualTo(3468)
val textPageGetText = page.textPageGetText(0, textPageCountChars)
assertThat(textPageGetText).startsWith("The 50 Best Videos For Kids")
val textPageGetUnicode = page.textPageGetUnicode(0)
assertThat(textPageGetUnicode).isEqualTo('T')
val textPageGetCharBox = page.textPageGetCharBox(0)
assertThat(textPageGetCharBox).isEqualTo(RectF(90.314415f, 715.3187f, 103.44171f, 699.1206f))
repeat(textPageCountRects) {
val textPageGetRect = page.textPageGetRect(it)
}
}
private fun testPageAttributes(page: PdfPage) {
val pageWidth = page.getPageWidth(72)
val pageHeight = page.getPageHeight(72)
val pageWidthPoint = page.getPageWidthPoint()
val pageHeightPoint = page.getPageHeightPoint()
val cropBox = page.getPageCropBox()
val mediaBox = page.getPageMediaBox()
val bleedBox = page.getPageBleedBox()
val trimBox = page.getPageTrimBox()
val artBox = page.getPageArtBox()
val boundingBox = page.getPageBoundingBox()
val size = page.getPageSize(72)
val links = page.getPageLinks()
val devicePt = page.mapRectToDevice(0, 0, 100, 100, 0, RectF(0f, 0f, 100f, 100f))
assertThat(pageWidth).isEqualTo(612) // 8.5 inches * 72 dpi
assertThat(pageHeight).isEqualTo(792) // 11 inches * 72 dpi
assertThat(pageWidthPoint).isEqualTo(612) // 11 inches * 72 dpi
assertThat(pageHeightPoint).isEqualTo(792) // 11 inches * 72 dpi
assertThat(cropBox).isEqualTo(noResultRect)
assertThat(mediaBox).isEqualTo(RectF(0.0f, 0.0f, 612.0f, 792.0f))
assertThat(bleedBox).isEqualTo(noResultRect)
assertThat(trimBox).isEqualTo(noResultRect)
assertThat(artBox).isEqualTo(noResultRect)
assertThat(boundingBox).isEqualTo(RectF(0f, 792f, 612f, 0f))
assertThat(size).isEqualTo(Size(612, 792))
assertThat(links.size).isEqualTo(0) // The test doc doesn't have links
assertThat(devicePt).isEqualTo(
Rect(
// 0f in coords to 0f in device
0,
// 0f in corrds in at the bottom, the bottom of the device is 100f
100,
// 100f in coords = 100f/(8.5*72) * 100f = 16f
16,
// 100f in coords = 100 - 100f/(11*72) * 100f = 87f
87,
),
)
}
}

View file

@ -0,0 +1,104 @@
package io.legere.pdfiumandroid
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfDocumentTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocument
private var pdfBytes: ByteArray? = null
@Before
fun setUp() {
pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCore().newDocument(pdfBytes)
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun getPageCount() {
val pageCount = pdfDocument.getPageCount()
assertThat(pageCount).isEqualTo(4)
}
@Test
fun openPage() {
val page = pdfDocument.openPage(0)
assertThat(page).isNotNull()
}
@Test
fun openPages() {
val page = pdfDocument.openPages(0, 3)
assertThat(page.size).isEqualTo(4)
}
@Test
fun getDocumentMeta() {
val meta = pdfDocument.getDocumentMeta()
assertThat(meta).isNotNull()
}
@Test
fun getTableOfContents() {
// I don't think this test document has a table of contents
val toc = pdfDocument.getTableOfContents()
assertThat(toc).isNotNull()
assertThat(toc.size).isEqualTo(0)
}
@Test
fun openTextPage() {
val page = pdfDocument.openPage(0)
val textPage = page.openTextPage()
assertThat(textPage).isNotNull()
}
// @Test
// fun openTextPages() {
// val textPages = pdfDocument.openTextPages(0, 3)
// assertThat(textPages.size).isEqualTo(4)
// }
@Test
fun saveAsCopy() {
pdfDocument.saveAsCopy(
object : PdfWriteCallback {
override fun WriteBlock(data: ByteArray?): Int {
// assertThat(data?.size).isEqualTo(pdfBytes?.size)
// assertThat(data).isEqualTo(pdfBytes)
return data?.size ?: 0
}
},
)
}
@Test(expected = IllegalStateException::class)
fun closeDocument() {
var shouldBeClosed: PdfDocument?
PdfiumCore().newDocument(pdfBytes).use { pdfDocument ->
assertThat(pdfDocument).isNotNull()
shouldBeClosed = pdfDocument
}
// Now it should be closed
shouldBeClosed?.openPage(0) // This should throw an exception
}
}

View file

@ -0,0 +1,87 @@
package io.legere.pdfiumandroid
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfPageLinkTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocument
private lateinit var pdfPage: PdfPage
private lateinit var pdfTextPage: PdfTextPage
private var pdfBytes: ByteArray? = null
@Before
fun setUp() {
pdfBytes = getPdfBytes("pdf-test.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCore().newDocument(pdfBytes)
pdfPage = pdfDocument.openPage(0)
pdfTextPage = pdfPage.openTextPage()
}
@After
fun tearDown() {
pdfTextPage.close()
pdfPage.close()
pdfDocument.close()
}
@Test
fun testLink() {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
links.close()
}
@Test
fun testCountWebLinks() {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
assertThat(links.countWebLinks()).isEqualTo(1)
links.close()
}
@Test
fun testGetTextRange() {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
assertThat(links.getTextRange(0)).isEqualTo(Pair(351, 31))
links.close()
}
@Test
fun testGetUrl() {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
val (_, count) = links.getTextRange(0)
assertThat(links.getURL(0, count)).isEqualTo("http://www.education.gov.yk.ca/")
links.close()
}
@Test
fun testCountRects() {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
val count = links.countRects(0)
assertThat(count).isEqualTo(1)
links.close()
}
@Test
fun testGetRect() {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
val count = links.getRect(0, 0)
assertThat(count).isEqualTo(RectF(221.46f, 480.624f, 389.66394f, 469.152f))
links.close()
}
}

View file

@ -0,0 +1,232 @@
package io.legere.pdfiumandroid
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import io.legere.pdfiumandroid.util.Size
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfPageTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocument
private var pdfBytes: ByteArray? = null
@Before
fun setUp() {
pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCore().newDocument(pdfBytes)
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun getPageWidth() {
pdfDocument.openPage(0).use { page ->
val pageWidth = page.getPageWidth(72)
assertThat(pageWidth).isEqualTo(612) // 8.5 inches * 72 dpi
}
}
@Test
fun getPageHeight() {
pdfDocument.openPage(0).use { page ->
val pageHeight = page.getPageHeight(72)
assertThat(pageHeight).isEqualTo(792) // 11 inches * 72 dpi
}
}
@Test
fun getPageWidthPoint() {
pdfDocument.openPage(0).use { page ->
val pageWidthPoint = page.getPageWidthPoint()
assertThat(pageWidthPoint).isEqualTo(612) // 11 inches * 72 dpi
}
}
@Test
fun getPageHeightPoint() {
pdfDocument.openPage(0).use { page ->
val pageHeightPoint = page.getPageHeightPoint()
assertThat(pageHeightPoint).isEqualTo(792) // 11 inches * 72 dpi
}
}
@Test
fun getPageCropBox() {
pdfDocument.openPage(0).use { page ->
val cropBox = page.getPageCropBox()
assertThat(cropBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageMediaBox() {
pdfDocument.openPage(0).use { page ->
val mediaBox = page.getPageMediaBox()
assertThat(mediaBox).isEqualTo(RectF(0.0f, 0.0f, 612.0f, 792.0f))
}
}
@Test
fun getPageBleedBox() {
pdfDocument.openPage(0).use { page ->
val bleedBox = page.getPageBleedBox()
assertThat(bleedBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageTrimBox() {
pdfDocument.openPage(0).use { page ->
val trimBox = page.getPageTrimBox()
assertThat(trimBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageArtBox() {
pdfDocument.openPage(0).use { page ->
val artBox = page.getPageArtBox()
assertThat(artBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageBoundingBox() {
pdfDocument.openPage(0).use { page ->
val boundingBox = page.getPageBoundingBox()
// Note, that looks incorrect, but pdfs coordinate systems starts from bottom left corner
assertThat(boundingBox).isEqualTo(RectF(0f, 792f, 612f, 0f))
}
}
@Test
fun getPageSize() {
pdfDocument.openPage(0).use { page ->
val size = page.getPageSize(72)
assertThat(size).isEqualTo(Size(612, 792))
}
}
@Test
fun renderPageBitmap() {
pdfDocument.openPage(0).use { page ->
val conf = Bitmap.Config.RGB_565 // see other conf types
val bmp = Bitmap.createBitmap(612, 792, conf) // this creates a MUTABLE bitmap
page.renderPageBitmap(bmp, 0, 0, 612, 792, true)
// How to verify that it's correct?
// Even if we don't verify the bitmap, we can check that it doesn't crash
}
}
@Test
fun testRenderPageBitmap() {
pdfDocument.openPage(0).use { page ->
val conf = Bitmap.Config.RGB_565 // see other conf types
val bmp = Bitmap.createBitmap(612, 792, conf) // this creates a MUTABLE bitmap
page.renderPageBitmap(bmp, 0, 0, 612, 792, renderAnnot = true, textMask = true)
// How to verify that it's correct?
// Even if we don't verify the bitmap, we can check that it doesn't crash
}
}
@Test
fun getPageLinks() {
pdfDocument.openPage(0).use { page ->
val links = page.getPageLinks()
assertThat(links.size).isEqualTo(0) // The test doc doesn't have links
}
}
@Test
fun mapPageCoordsToDevice() {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapPageCoordsToDevice(0, 0, 100, 100, 0, 0.0, 0.0)
assertThat(devicePt).isEqualTo(Point(0, 100))
}
}
@Test
fun mapDeviceCoordsToPage() {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapDeviceCoordsToPage(0, 0, 100, 100, 0, 0, 0)
assertThat(devicePt).isEqualTo(PointF(0f, 792.00006f))
}
}
@Test
fun mapRectToDevice() {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapRectToDevice(0, 0, 100, 100, 0, RectF(0f, 0f, 100f, 100f))
assertThat(devicePt).isEqualTo(
Rect(
// 0f in coords to 0f in device
0,
// 0f in corrds in at the bottom, the bottom of the device is 100f
100,
// 100f in coords = 100f/(8.5*72) * 100f = 16f
16,
// 100f in coords = 100 - 100f/(11*72) * 100f = 87f
87,
),
)
}
}
@Test
fun mapRectToPage() {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapRectToPage(0, 0, 100, 100, 0, Rect(0, 0, 100, 100))
assertThat(devicePt).isEqualTo(
RectF(0.0f, 792.00006f, 612.0f, 0.0f),
)
}
}
@Test(expected = IllegalStateException::class)
fun close() {
var pageAfterClose: PdfPage?
pdfDocument.openPage(0).use { page ->
pageAfterClose = page
}
pageAfterClose!!.getPageWidth(72)
}
}

View file

@ -0,0 +1,207 @@
package io.legere.pdfiumandroid
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfTextPageTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocument
private var pdfBytes: ByteArray? = null
@Before
fun setUp() {
pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
pdfDocument = PdfiumCore().newDocument(pdfBytes)
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun textPageCountChars() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val charCount = textPage.textPageCountChars()
assertThat(charCount).isEqualTo(3468)
}
}
}
@Test
fun textPageGetText() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val text = textPage.textPageGetText(0, 100)
assertThat(text?.length).isEqualTo(100)
}
}
}
@Test
fun textPageGetUnicode() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val char = textPage.textPageGetUnicode(0)
assertThat(char).isEqualTo('T')
}
}
}
@Test
fun textPageGetCharBox() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val rect = textPage.textPageGetCharBox(0)
assertThat(rect).isEqualTo(RectF(90.314415f, 715.3187f, 103.44171f, 699.1206f))
}
}
}
@Test
fun textPageGetCharIndexAtPos() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val characterToLookup = 0
val rect = textPage.textPageGetCharBox(characterToLookup)
val pos =
textPage.textPageGetCharIndexAtPos(
rect?.centerX()?.toDouble() ?: 0.0,
rect?.centerY()?.toDouble() ?: 0.0,
// Shouldn't need much since we're in the middle of the rect
1.0,
1.0,
)
assertThat(pos).isEqualTo(characterToLookup)
}
}
}
@Test
fun textPageCountRects() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val rectCount = textPage.textPageCountRects(0, 100)
assertThat(rectCount).isEqualTo(4)
}
}
}
@Test
fun textPageGetRect() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val rect = textPage.textPageGetRect(0)
assertThat(rect).isEqualTo(RectF(0f, 0f, 0f, 0f))
}
}
}
@Test
fun textPageGetBoundedText() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val text = textPage.textPageGetBoundedText(RectF(0f, 97f, 100f, 100f), 100)
assertThat(text).isEqualTo("Do")
}
}
}
@Test
fun getFontSize() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val fontSize = textPage.getFontSize(0)
// We get 0, but that doesn't seem right
assertThat(fontSize).isEqualTo(22.559999465942383)
}
}
}
@Test
fun findStart() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val findWhat = "children's"
val startIndex = 0
textPage.findStart(findWhat, emptySet(), startIndex)?.use { findHandle ->
var result = findHandle.findNext()
assertThat(result).isTrue()
var index = findHandle.getSchResultIndex()
var count = findHandle.getSchCount()
var text = textPage.textPageGetText(index, count)
assertThat(index).isEqualTo(1525)
assertThat(count).isEqualTo(10)
assertThat(text).isEqualTo(findWhat)
result = findHandle.findNext()
assertThat(result).isTrue()
index = findHandle.getSchResultIndex()
count = findHandle.getSchCount()
text = textPage.textPageGetText(index, count)
assertThat(index).isEqualTo(2761)
assertThat(count).isEqualTo(10)
assertThat(text).isEqualTo(findWhat)
result = findHandle.findNext()
assertThat(result).isFalse()
}
}
}
}
@Test(expected = IllegalStateException::class)
fun close() {
var pageAfterClose: PdfTextPage?
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
pageAfterClose = textPage
}
pageAfterClose!!.textPageCountChars()
}
}
@Test
fun getDoc() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
assertThat(textPage.doc).isNotNull()
}
}
}
@Test
fun getPageIndex() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
assertThat(textPage.pageIndex).isEqualTo(0)
}
}
}
@Test
fun getPagePtr() {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
assertThat(textPage.pagePtr).isNotNull()
}
}
}
}

View file

@ -0,0 +1,35 @@
package io.legere.pdfiumandroid
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import io.legere.pdfiumandroid.base.ByteArrayPdfiumSource
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfiumCoreTest : BasePDFTest() {
@Test
fun newDocument() {
val pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
val pdfiumCore = PdfiumCore()
val pdfDocument = pdfiumCore.newDocument(pdfBytes)
assertThat(pdfDocument).isNotNull()
}
@Test
fun newDocumentWithCustomSource() {
val pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
val pdfiumCore = PdfiumCore()
val pdfDocument = pdfiumCore.newDocument(ByteArrayPdfiumSource(pdfBytes!!))
assertThat(pdfDocument).isNotNull()
}
}

View file

@ -0,0 +1,28 @@
package io.legere.pdfiumandroid.base
import android.graphics.RectF
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import io.legere.pdfiumandroid.PdfiumCoreTest
@Suppress("unused")
open class BasePDFTest {
// set to true to skip tests that are not implemented yet
// set to false to force unimplemented tests to fail
val notImplementedAssetValue = false
val noResultRect = RectF(-1f, -1f, -1f, -1f)
fun getPdfBytes(filename: String): ByteArray? {
val appContext = InstrumentationRegistry.getInstrumentation().context
val assetManager = appContext.assets
try {
val input = assetManager.open(filename)
return input.readBytes()
} catch (e: Exception) {
Log.e(PdfiumCoreTest::class.simpleName, "Ugh", e)
}
assetManager.close()
return null
}
}

View file

@ -0,0 +1,28 @@
package io.legere.pdfiumandroid.base
import io.legere.pdfiumandroid.PdfiumSource
class ByteArrayPdfiumSource(
private val array: ByteArray,
) : PdfiumSource {
override val length: Long
get() = array.size.toLong()
override fun read(
position: Long,
buffer: ByteArray,
size: Int,
): Int {
array.copyInto(
destination = buffer,
destinationOffset = 0,
startIndex = position.toInt(),
endIndex = position.toInt() + size,
)
return size
}
override fun close() {
// nothing to close
}
}

View file

@ -0,0 +1,117 @@
package io.legere.pdfiumandroid.suspend
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth
import io.legere.pdfiumandroid.PdfWriteCallback
import io.legere.pdfiumandroid.base.BasePDFTest
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfDocumentKtTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKt
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("f01.pdf")
TestCase.assertNotNull(pdfBytes)
pdfDocument = PdfiumCoreKt(Dispatchers.Unconfined).newDocument(pdfBytes)
}
@After
fun tearDown() =
runTest {
pdfDocument.close()
}
@Test
fun getPageCount() =
runTest {
val pageCount = pdfDocument.getPageCount()
assert(pageCount == 4) { "Page count should be 4" }
}
@Test
fun openPage() =
runTest {
val page = pdfDocument.openPage(0)
TestCase.assertNotNull(page)
}
@Test
fun openPages() =
runTest {
val page = pdfDocument.openPages(0, 3)
assert(page.size == 4) { "Page count should be 4" }
}
@Test
fun getDocumentMeta() =
runTest {
val meta = pdfDocument.getDocumentMeta()
TestCase.assertNotNull(meta)
}
@Test
fun getTableOfContents() =
runTest {
// I don't think this test document has a table of contents
val toc = pdfDocument.getTableOfContents()
TestCase.assertNotNull(toc)
Truth.assertThat(toc.size).isEqualTo(0)
}
@Test
fun openTextPage() =
runTest {
val page = pdfDocument.openPage(0)
val textPage = page.openTextPage()
TestCase.assertNotNull(textPage)
}
@Test
fun openTextPages() =
runTest {
val textPages = pdfDocument.openTextPages(0, 3)
Truth.assertThat(textPages.size).isEqualTo(4)
}
@Test
fun saveAsCopy() =
runTest {
pdfDocument.saveAsCopy(
object : PdfWriteCallback {
override fun WriteBlock(data: ByteArray?): Int {
// Truth.assertThat(data?.size).isEqualTo(pdfBytes?.size)
// Truth.assertThat(data).isEqualTo(pdfBytes)
return data?.size ?: 0
}
},
)
}
@Test(expected = IllegalStateException::class)
fun close() =
runTest {
var documentAfterClose: PdfDocumentKt?
PdfiumCoreKt(Dispatchers.Unconfined).newDocument(pdfBytes).use {
documentAfterClose = it
}
documentAfterClose?.openPage(0)
}
}

View file

@ -0,0 +1,275 @@
package io.legere.pdfiumandroid.suspend
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import io.legere.pdfiumandroid.util.Size
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfPageKtTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKt
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("f01.pdf")
TestCase.assertNotNull(pdfBytes)
pdfDocument = PdfiumCoreKt(Dispatchers.Unconfined).newDocument(pdfBytes)
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun getPageWidth() =
runTest {
pdfDocument.openPage(0).use { page ->
val pageWidth = page.getPageWidth(72)
assertThat(pageWidth).isEqualTo(612) // 8.5 inches * 72 dpi
}
}
@Test
fun getPageHeight() =
runTest {
pdfDocument.openPage(0).use { page ->
val pageWidth = page.getPageHeight(72)
assertThat(pageWidth).isEqualTo(792) // 11 inches * 72 dpi
}
}
@Test
fun getPageWidthPoint() =
runTest {
pdfDocument.openPage(0).use { page ->
val pageWidth = page.getPageWidthPoint()
assertThat(pageWidth).isEqualTo(612) // 11 inches * 72 dpi
}
}
@Test
fun getPageHeightPoint() =
runTest {
pdfDocument.openPage(0).use { page ->
val pageWidth = page.getPageHeightPoint()
assertThat(pageWidth).isEqualTo(792) // 11 inches * 72 dpi
}
}
@Test
fun getPageCropBox() =
runTest {
pdfDocument.openPage(0).use { page ->
val cropBox = page.getPageCropBox()
assertThat(cropBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageMediaBox() =
runTest {
pdfDocument.openPage(0).use { page ->
val mediaBox = page.getPageMediaBox()
assertThat(mediaBox).isEqualTo(RectF(0.0f, 0.0f, 612.0f, 792.0f))
}
}
@Test
fun getPageBleedBox() =
runTest {
pdfDocument.openPage(0).use { page ->
val bleedBox = page.getPageBleedBox()
assertThat(bleedBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageTrimBox() =
runTest {
pdfDocument.openPage(0).use { page ->
val trimBox = page.getPageTrimBox()
assertThat(trimBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageArtBox() =
runTest {
pdfDocument.openPage(0).use { page ->
val artBox = page.getPageArtBox()
assertThat(artBox).isEqualTo(noResultRect)
}
}
@Test
fun getPageBoundingBox() =
runTest {
pdfDocument.openPage(0).use { page ->
val artBox = page.getPageBoundingBox()
assertThat(artBox).isEqualTo(RectF(0f, 792f, 612f, 0f))
}
}
@Test
fun getPageSize() =
runTest {
pdfDocument.openPage(0).use { page ->
val size = page.getPageSize(72)
assertThat(size).isEqualTo(Size(612, 792))
}
}
@Test
fun renderPage() =
runTest {
// I really don't know how to test it
}
@Test
fun testRenderPage() =
runTest {
// I really don't know how to test it
}
@Test
fun renderPageBitmap() =
runTest {
pdfDocument.openPage(0).use { page ->
val conf = Bitmap.Config.RGB_565 // see other conf types
val bmp = Bitmap.createBitmap(612, 792, conf) // this creates a MUTABLE bitmap
page.renderPageBitmap(bmp, 0, 0, 612, 792)
// How to verify that it's correct?
// Even if we don't verify the bitmap, we can check that it doesn't crash
}
}
@Test
fun testRenderPageBitmap() =
runTest {
pdfDocument.openPage(0).use { page ->
val conf = Bitmap.Config.RGB_565 // see other conf types
val bmp = Bitmap.createBitmap(612, 792, conf) // this creates a MUTABLE bitmap
page.renderPageBitmap(bmp, 0, 0, 612, 792, renderAnnot = true, textMask = true)
// How to verify that it's correct?
// Even if we don't verify the bitmap, we can check that it doesn't crash
}
}
@Test
fun getPageLinks() =
runTest {
pdfDocument.openPage(0).use { page ->
val links = page.getPageLinks()
assertThat(links.size).isEqualTo(0) // The test doc doesn't have links
}
}
@Test
fun mapPageCoordsToDevice() =
runTest {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapPageCoordsToDevice(0, 0, 100, 100, 0, 0.0, 0.0)
assertThat(devicePt).isEqualTo(Point(0, 100))
}
}
@Test
fun mapDeviceCoordsToPage() =
runTest {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapDeviceCoordsToPage(0, 0, 100, 100, 0, 0, 0)
assertThat(devicePt).isEqualTo(PointF(0f, 792.00006f))
}
}
@Test
fun mapRectToDevice() =
runTest {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapRectToDevice(0, 0, 100, 100, 0, RectF(0f, 0f, 100f, 100f))
assertThat(devicePt).isEqualTo(
Rect(
// 0f in coords to 0f in device
0,
// 0f in corrds in at the bottom, the bottom of the device is 100f
100,
// 100f in coords = 100f/(8.5*72) * 100f = 16f
16,
// 100f in coords = 100 - 100f/(11*72) * 100f = 87f
87,
),
)
}
}
@Test
fun mapRectToPage() =
runTest {
pdfDocument.openPage(0).use { page ->
val devicePt = page.mapRectToPage(0, 0, 100, 100, 0, Rect(0, 0, 100, 100))
assertThat(devicePt).isEqualTo(
RectF(0.0f, 792.00006f, 612.0f, 0.0f),
)
}
}
@Test(expected = IllegalStateException::class)
fun close() =
runTest {
var pageAfterClose: PdfPageKt?
pdfDocument.openPage(0).use { page ->
pageAfterClose = page
}
pageAfterClose!!.getPageWidth(72)
}
@Test
fun getPage() =
runTest {
pdfDocument.openPage(0).use { page ->
assertThat(page).isNotNull()
}
}
}

View file

@ -0,0 +1,99 @@
package io.legere.pdfiumandroid.suspend
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfPageLinkKtTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKt
private lateinit var pdfPage: PdfPageKt
private lateinit var pdfTextPage: PdfTextPageKt
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("pdf-test.pdf")
TestCase.assertNotNull(pdfBytes)
pdfDocument = PdfiumCoreKt(Dispatchers.Unconfined).newDocument(pdfBytes)
pdfPage = pdfDocument.openPage(0)
pdfTextPage = pdfPage.openTextPage()
}
@After
fun tearDown() {
pdfTextPage.close()
pdfPage.close()
pdfDocument.close()
}
@Test
fun testLink(): TestResult =
runTest {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
links.close()
}
@Test
fun testCountWebLinks(): TestResult =
runTest {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
assertThat(links.countWebLinks()).isEqualTo(1)
links.close()
}
@Test
fun testGetTextRange(): TestResult =
runTest {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
assertThat(links.getTextRange(0)).isEqualTo(Pair(351, 31))
links.close()
}
@Test
fun testGetUrl(): TestResult =
runTest {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
val (_, count) = links.getTextRange(0)
assertThat(links.getURL(0, count)).isEqualTo("http://www.education.gov.yk.ca/")
links.close()
}
@Test
fun testCountRects(): TestResult =
runTest {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
val count = links.countRects(0)
assertThat(count).isEqualTo(1)
links.close()
}
@Test
fun testGetRect(): TestResult =
runTest {
val links = pdfTextPage.loadWebLink()
assertThat(links).isNotNull()
val count = links.getRect(0, 0)
assertThat(count).isEqualTo(RectF(221.46f, 480.624f, 389.66394f, 469.152f))
links.close()
}
}

View file

@ -0,0 +1,178 @@
package io.legere.pdfiumandroid.suspend
import android.graphics.RectF
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth
import io.legere.pdfiumandroid.base.BasePDFTest
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfTextPageKtTest : BasePDFTest() {
private lateinit var pdfDocument: PdfDocumentKt
private var pdfBytes: ByteArray? = null
@Before
fun setUp() =
runBlocking {
pdfBytes = getPdfBytes("f01.pdf")
TestCase.assertNotNull(pdfBytes)
pdfDocument = PdfiumCoreKt(Dispatchers.Unconfined).newDocument(pdfBytes)
}
@After
fun tearDown() {
pdfDocument.close()
}
@Test
fun textPageCountChars() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val charCount = textPage.textPageCountChars()
Truth.assertThat(charCount).isEqualTo(3468)
}
}
}
@Test
fun textPageGetText() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val text = textPage.textPageGetText(0, 100)
Truth.assertThat(text?.length).isEqualTo(100)
}
}
}
@Test
fun textPageGetUnicode() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val char = textPage.textPageGetUnicode(0)
Truth.assertThat(char).isEqualTo('T')
}
}
}
@Test
fun textPageGetCharBox() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val rect = textPage.textPageGetCharBox(0)
Truth
.assertThat(rect)
.isEqualTo(RectF(90.314415f, 715.3187f, 103.44171f, 699.1206f))
}
}
}
@Test
fun textPageGetCharIndexAtPos() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val characterToLookup = 0
val rect = textPage.textPageGetCharBox(characterToLookup)
val pos =
textPage.textPageGetCharIndexAtPos(
rect?.centerX()?.toDouble() ?: 0.0,
rect?.centerY()?.toDouble() ?: 0.0,
// Shouldn't need much since we're in the middle of the rect
1.0,
1.0,
)
Truth.assertThat(pos).isEqualTo(characterToLookup)
}
}
}
@Test
fun textPageCountRects() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val rectCount = textPage.textPageCountRects(0, 100)
Truth.assertThat(rectCount).isEqualTo(4)
}
}
}
@Test
fun textPageGetRect() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val rect = textPage.textPageGetRect(0)
Truth.assertThat(rect).isEqualTo(RectF(0f, 0f, 0f, 0f))
}
}
}
@Test
fun textPageGetBoundedText() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val text = textPage.textPageGetBoundedText(RectF(0f, 97f, 100f, 100f), 100)
Truth.assertThat(text).isEqualTo("Do")
}
}
}
@Test
fun getFontSize() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
val fontSize = textPage.getFontSize(0)
Truth.assertThat(fontSize).isEqualTo(22.559999465942383)
}
}
}
@Test(expected = IllegalStateException::class)
fun close() =
runTest {
var pageAfterClose: PdfTextPageKt?
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
pageAfterClose = textPage
}
}
pageAfterClose!!.textPageCountChars()
}
@Test
fun getPage() =
runTest {
pdfDocument.openPage(0).use { page ->
page.openTextPage().use { textPage ->
Truth.assertThat(textPage.page).isNotNull()
}
}
}
}

View file

@ -0,0 +1,39 @@
package io.legere.pdfiumandroid.suspend
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.base.BasePDFTest
import io.legere.pdfiumandroid.base.ByteArrayPdfiumSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PdfiumCoreKtTest : BasePDFTest() {
@Test
fun newDocument() =
runTest {
val pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
val pdfiumCore = PdfiumCoreKt(Dispatchers.Unconfined)
val pdfDocument = pdfiumCore.newDocument(pdfBytes)
assertThat(pdfDocument).isNotNull()
}
@Test
fun newDocumentWitCustomSource() =
runTest {
val pdfBytes = getPdfBytes("f01.pdf")
assertThat(pdfBytes).isNotNull()
val pdfiumCore = PdfiumCoreKt(Dispatchers.Unconfined)
val pdfDocument = pdfiumCore.newDocument(ByteArrayPdfiumSource(pdfBytes!!))
assertThat(pdfDocument).isNotNull()
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View file

@ -0,0 +1,72 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.22.1)
# Declares and names the project.
project("pdfiumandroid")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
pdfiumandroid
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
pdfiumandroid.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
find_library( # Sets the name of the path variable.
android-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
android)
find_library( # Sets the name of the path variable.
jnigraphics-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
jnigraphics)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
pdfiumandroid
libpdfium
# Links the target library to the log library
# included in the NDK.
${log-lib}
${android-lib}
${jnigraphics-lib}
)
add_library( libpdfium SHARED IMPORTED )
set_target_properties( libpdfium PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libpdfium.so)

View file

@ -0,0 +1,86 @@
// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_CPP_FPDF_DELETERS_H_
#define PUBLIC_CPP_FPDF_DELETERS_H_
#include "../fpdf_annot.h"
#include "../fpdf_dataavail.h"
#include "../fpdf_edit.h"
#include "../fpdf_formfill.h"
#include "../fpdf_javascript.h"
#include "../fpdf_structtree.h"
#include "../fpdf_text.h"
#include "../fpdf_transformpage.h"
#include "../fpdfview.h"
// Custom deleters for using FPDF_* types with std::unique_ptr<>.
struct FPDFAnnotationDeleter {
inline void operator()(FPDF_ANNOTATION annot) { FPDFPage_CloseAnnot(annot); }
};
struct FPDFAvailDeleter {
inline void operator()(FPDF_AVAIL avail) { FPDFAvail_Destroy(avail); }
};
struct FPDFBitmapDeleter {
inline void operator()(FPDF_BITMAP bitmap) { FPDFBitmap_Destroy(bitmap); }
};
struct FPDFClipPathDeleter {
inline void operator()(FPDF_CLIPPATH clip_path) {
FPDF_DestroyClipPath(clip_path);
}
};
struct FPDFDocumentDeleter {
inline void operator()(FPDF_DOCUMENT doc) { FPDF_CloseDocument(doc); }
};
struct FPDFFontDeleter {
inline void operator()(FPDF_FONT font) { FPDFFont_Close(font); }
};
struct FPDFFormHandleDeleter {
inline void operator()(FPDF_FORMHANDLE form) {
FPDFDOC_ExitFormFillEnvironment(form);
}
};
struct FPDFJavaScriptActionDeleter {
inline void operator()(FPDF_JAVASCRIPT_ACTION javascript) {
FPDFDoc_CloseJavaScriptAction(javascript);
}
};
struct FPDFPageDeleter {
inline void operator()(FPDF_PAGE page) { FPDF_ClosePage(page); }
};
struct FPDFPageLinkDeleter {
inline void operator()(FPDF_PAGELINK pagelink) {
FPDFLink_CloseWebLinks(pagelink);
}
};
struct FPDFPageObjectDeleter {
inline void operator()(FPDF_PAGEOBJECT object) {
FPDFPageObj_Destroy(object);
}
};
struct FPDFStructTreeDeleter {
inline void operator()(FPDF_STRUCTTREE tree) { FPDF_StructTree_Close(tree); }
};
struct FPDFTextFindDeleter {
inline void operator()(FPDF_SCHHANDLE handle) { FPDFText_FindClose(handle); }
};
struct FPDFTextPageDeleter {
inline void operator()(FPDF_TEXTPAGE text) { FPDFText_ClosePage(text); }
};
#endif // PUBLIC_CPP_FPDF_DELETERS_H_

View file

@ -0,0 +1,67 @@
// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_CPP_FPDF_SCOPERS_H_
#define PUBLIC_CPP_FPDF_SCOPERS_H_
#include <memory>
#include <type_traits>
#include "fpdf_deleters.h"
// Versions of FPDF types that clean up the object at scope exit.
using ScopedFPDFAnnotation =
std::unique_ptr<std::remove_pointer<FPDF_ANNOTATION>::type,
FPDFAnnotationDeleter>;
using ScopedFPDFAvail =
std::unique_ptr<std::remove_pointer<FPDF_AVAIL>::type, FPDFAvailDeleter>;
using ScopedFPDFBitmap =
std::unique_ptr<std::remove_pointer<FPDF_BITMAP>::type, FPDFBitmapDeleter>;
using ScopedFPDFClipPath =
std::unique_ptr<std::remove_pointer<FPDF_CLIPPATH>::type,
FPDFClipPathDeleter>;
using ScopedFPDFDocument =
std::unique_ptr<std::remove_pointer<FPDF_DOCUMENT>::type,
FPDFDocumentDeleter>;
using ScopedFPDFFont =
std::unique_ptr<std::remove_pointer<FPDF_FONT>::type, FPDFFontDeleter>;
using ScopedFPDFFormHandle =
std::unique_ptr<std::remove_pointer<FPDF_FORMHANDLE>::type,
FPDFFormHandleDeleter>;
using ScopedFPDFJavaScriptAction =
std::unique_ptr<std::remove_pointer<FPDF_JAVASCRIPT_ACTION>::type,
FPDFJavaScriptActionDeleter>;
using ScopedFPDFPage =
std::unique_ptr<std::remove_pointer<FPDF_PAGE>::type, FPDFPageDeleter>;
using ScopedFPDFPageLink =
std::unique_ptr<std::remove_pointer<FPDF_PAGELINK>::type,
FPDFPageLinkDeleter>;
using ScopedFPDFPageObject =
std::unique_ptr<std::remove_pointer<FPDF_PAGEOBJECT>::type,
FPDFPageObjectDeleter>;
using ScopedFPDFStructTree =
std::unique_ptr<std::remove_pointer<FPDF_STRUCTTREE>::type,
FPDFStructTreeDeleter>;
using ScopedFPDFTextFind =
std::unique_ptr<std::remove_pointer<FPDF_SCHHANDLE>::type,
FPDFTextFindDeleter>;
using ScopedFPDFTextPage =
std::unique_ptr<std::remove_pointer<FPDF_TEXTPAGE>::type,
FPDFTextPageDeleter>;
#endif // PUBLIC_CPP_FPDF_SCOPERS_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,179 @@
// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_FPDF_ATTACHMENT_H_
#define PUBLIC_FPDF_ATTACHMENT_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Experimental API.
// Get the number of embedded files in |document|.
//
// document - handle to a document.
//
// Returns the number of embedded files in |document|.
FPDF_EXPORT int FPDF_CALLCONV
FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document);
// Experimental API.
// Add an embedded file with |name| in |document|. If |name| is empty, or if
// |name| is the name of a existing embedded file in |document|, or if
// |document|'s embedded file name tree is too deep (i.e. |document| has too
// many embedded files already), then a new attachment will not be added.
//
// document - handle to a document.
// name - name of the new attachment.
//
// Returns a handle to the new attachment object, or NULL on failure.
FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name);
// Experimental API.
// Get the embedded attachment at |index| in |document|. Note that the returned
// attachment handle is only valid while |document| is open.
//
// document - handle to a document.
// index - the index of the requested embedded file.
//
// Returns the handle to the attachment object, or NULL on failure.
FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index);
// Experimental API.
// Delete the embedded attachment at |index| in |document|. Note that this does
// not remove the attachment data from the PDF file; it simply removes the
// file's entry in the embedded files name tree so that it does not appear in
// the attachment list. This behavior may change in the future.
//
// document - handle to a document.
// index - the index of the embedded file to be deleted.
//
// Returns true if successful.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index);
// Experimental API.
// Get the name of the |attachment| file. |buffer| is only modified if |buflen|
// is longer than the length of the file name. On errors, |buffer| is unmodified
// and the returned length is 0.
//
// attachment - handle to an attachment.
// buffer - buffer for holding the file name, encoded in UTF-16LE.
// buflen - length of the buffer in bytes.
//
// Returns the length of the file name in bytes.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
FPDF_WCHAR* buffer,
unsigned long buflen);
// Experimental API.
// Check if the params dictionary of |attachment| has |key| as a key.
//
// attachment - handle to an attachment.
// key - the key to look for, encoded in UTF-8.
//
// Returns true if |key| exists.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key);
// Experimental API.
// Get the type of the value corresponding to |key| in the params dictionary of
// the embedded |attachment|.
//
// attachment - handle to an attachment.
// key - the key to look for, encoded in UTF-8.
//
// Returns the type of the dictionary value.
FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key);
// Experimental API.
// Set the string value corresponding to |key| in the params dictionary of the
// embedded file |attachment|, overwriting the existing value if any. The value
// type should be FPDF_OBJECT_STRING after this function call succeeds.
//
// attachment - handle to an attachment.
// key - the key to the dictionary entry, encoded in UTF-8.
// value - the string value to be set, encoded in UTF-16LE.
//
// Returns true if successful.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,
FPDF_BYTESTRING key,
FPDF_WIDESTRING value);
// Experimental API.
// Get the string value corresponding to |key| in the params dictionary of the
// embedded file |attachment|. |buffer| is only modified if |buflen| is longer
// than the length of the string value. Note that if |key| does not exist in the
// dictionary or if |key|'s corresponding value in the dictionary is not a
// string (i.e. the value is not of type FPDF_OBJECT_STRING or
// FPDF_OBJECT_NAME), then an empty string would be copied to |buffer| and the
// return value would be 2. On other errors, nothing would be added to |buffer|
// and the return value would be 0.
//
// attachment - handle to an attachment.
// key - the key to the requested string value, encoded in UTF-8.
// buffer - buffer for holding the string value encoded in UTF-16LE.
// buflen - length of the buffer in bytes.
//
// Returns the length of the dictionary value string in bytes.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
FPDF_BYTESTRING key,
FPDF_WCHAR* buffer,
unsigned long buflen);
// Experimental API.
// Set the file data of |attachment|, overwriting the existing file data if any.
// The creation date and checksum will be updated, while all other dictionary
// entries will be deleted. Note that only contents with |len| smaller than
// INT_MAX is supported.
//
// attachment - handle to an attachment.
// contents - buffer holding the file data to write to |attachment|.
// len - length of file data in bytes.
//
// Returns true if successful.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
FPDF_DOCUMENT document,
const void* contents,
unsigned long len);
// Experimental API.
// Get the file data of |attachment|.
// When the attachment file data is readable, true is returned, and |out_buflen|
// is updated to indicate the file data size. |buffer| is only modified if
// |buflen| is non-null and long enough to contain the entire file data. Callers
// must check both the return value and the input |buflen| is no less than the
// returned |out_buflen| before using the data.
//
// Otherwise, when the attachment file data is unreadable or when |out_buflen|
// is null, false is returned and |buffer| and |out_buflen| remain unmodified.
//
// attachment - handle to an attachment.
// buffer - buffer for holding the file data from |attachment|.
// buflen - length of the buffer in bytes.
// out_buflen - pointer to the variable that will receive the minimum buffer
// size to contain the file data of |attachment|.
//
// Returns true on success, false otherwise.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_ATTACHMENT_H_

View file

@ -0,0 +1,42 @@
// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_FPDF_CATALOG_H_
#define PUBLIC_FPDF_CATALOG_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Experimental API.
//
// Determine if |document| represents a tagged PDF.
//
// For the definition of tagged PDF, See (see 10.7 "Tagged PDF" in PDF
// Reference 1.7).
//
// document - handle to a document.
//
// Returns |true| iff |document| is a tagged PDF.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFCatalog_IsTagged(FPDF_DOCUMENT document);
// Experimental API.
// Sets the language of |document| to |language|.
//
// document - handle to a document.
// language - the language to set to.
//
// Returns TRUE on success.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFCatalog_SetLanguage(FPDF_DOCUMENT document, FPDF_BYTESTRING language);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_CATALOG_H_

View file

@ -0,0 +1,204 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_DATAAVAIL_H_
#define PUBLIC_FPDF_DATAAVAIL_H_
#include <stddef.h>
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#define PDF_LINEARIZATION_UNKNOWN -1
#define PDF_NOT_LINEARIZED 0
#define PDF_LINEARIZED 1
#define PDF_DATA_ERROR -1
#define PDF_DATA_NOTAVAIL 0
#define PDF_DATA_AVAIL 1
#define PDF_FORM_ERROR -1
#define PDF_FORM_NOTAVAIL 0
#define PDF_FORM_AVAIL 1
#define PDF_FORM_NOTEXIST 2
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Interface for checking whether sections of the file are available.
typedef struct _FX_FILEAVAIL {
// Version number of the interface. Must be 1.
int version;
// Reports if the specified data section is currently available. A section is
// available if all bytes in the section are available.
//
// Interface Version: 1
// Implementation Required: Yes
//
// pThis - pointer to the interface structure.
// offset - the offset of the data section in the file.
// size - the size of the data section.
//
// Returns true if the specified data section at |offset| of |size|
// is available.
FPDF_BOOL (*IsDataAvail)(struct _FX_FILEAVAIL* pThis,
size_t offset,
size_t size);
} FX_FILEAVAIL;
// Create a document availability provider.
//
// file_avail - pointer to file availability interface.
// file - pointer to a file access interface.
//
// Returns a handle to the document availability provider, or NULL on error.
//
// FPDFAvail_Destroy() must be called when done with the availability provider.
FPDF_EXPORT FPDF_AVAIL FPDF_CALLCONV FPDFAvail_Create(FX_FILEAVAIL* file_avail,
FPDF_FILEACCESS* file);
// Destroy the |avail| document availability provider.
//
// avail - handle to document availability provider to be destroyed.
FPDF_EXPORT void FPDF_CALLCONV FPDFAvail_Destroy(FPDF_AVAIL avail);
// Download hints interface. Used to receive hints for further downloading.
typedef struct _FX_DOWNLOADHINTS {
// Version number of the interface. Must be 1.
int version;
// Add a section to be downloaded.
//
// Interface Version: 1
// Implementation Required: Yes
//
// pThis - pointer to the interface structure.
// offset - the offset of the hint reported to be downloaded.
// size - the size of the hint reported to be downloaded.
//
// The |offset| and |size| of the section may not be unique. Part of the
// section might be already available. The download manager must deal with
// overlapping sections.
void (*AddSegment)(struct _FX_DOWNLOADHINTS* pThis,
size_t offset,
size_t size);
} FX_DOWNLOADHINTS;
// Checks if the document is ready for loading, if not, gets download hints.
//
// avail - handle to document availability provider.
// hints - pointer to a download hints interface.
//
// Returns one of:
// PDF_DATA_ERROR: A common error is returned. Data availability unknown.
// PDF_DATA_NOTAVAIL: Data not yet available.
// PDF_DATA_AVAIL: Data available.
//
// Applications should call this function whenever new data arrives, and process
// all the generated download hints, if any, until the function returns
// |PDF_DATA_ERROR| or |PDF_DATA_AVAIL|.
// if hints is nullptr, the function just check current document availability.
//
// Once all data is available, call FPDFAvail_GetDocument() to get a document
// handle.
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsDocAvail(FPDF_AVAIL avail,
FX_DOWNLOADHINTS* hints);
// Get document from the availability provider.
//
// avail - handle to document availability provider.
// password - password for decrypting the PDF file. Optional.
//
// Returns a handle to the document.
//
// When FPDFAvail_IsDocAvail() returns TRUE, call FPDFAvail_GetDocument() to
// retrieve the document handle.
// See the comments for FPDF_LoadDocument() regarding the encoding for
// |password|.
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDFAvail_GetDocument(FPDF_AVAIL avail, FPDF_BYTESTRING password);
// Get the page number for the first available page in a linearized PDF.
//
// doc - document handle.
//
// Returns the zero-based index for the first available page.
//
// For most linearized PDFs, the first available page will be the first page,
// however, some PDFs might make another page the first available page.
// For non-linearized PDFs, this function will always return zero.
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_GetFirstPageNum(FPDF_DOCUMENT doc);
// Check if |page_index| is ready for loading, if not, get the
// |FX_DOWNLOADHINTS|.
//
// avail - handle to document availability provider.
// page_index - index number of the page. Zero for the first page.
// hints - pointer to a download hints interface. Populated if
// |page_index| is not available.
//
// Returns one of:
// PDF_DATA_ERROR: A common error is returned. Data availability unknown.
// PDF_DATA_NOTAVAIL: Data not yet available.
// PDF_DATA_AVAIL: Data available.
//
// This function can be called only after FPDFAvail_GetDocument() is called.
// Applications should call this function whenever new data arrives and process
// all the generated download |hints|, if any, until this function returns
// |PDF_DATA_ERROR| or |PDF_DATA_AVAIL|. Applications can then perform page
// loading.
// if hints is nullptr, the function just check current availability of
// specified page.
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsPageAvail(FPDF_AVAIL avail,
int page_index,
FX_DOWNLOADHINTS* hints);
// Check if form data is ready for initialization, if not, get the
// |FX_DOWNLOADHINTS|.
//
// avail - handle to document availability provider.
// hints - pointer to a download hints interface. Populated if form is not
// ready for initialization.
//
// Returns one of:
// PDF_FORM_ERROR: A common eror, in general incorrect parameters.
// PDF_FORM_NOTAVAIL: Data not available.
// PDF_FORM_AVAIL: Data available.
// PDF_FORM_NOTEXIST: No form data.
//
// This function can be called only after FPDFAvail_GetDocument() is called.
// The application should call this function whenever new data arrives and
// process all the generated download |hints|, if any, until the function
// |PDF_FORM_ERROR|, |PDF_FORM_AVAIL| or |PDF_FORM_NOTEXIST|.
// if hints is nullptr, the function just check current form availability.
//
// Applications can then perform page loading. It is recommend to call
// FPDFDOC_InitFormFillEnvironment() when |PDF_FORM_AVAIL| is returned.
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsFormAvail(FPDF_AVAIL avail,
FX_DOWNLOADHINTS* hints);
// Check whether a document is a linearized PDF.
//
// avail - handle to document availability provider.
//
// Returns one of:
// PDF_LINEARIZED
// PDF_NOT_LINEARIZED
// PDF_LINEARIZATION_UNKNOWN
//
// FPDFAvail_IsLinearized() will return |PDF_LINEARIZED| or |PDF_NOT_LINEARIZED|
// when we have 1k of data. If the files size less than 1k, it returns
// |PDF_LINEARIZATION_UNKNOWN| as there is insufficient information to determine
// if the PDF is linearlized.
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsLinearized(FPDF_AVAIL avail);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_DATAAVAIL_H_

View file

@ -0,0 +1,438 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_DOC_H_
#define PUBLIC_FPDF_DOC_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Unsupported action type.
#define PDFACTION_UNSUPPORTED 0
// Go to a destination within current document.
#define PDFACTION_GOTO 1
// Go to a destination within another document.
#define PDFACTION_REMOTEGOTO 2
// URI, including web pages and other Internet resources.
#define PDFACTION_URI 3
// Launch an application or open a file.
#define PDFACTION_LAUNCH 4
// Go to a destination in an embedded file.
#define PDFACTION_EMBEDDEDGOTO 5
// View destination fit types. See pdfmark reference v9, page 48.
#define PDFDEST_VIEW_UNKNOWN_MODE 0
#define PDFDEST_VIEW_XYZ 1
#define PDFDEST_VIEW_FIT 2
#define PDFDEST_VIEW_FITH 3
#define PDFDEST_VIEW_FITV 4
#define PDFDEST_VIEW_FITR 5
#define PDFDEST_VIEW_FITB 6
#define PDFDEST_VIEW_FITBH 7
#define PDFDEST_VIEW_FITBV 8
// The file identifier entry type. See section 14.4 "File Identifiers" of the
// ISO 32000-1:2008 spec.
typedef enum {
FILEIDTYPE_PERMANENT = 0,
FILEIDTYPE_CHANGING = 1
} FPDF_FILEIDTYPE;
// Get the first child of |bookmark|, or the first top-level bookmark item.
//
// document - handle to the document.
// bookmark - handle to the current bookmark. Pass NULL for the first top
// level item.
//
// Returns a handle to the first child of |bookmark| or the first top-level
// bookmark item. NULL if no child or top-level bookmark found.
// Note that another name for the bookmarks is the document outline, as
// described in ISO 32000-1:2008, section 12.3.3.
FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark);
// Get the next sibling of |bookmark|.
//
// document - handle to the document.
// bookmark - handle to the current bookmark.
//
// Returns a handle to the next sibling of |bookmark|, or NULL if this is the
// last bookmark at this level.
//
// Note that the caller is responsible for handling circular bookmark
// references, as may arise from malformed documents.
FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark);
// Get the title of |bookmark|.
//
// bookmark - handle to the bookmark.
// buffer - buffer for the title. May be NULL.
// buflen - the length of the buffer in bytes. May be 0.
//
// Returns the number of bytes in the title, including the terminating NUL
// character. The number of bytes is returned regardless of the |buffer| and
// |buflen| parameters.
//
// Regardless of the platform, the |buffer| is always in UTF-16LE encoding. The
// string is terminated by a UTF16 NUL character. If |buflen| is less than the
// required length, or |buffer| is NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark,
void* buffer,
unsigned long buflen);
// Experimental API.
// Get the number of chlidren of |bookmark|.
//
// bookmark - handle to the bookmark.
//
// Returns a signed integer that represents the number of sub-items the given
// bookmark has. If the value is positive, child items shall be shown by default
// (open state). If the value is negative, child items shall be hidden by
// default (closed state). Please refer to PDF 32000-1:2008, Table 153.
// Returns 0 if the bookmark has no children or is invalid.
FPDF_EXPORT int FPDF_CALLCONV FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark);
// Find the bookmark with |title| in |document|.
//
// document - handle to the document.
// title - the UTF-16LE encoded Unicode title for which to search.
//
// Returns the handle to the bookmark, or NULL if |title| can't be found.
//
// FPDFBookmark_Find() will always return the first bookmark found even if
// multiple bookmarks have the same |title|.
FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title);
// Get the destination associated with |bookmark|.
//
// document - handle to the document.
// bookmark - handle to the bookmark.
//
// Returns the handle to the destination data, or NULL if no destination is
// associated with |bookmark|.
FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark);
// Get the action associated with |bookmark|.
//
// bookmark - handle to the bookmark.
//
// Returns the handle to the action data, or NULL if no action is associated
// with |bookmark|.
// If this function returns a valid handle, it is valid as long as |bookmark| is
// valid.
// If this function returns NULL, FPDFBookmark_GetDest() should be called to get
// the |bookmark| destination data.
FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark);
// Get the type of |action|.
//
// action - handle to the action.
//
// Returns one of:
// PDFACTION_UNSUPPORTED
// PDFACTION_GOTO
// PDFACTION_REMOTEGOTO
// PDFACTION_URI
// PDFACTION_LAUNCH
FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION action);
// Get the destination of |action|.
//
// document - handle to the document.
// action - handle to the action. |action| must be a |PDFACTION_GOTO| or
// |PDFACTION_REMOTEGOTO|.
//
// Returns a handle to the destination data, or NULL on error, typically
// because the arguments were bad or the action was of the wrong type.
//
// In the case of |PDFACTION_REMOTEGOTO|, you must first call
// FPDFAction_GetFilePath(), then load the document at that path, then pass
// the document handle from that document as |document| to FPDFAction_GetDest().
FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
FPDF_ACTION action);
// Get the file path of |action|.
//
// action - handle to the action. |action| must be a |PDFACTION_LAUNCH| or
// |PDFACTION_REMOTEGOTO|.
// buffer - a buffer for output the path string. May be NULL.
// buflen - the length of the buffer, in bytes. May be 0.
//
// Returns the number of bytes in the file path, including the trailing NUL
// character, or 0 on error, typically because the arguments were bad or the
// action was of the wrong type.
//
// Regardless of the platform, the |buffer| is always in UTF-8 encoding.
// If |buflen| is less than the returned length, or |buffer| is NULL, |buffer|
// will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen);
// Get the URI path of |action|.
//
// document - handle to the document.
// action - handle to the action. Must be a |PDFACTION_URI|.
// buffer - a buffer for the path string. May be NULL.
// buflen - the length of the buffer, in bytes. May be 0.
//
// Returns the number of bytes in the URI path, including the trailing NUL
// character, or 0 on error, typically because the arguments were bad or the
// action was of the wrong type.
//
// The |buffer| may contain badly encoded data. The caller should validate the
// output. e.g. Check to see if it is UTF-8.
//
// If |buflen| is less than the returned length, or |buffer| is NULL, |buffer|
// will not be modified.
//
// Historically, the documentation for this API claimed |buffer| is always
// encoded in 7-bit ASCII, but did not actually enforce it.
// https://pdfium.googlesource.com/pdfium.git/+/d609e84cee2e14a18333247485af91df48a40592
// added that enforcement, but that did not work well for real world PDFs that
// used UTF-8. As of this writing, this API reverted back to its original
// behavior prior to commit d609e84cee.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAction_GetURIPath(FPDF_DOCUMENT document,
FPDF_ACTION action,
void* buffer,
unsigned long buflen);
// Get the page index of |dest|.
//
// document - handle to the document.
// dest - handle to the destination.
//
// Returns the 0-based page index containing |dest|. Returns -1 on error.
FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document,
FPDF_DEST dest);
// Experimental API.
// Get the view (fit type) specified by |dest|.
//
// dest - handle to the destination.
// pNumParams - receives the number of view parameters, which is at most 4.
// pParams - buffer to write the view parameters. Must be at least 4
// FS_FLOATs long.
// Returns one of the PDFDEST_VIEW_* constants, PDFDEST_VIEW_UNKNOWN_MODE if
// |dest| does not specify a view.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFDest_GetView(FPDF_DEST dest, unsigned long* pNumParams, FS_FLOAT* pParams);
// Get the (x, y, zoom) location of |dest| in the destination page, if the
// destination is in [page /XYZ x y zoom] syntax.
//
// dest - handle to the destination.
// hasXVal - out parameter; true if the x value is not null
// hasYVal - out parameter; true if the y value is not null
// hasZoomVal - out parameter; true if the zoom value is not null
// x - out parameter; the x coordinate, in page coordinates.
// y - out parameter; the y coordinate, in page coordinates.
// zoom - out parameter; the zoom value.
// Returns TRUE on successfully reading the /XYZ value.
//
// Note the [x, y, zoom] values are only set if the corresponding hasXVal,
// hasYVal or hasZoomVal flags are true.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFDest_GetLocationInPage(FPDF_DEST dest,
FPDF_BOOL* hasXVal,
FPDF_BOOL* hasYVal,
FPDF_BOOL* hasZoomVal,
FS_FLOAT* x,
FS_FLOAT* y,
FS_FLOAT* zoom);
// Find a link at point (|x|,|y|) on |page|.
//
// page - handle to the document page.
// x - the x coordinate, in the page coordinate system.
// y - the y coordinate, in the page coordinate system.
//
// Returns a handle to the link, or NULL if no link found at the given point.
//
// You can convert coordinates from screen coordinates to page coordinates using
// FPDF_DeviceToPage().
FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
double x,
double y);
// Find the Z-order of link at point (|x|,|y|) on |page|.
//
// page - handle to the document page.
// x - the x coordinate, in the page coordinate system.
// y - the y coordinate, in the page coordinate system.
//
// Returns the Z-order of the link, or -1 if no link found at the given point.
// Larger Z-order numbers are closer to the front.
//
// You can convert coordinates from screen coordinates to page coordinates using
// FPDF_DeviceToPage().
FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
double x,
double y);
// Get destination info for |link|.
//
// document - handle to the document.
// link - handle to the link.
//
// Returns a handle to the destination, or NULL if there is no destination
// associated with the link. In this case, you should call FPDFLink_GetAction()
// to retrieve the action associated with |link|.
FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
FPDF_LINK link);
// Get action info for |link|.
//
// link - handle to the link.
//
// Returns a handle to the action associated to |link|, or NULL if no action.
// If this function returns a valid handle, it is valid as long as |link| is
// valid.
FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK link);
// Enumerates all the link annotations in |page|.
//
// page - handle to the page.
// start_pos - the start position, should initially be 0 and is updated with
// the next start position on return.
// link_annot - the link handle for |startPos|.
//
// Returns TRUE on success.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
int* start_pos,
FPDF_LINK* link_annot);
// Experimental API.
// Gets FPDF_ANNOTATION object for |link_annot|.
//
// page - handle to the page in which FPDF_LINK object is present.
// link_annot - handle to link annotation.
//
// Returns FPDF_ANNOTATION from the FPDF_LINK and NULL on failure,
// if the input link annot or page is NULL.
FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
FPDFLink_GetAnnot(FPDF_PAGE page, FPDF_LINK link_annot);
// Get the rectangle for |link_annot|.
//
// link_annot - handle to the link annotation.
// rect - the annotation rectangle.
//
// Returns true on success.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot,
FS_RECTF* rect);
// Get the count of quadrilateral points to the |link_annot|.
//
// link_annot - handle to the link annotation.
//
// Returns the count of quadrilateral points.
FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK link_annot);
// Get the quadrilateral points for the specified |quad_index| in |link_annot|.
//
// link_annot - handle to the link annotation.
// quad_index - the specified quad point index.
// quad_points - receives the quadrilateral points.
//
// Returns true on success.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFLink_GetQuadPoints(FPDF_LINK link_annot,
int quad_index,
FS_QUADPOINTSF* quad_points);
// Experimental API
// Gets an additional-action from |page|.
//
// page - handle to the page, as returned by FPDF_LoadPage().
// aa_type - the type of the page object's addtional-action, defined
// in public/fpdf_formfill.h
//
// Returns the handle to the action data, or NULL if there is no
// additional-action of type |aa_type|.
// If this function returns a valid handle, it is valid as long as |page| is
// valid.
FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page,
int aa_type);
// Experimental API.
// Get the file identifer defined in the trailer of |document|.
//
// document - handle to the document.
// id_type - the file identifier type to retrieve.
// buffer - a buffer for the file identifier. May be NULL.
// buflen - the length of the buffer, in bytes. May be 0.
//
// Returns the number of bytes in the file identifier, including the NUL
// terminator.
//
// The |buffer| is always a byte string. The |buffer| is followed by a NUL
// terminator. If |buflen| is less than the returned length, or |buffer| is
// NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetFileIdentifier(FPDF_DOCUMENT document,
FPDF_FILEIDTYPE id_type,
void* buffer,
unsigned long buflen);
// Get meta-data |tag| content from |document|.
//
// document - handle to the document.
// tag - the tag to retrieve. The tag can be one of:
// Title, Author, Subject, Keywords, Creator, Producer,
// CreationDate, or ModDate.
// For detailed explanations of these tags and their respective
// values, please refer to PDF Reference 1.6, section 10.2.1,
// 'Document Information Dictionary'.
// buffer - a buffer for the tag. May be NULL.
// buflen - the length of the buffer, in bytes. May be 0.
//
// Returns the number of bytes in the tag, including trailing zeros.
//
// The |buffer| is always encoded in UTF-16LE. The |buffer| is followed by two
// bytes of zeros indicating the end of the string. If |buflen| is less than
// the returned length, or |buffer| is NULL, |buffer| will not be modified.
//
// For linearized files, FPDFAvail_IsFormAvail must be called before this, and
// it must have returned PDF_FORM_AVAIL or PDF_FORM_NOTEXIST. Before that, there
// is no guarantee the metadata has been loaded.
FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
FPDF_BYTESTRING tag,
void* buffer,
unsigned long buflen);
// Get the page label for |page_index| from |document|.
//
// document - handle to the document.
// page_index - the 0-based index of the page.
// buffer - a buffer for the page label. May be NULL.
// buflen - the length of the buffer, in bytes. May be 0.
//
// Returns the number of bytes in the page label, including trailing zeros.
//
// The |buffer| is always encoded in UTF-16LE. The |buffer| is followed by two
// bytes of zeros indicating the end of the string. If |buflen| is less than
// the returned length, or |buffer| is NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetPageLabel(FPDF_DOCUMENT document,
int page_index,
void* buffer,
unsigned long buflen);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_DOC_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,119 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_EXT_H_
#define PUBLIC_FPDF_EXT_H_
#include <time.h>
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Unsupported XFA form.
#define FPDF_UNSP_DOC_XFAFORM 1
// Unsupported portable collection.
#define FPDF_UNSP_DOC_PORTABLECOLLECTION 2
// Unsupported attachment.
#define FPDF_UNSP_DOC_ATTACHMENT 3
// Unsupported security.
#define FPDF_UNSP_DOC_SECURITY 4
// Unsupported shared review.
#define FPDF_UNSP_DOC_SHAREDREVIEW 5
// Unsupported shared form, acrobat.
#define FPDF_UNSP_DOC_SHAREDFORM_ACROBAT 6
// Unsupported shared form, filesystem.
#define FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM 7
// Unsupported shared form, email.
#define FPDF_UNSP_DOC_SHAREDFORM_EMAIL 8
// Unsupported 3D annotation.
#define FPDF_UNSP_ANNOT_3DANNOT 11
// Unsupported movie annotation.
#define FPDF_UNSP_ANNOT_MOVIE 12
// Unsupported sound annotation.
#define FPDF_UNSP_ANNOT_SOUND 13
// Unsupported screen media annotation.
#define FPDF_UNSP_ANNOT_SCREEN_MEDIA 14
// Unsupported screen rich media annotation.
#define FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA 15
// Unsupported attachment annotation.
#define FPDF_UNSP_ANNOT_ATTACHMENT 16
// Unsupported signature annotation.
#define FPDF_UNSP_ANNOT_SIG 17
// Interface for unsupported feature notifications.
typedef struct _UNSUPPORT_INFO {
// Version number of the interface. Must be 1.
int version;
// Unsupported object notification function.
// Interface Version: 1
// Implementation Required: Yes
//
// pThis - pointer to the interface structure.
// nType - the type of unsupported object. One of the |FPDF_UNSP_*| entries.
void (*FSDK_UnSupport_Handler)(struct _UNSUPPORT_INFO* pThis, int nType);
} UNSUPPORT_INFO;
// Setup an unsupported object handler.
//
// unsp_info - Pointer to an UNSUPPORT_INFO structure.
//
// Returns TRUE on success.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FSDK_SetUnSpObjProcessHandler(UNSUPPORT_INFO* unsp_info);
// Set replacement function for calls to time().
//
// This API is intended to be used only for testing, thus may cause PDFium to
// behave poorly in production environments.
//
// func - Function pointer to alternate implementation of time(), or
// NULL to restore to actual time() call itself.
FPDF_EXPORT void FPDF_CALLCONV FSDK_SetTimeFunction(time_t (*func)());
// Set replacement function for calls to localtime().
//
// This API is intended to be used only for testing, thus may cause PDFium to
// behave poorly in production environments.
//
// func - Function pointer to alternate implementation of localtime(), or
// NULL to restore to actual localtime() call itself.
FPDF_EXPORT void FPDF_CALLCONV
FSDK_SetLocaltimeFunction(struct tm* (*func)(const time_t*));
// Unknown page mode.
#define PAGEMODE_UNKNOWN -1
// Document outline, and thumbnails hidden.
#define PAGEMODE_USENONE 0
// Document outline visible.
#define PAGEMODE_USEOUTLINES 1
// Thumbnail images visible.
#define PAGEMODE_USETHUMBS 2
// Full-screen mode, no menu bar, window controls, or other decorations visible.
#define PAGEMODE_FULLSCREEN 3
// Optional content group panel visible.
#define PAGEMODE_USEOC 4
// Attachments panel visible.
#define PAGEMODE_USEATTACHMENTS 5
// Get the document's PageMode.
//
// doc - Handle to document.
//
// Returns one of the |PAGEMODE_*| flags defined above.
//
// The page mode defines how the document should be initially displayed.
FPDF_EXPORT int FPDF_CALLCONV FPDFDoc_GetPageMode(FPDF_DOCUMENT document);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_EXT_H_

View file

@ -0,0 +1,44 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_FLATTEN_H_
#define PUBLIC_FPDF_FLATTEN_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
// Flatten operation failed.
#define FLATTEN_FAIL 0
// Flatten operation succeed.
#define FLATTEN_SUCCESS 1
// Nothing to be flattened.
#define FLATTEN_NOTHINGTODO 2
// Flatten for normal display.
#define FLAT_NORMALDISPLAY 0
// Flatten for print.
#define FLAT_PRINT 1
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Flatten annotations and form fields into the page contents.
//
// page - handle to the page.
// nFlag - One of the |FLAT_*| values denoting the page usage.
//
// Returns one of the |FLATTEN_*| values.
//
// Currently, all failures return |FLATTEN_FAIL| with no indication of the
// cause.
FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_FLATTEN_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,207 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_FWLEVENT_H_
#define PUBLIC_FPDF_FWLEVENT_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Key flags.
typedef enum {
FWL_EVENTFLAG_ShiftKey = 1 << 0,
FWL_EVENTFLAG_ControlKey = 1 << 1,
FWL_EVENTFLAG_AltKey = 1 << 2,
FWL_EVENTFLAG_MetaKey = 1 << 3,
FWL_EVENTFLAG_KeyPad = 1 << 4,
FWL_EVENTFLAG_AutoRepeat = 1 << 5,
FWL_EVENTFLAG_LeftButtonDown = 1 << 6,
FWL_EVENTFLAG_MiddleButtonDown = 1 << 7,
FWL_EVENTFLAG_RightButtonDown = 1 << 8,
} FWL_EVENTFLAG;
// Virtual keycodes.
typedef enum {
FWL_VKEY_Back = 0x08,
FWL_VKEY_Tab = 0x09,
FWL_VKEY_NewLine = 0x0A,
FWL_VKEY_Clear = 0x0C,
FWL_VKEY_Return = 0x0D,
FWL_VKEY_Shift = 0x10,
FWL_VKEY_Control = 0x11,
FWL_VKEY_Menu = 0x12,
FWL_VKEY_Pause = 0x13,
FWL_VKEY_Capital = 0x14,
FWL_VKEY_Kana = 0x15,
FWL_VKEY_Hangul = 0x15,
FWL_VKEY_Junja = 0x17,
FWL_VKEY_Final = 0x18,
FWL_VKEY_Hanja = 0x19,
FWL_VKEY_Kanji = 0x19,
FWL_VKEY_Escape = 0x1B,
FWL_VKEY_Convert = 0x1C,
FWL_VKEY_NonConvert = 0x1D,
FWL_VKEY_Accept = 0x1E,
FWL_VKEY_ModeChange = 0x1F,
FWL_VKEY_Space = 0x20,
FWL_VKEY_Prior = 0x21,
FWL_VKEY_Next = 0x22,
FWL_VKEY_End = 0x23,
FWL_VKEY_Home = 0x24,
FWL_VKEY_Left = 0x25,
FWL_VKEY_Up = 0x26,
FWL_VKEY_Right = 0x27,
FWL_VKEY_Down = 0x28,
FWL_VKEY_Select = 0x29,
FWL_VKEY_Print = 0x2A,
FWL_VKEY_Execute = 0x2B,
FWL_VKEY_Snapshot = 0x2C,
FWL_VKEY_Insert = 0x2D,
FWL_VKEY_Delete = 0x2E,
FWL_VKEY_Help = 0x2F,
FWL_VKEY_0 = 0x30,
FWL_VKEY_1 = 0x31,
FWL_VKEY_2 = 0x32,
FWL_VKEY_3 = 0x33,
FWL_VKEY_4 = 0x34,
FWL_VKEY_5 = 0x35,
FWL_VKEY_6 = 0x36,
FWL_VKEY_7 = 0x37,
FWL_VKEY_8 = 0x38,
FWL_VKEY_9 = 0x39,
FWL_VKEY_A = 0x41,
FWL_VKEY_B = 0x42,
FWL_VKEY_C = 0x43,
FWL_VKEY_D = 0x44,
FWL_VKEY_E = 0x45,
FWL_VKEY_F = 0x46,
FWL_VKEY_G = 0x47,
FWL_VKEY_H = 0x48,
FWL_VKEY_I = 0x49,
FWL_VKEY_J = 0x4A,
FWL_VKEY_K = 0x4B,
FWL_VKEY_L = 0x4C,
FWL_VKEY_M = 0x4D,
FWL_VKEY_N = 0x4E,
FWL_VKEY_O = 0x4F,
FWL_VKEY_P = 0x50,
FWL_VKEY_Q = 0x51,
FWL_VKEY_R = 0x52,
FWL_VKEY_S = 0x53,
FWL_VKEY_T = 0x54,
FWL_VKEY_U = 0x55,
FWL_VKEY_V = 0x56,
FWL_VKEY_W = 0x57,
FWL_VKEY_X = 0x58,
FWL_VKEY_Y = 0x59,
FWL_VKEY_Z = 0x5A,
FWL_VKEY_LWin = 0x5B,
FWL_VKEY_Command = 0x5B,
FWL_VKEY_RWin = 0x5C,
FWL_VKEY_Apps = 0x5D,
FWL_VKEY_Sleep = 0x5F,
FWL_VKEY_NumPad0 = 0x60,
FWL_VKEY_NumPad1 = 0x61,
FWL_VKEY_NumPad2 = 0x62,
FWL_VKEY_NumPad3 = 0x63,
FWL_VKEY_NumPad4 = 0x64,
FWL_VKEY_NumPad5 = 0x65,
FWL_VKEY_NumPad6 = 0x66,
FWL_VKEY_NumPad7 = 0x67,
FWL_VKEY_NumPad8 = 0x68,
FWL_VKEY_NumPad9 = 0x69,
FWL_VKEY_Multiply = 0x6A,
FWL_VKEY_Add = 0x6B,
FWL_VKEY_Separator = 0x6C,
FWL_VKEY_Subtract = 0x6D,
FWL_VKEY_Decimal = 0x6E,
FWL_VKEY_Divide = 0x6F,
FWL_VKEY_F1 = 0x70,
FWL_VKEY_F2 = 0x71,
FWL_VKEY_F3 = 0x72,
FWL_VKEY_F4 = 0x73,
FWL_VKEY_F5 = 0x74,
FWL_VKEY_F6 = 0x75,
FWL_VKEY_F7 = 0x76,
FWL_VKEY_F8 = 0x77,
FWL_VKEY_F9 = 0x78,
FWL_VKEY_F10 = 0x79,
FWL_VKEY_F11 = 0x7A,
FWL_VKEY_F12 = 0x7B,
FWL_VKEY_F13 = 0x7C,
FWL_VKEY_F14 = 0x7D,
FWL_VKEY_F15 = 0x7E,
FWL_VKEY_F16 = 0x7F,
FWL_VKEY_F17 = 0x80,
FWL_VKEY_F18 = 0x81,
FWL_VKEY_F19 = 0x82,
FWL_VKEY_F20 = 0x83,
FWL_VKEY_F21 = 0x84,
FWL_VKEY_F22 = 0x85,
FWL_VKEY_F23 = 0x86,
FWL_VKEY_F24 = 0x87,
FWL_VKEY_NunLock = 0x90,
FWL_VKEY_Scroll = 0x91,
FWL_VKEY_LShift = 0xA0,
FWL_VKEY_RShift = 0xA1,
FWL_VKEY_LControl = 0xA2,
FWL_VKEY_RControl = 0xA3,
FWL_VKEY_LMenu = 0xA4,
FWL_VKEY_RMenu = 0xA5,
FWL_VKEY_BROWSER_Back = 0xA6,
FWL_VKEY_BROWSER_Forward = 0xA7,
FWL_VKEY_BROWSER_Refresh = 0xA8,
FWL_VKEY_BROWSER_Stop = 0xA9,
FWL_VKEY_BROWSER_Search = 0xAA,
FWL_VKEY_BROWSER_Favorites = 0xAB,
FWL_VKEY_BROWSER_Home = 0xAC,
FWL_VKEY_VOLUME_Mute = 0xAD,
FWL_VKEY_VOLUME_Down = 0xAE,
FWL_VKEY_VOLUME_Up = 0xAF,
FWL_VKEY_MEDIA_NEXT_Track = 0xB0,
FWL_VKEY_MEDIA_PREV_Track = 0xB1,
FWL_VKEY_MEDIA_Stop = 0xB2,
FWL_VKEY_MEDIA_PLAY_Pause = 0xB3,
FWL_VKEY_MEDIA_LAUNCH_Mail = 0xB4,
FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select = 0xB5,
FWL_VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
FWL_VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
FWL_VKEY_OEM_1 = 0xBA,
FWL_VKEY_OEM_Plus = 0xBB,
FWL_VKEY_OEM_Comma = 0xBC,
FWL_VKEY_OEM_Minus = 0xBD,
FWL_VKEY_OEM_Period = 0xBE,
FWL_VKEY_OEM_2 = 0xBF,
FWL_VKEY_OEM_3 = 0xC0,
FWL_VKEY_OEM_4 = 0xDB,
FWL_VKEY_OEM_5 = 0xDC,
FWL_VKEY_OEM_6 = 0xDD,
FWL_VKEY_OEM_7 = 0xDE,
FWL_VKEY_OEM_8 = 0xDF,
FWL_VKEY_OEM_102 = 0xE2,
FWL_VKEY_ProcessKey = 0xE5,
FWL_VKEY_Packet = 0xE7,
FWL_VKEY_Attn = 0xF6,
FWL_VKEY_Crsel = 0xF7,
FWL_VKEY_Exsel = 0xF8,
FWL_VKEY_Ereof = 0xF9,
FWL_VKEY_Play = 0xFA,
FWL_VKEY_Zoom = 0xFB,
FWL_VKEY_NoName = 0xFC,
FWL_VKEY_PA1 = 0xFD,
FWL_VKEY_OEM_Clear = 0xFE,
FWL_VKEY_Unknown = 0,
} FWL_VKEYCODE;
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_FWLEVENT_H_

View file

@ -0,0 +1,77 @@
// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_FPDF_JAVASCRIPT_H_
#define PUBLIC_FPDF_JAVASCRIPT_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Experimental API.
// Get the number of JavaScript actions in |document|.
//
// document - handle to a document.
//
// Returns the number of JavaScript actions in |document| or -1 on error.
FPDF_EXPORT int FPDF_CALLCONV
FPDFDoc_GetJavaScriptActionCount(FPDF_DOCUMENT document);
// Experimental API.
// Get the JavaScript action at |index| in |document|.
//
// document - handle to a document.
// index - the index of the requested JavaScript action.
//
// Returns the handle to the JavaScript action, or NULL on failure.
// Caller owns the returned handle and must close it with
// FPDFDoc_CloseJavaScriptAction().
FPDF_EXPORT FPDF_JAVASCRIPT_ACTION FPDF_CALLCONV
FPDFDoc_GetJavaScriptAction(FPDF_DOCUMENT document, int index);
// Experimental API.
// Close a loaded FPDF_JAVASCRIPT_ACTION object.
// javascript - Handle to a JavaScript action.
FPDF_EXPORT void FPDF_CALLCONV
FPDFDoc_CloseJavaScriptAction(FPDF_JAVASCRIPT_ACTION javascript);
// Experimental API.
// Get the name from the |javascript| handle. |buffer| is only modified if
// |buflen| is longer than the length of the name. On errors, |buffer| is
// unmodified and the returned length is 0.
//
// javascript - handle to an JavaScript action.
// buffer - buffer for holding the name, encoded in UTF-16LE.
// buflen - length of the buffer in bytes.
//
// Returns the length of the JavaScript action name in bytes.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFJavaScriptAction_GetName(FPDF_JAVASCRIPT_ACTION javascript,
FPDF_WCHAR* buffer,
unsigned long buflen);
// Experimental API.
// Get the script from the |javascript| handle. |buffer| is only modified if
// |buflen| is longer than the length of the script. On errors, |buffer| is
// unmodified and the returned length is 0.
//
// javascript - handle to an JavaScript action.
// buffer - buffer for holding the name, encoded in UTF-16LE.
// buflen - length of the buffer in bytes.
//
// Returns the length of the JavaScript action name in bytes.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFJavaScriptAction_GetScript(FPDF_JAVASCRIPT_ACTION javascript,
FPDF_WCHAR* buffer,
unsigned long buflen);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_JAVASCRIPT_H_

View file

@ -0,0 +1,115 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_PPO_H_
#define PUBLIC_FPDF_PPO_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif
// Experimental API.
// Import pages to a FPDF_DOCUMENT.
//
// dest_doc - The destination document for the pages.
// src_doc - The document to be imported.
// page_indices - An array of page indices to be imported. The first page is
// zero. If |page_indices| is NULL, all pages from |src_doc|
// are imported.
// length - The length of the |page_indices| array.
// index - The page index at which to insert the first imported page
// into |dest_doc|. The first page is zero.
//
// Returns TRUE on success. Returns FALSE if any pages in |page_indices| is
// invalid.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_ImportPagesByIndex(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
const int* page_indices,
unsigned long length,
int index);
// Import pages to a FPDF_DOCUMENT.
//
// dest_doc - The destination document for the pages.
// src_doc - The document to be imported.
// pagerange - A page range string, Such as "1,3,5-7". The first page is one.
// If |pagerange| is NULL, all pages from |src_doc| are imported.
// index - The page index at which to insert the first imported page into
// |dest_doc|. The first page is zero.
//
// Returns TRUE on success. Returns FALSE if any pages in |pagerange| is
// invalid or if |pagerange| cannot be read.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
FPDF_BYTESTRING pagerange,
int index);
// Experimental API.
// Create a new document from |src_doc|. The pages of |src_doc| will be
// combined to provide |num_pages_on_x_axis x num_pages_on_y_axis| pages per
// |output_doc| page.
//
// src_doc - The document to be imported.
// output_width - The output page width in PDF "user space" units.
// output_height - The output page height in PDF "user space" units.
// num_pages_on_x_axis - The number of pages on X Axis.
// num_pages_on_y_axis - The number of pages on Y Axis.
//
// Return value:
// A handle to the created document, or NULL on failure.
//
// Comments:
// number of pages per page = num_pages_on_x_axis * num_pages_on_y_axis
//
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc,
float output_width,
float output_height,
size_t num_pages_on_x_axis,
size_t num_pages_on_y_axis);
// Experimental API.
// Create a template to generate form xobjects from |src_doc|'s page at
// |src_page_index|, for use in |dest_doc|.
//
// Returns a handle on success, or NULL on failure. Caller owns the newly
// created object.
FPDF_EXPORT FPDF_XOBJECT FPDF_CALLCONV
FPDF_NewXObjectFromPage(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
int src_page_index);
// Experimental API.
// Close an FPDF_XOBJECT handle created by FPDF_NewXObjectFromPage().
// FPDF_PAGEOBJECTs created from the FPDF_XOBJECT handle are not affected.
FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseXObject(FPDF_XOBJECT xobject);
// Experimental API.
// Create a new form object from an FPDF_XOBJECT object.
//
// Returns a new form object on success, or NULL on failure. Caller owns the
// newly created object.
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDF_NewFormObjectFromXObject(FPDF_XOBJECT xobject);
// Copy the viewer preferences from |src_doc| into |dest_doc|.
//
// dest_doc - Document to write the viewer preferences into.
// src_doc - Document to read the viewer preferences from.
//
// Returns TRUE on success.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_PPO_H_

View file

@ -0,0 +1,159 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_PROGRESSIVE_H_
#define PUBLIC_FPDF_PROGRESSIVE_H_
// clang-format off
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
// Flags for progressive process status.
#define FPDF_RENDER_READY 0
#define FPDF_RENDER_TOBECONTINUED 1
#define FPDF_RENDER_DONE 2
#define FPDF_RENDER_FAILED 3
#ifdef __cplusplus
extern "C" {
#endif
// IFPDF_RENDERINFO interface.
typedef struct _IFSDK_PAUSE {
/*
* Version number of the interface. Currently must be 1.
*/
int version;
/*
* Method: NeedToPauseNow
* Check if we need to pause a progressive process now.
* Interface Version:
* 1
* Implementation Required:
* yes
* Parameters:
* pThis - Pointer to the interface structure itself
* Return Value:
* Non-zero for pause now, 0 for continue.
*/
FPDF_BOOL (*NeedToPauseNow)(struct _IFSDK_PAUSE* pThis);
// A user defined data pointer, used by user's application. Can be NULL.
void* user;
} IFSDK_PAUSE;
// Experimental API.
// Function: FPDF_RenderPageBitmapWithColorScheme_Start
// Start to render page contents to a device independent bitmap
// progressively with a specified color scheme for the content.
// Parameters:
// bitmap - Handle to the device independent bitmap (as the
// output buffer). Bitmap handle can be created by
// FPDFBitmap_Create function.
// page - Handle to the page as returned by FPDF_LoadPage
// function.
// start_x - Left pixel position of the display area in the
// bitmap coordinate.
// start_y - Top pixel position of the display area in the
// bitmap coordinate.
// size_x - Horizontal size (in pixels) for displaying the
// page.
// size_y - Vertical size (in pixels) for displaying the page.
// rotate - Page orientation: 0 (normal), 1 (rotated 90
// degrees clockwise), 2 (rotated 180 degrees),
// 3 (rotated 90 degrees counter-clockwise).
// flags - 0 for normal display, or combination of flags
// defined in fpdfview.h. With FPDF_ANNOT flag, it
// renders all annotations that does not require
// user-interaction, which are all annotations except
// widget and popup annotations.
// color_scheme - Color scheme to be used in rendering the |page|.
// If null, this function will work similar to
// FPDF_RenderPageBitmap_Start().
// pause - The IFSDK_PAUSE interface. A callback mechanism
// allowing the page rendering process.
// Return value:
// Rendering Status. See flags for progressive process status for the
// details.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_RenderPageBitmapWithColorScheme_Start(FPDF_BITMAP bitmap,
FPDF_PAGE page,
int start_x,
int start_y,
int size_x,
int size_y,
int rotate,
int flags,
const FPDF_COLORSCHEME* color_scheme,
IFSDK_PAUSE* pause);
// Function: FPDF_RenderPageBitmap_Start
// Start to render page contents to a device independent bitmap
// progressively.
// Parameters:
// bitmap - Handle to the device independent bitmap (as the
// output buffer). Bitmap handle can be created by
// FPDFBitmap_Create().
// page - Handle to the page, as returned by FPDF_LoadPage().
// start_x - Left pixel position of the display area in the
// bitmap coordinates.
// start_y - Top pixel position of the display area in the bitmap
// coordinates.
// size_x - Horizontal size (in pixels) for displaying the page.
// size_y - Vertical size (in pixels) for displaying the page.
// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees
// clockwise), 2 (rotated 180 degrees), 3 (rotated 90
// degrees counter-clockwise).
// flags - 0 for normal display, or combination of flags
// defined in fpdfview.h. With FPDF_ANNOT flag, it
// renders all annotations that does not require
// user-interaction, which are all annotations except
// widget and popup annotations.
// pause - The IFSDK_PAUSE interface.A callback mechanism
// allowing the page rendering process
// Return value:
// Rendering Status. See flags for progressive process status for the
// details.
FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap,
FPDF_PAGE page,
int start_x,
int start_y,
int size_x,
int size_y,
int rotate,
int flags,
IFSDK_PAUSE* pause);
// Function: FPDF_RenderPage_Continue
// Continue rendering a PDF page.
// Parameters:
// page - Handle to the page, as returned by FPDF_LoadPage().
// pause - The IFSDK_PAUSE interface (a callback mechanism
// allowing the page rendering process to be paused
// before it's finished). This can be NULL if you
// don't want to pause.
// Return value:
// The rendering status. See flags for progressive process status for
// the details.
FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPage_Continue(FPDF_PAGE page,
IFSDK_PAUSE* pause);
// Function: FPDF_RenderPage_Close
// Release the resource allocate during page rendering. Need to be
// called after finishing rendering or
// cancel the rendering.
// Parameters:
// page - Handle to the page, as returned by FPDF_LoadPage().
// Return value:
// None.
FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage_Close(FPDF_PAGE page);
#ifdef __cplusplus
}
#endif
#endif // PUBLIC_FPDF_PROGRESSIVE_H_

View file

@ -0,0 +1,85 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_SAVE_H_
#define PUBLIC_FPDF_SAVE_H_
// clang-format off
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif
// Structure for custom file write
typedef struct FPDF_FILEWRITE_ {
//
// Version number of the interface. Currently must be 1.
//
int version;
// Method: WriteBlock
// Output a block of data in your custom way.
// Interface Version:
// 1
// Implementation Required:
// Yes
// Comments:
// Called by function FPDF_SaveDocument
// Parameters:
// pThis - Pointer to the structure itself
// pData - Pointer to a buffer to output
// size - The size of the buffer.
// Return value:
// Should be non-zero if successful, zero for error.
int (*WriteBlock)(struct FPDF_FILEWRITE_* pThis,
const void* pData,
unsigned long size);
} FPDF_FILEWRITE;
// Flags for FPDF_SaveAsCopy()
#define FPDF_INCREMENTAL 1
#define FPDF_NO_INCREMENTAL 2
#define FPDF_REMOVE_SECURITY 3
// Function: FPDF_SaveAsCopy
// Saves the copy of specified document in custom way.
// Parameters:
// document - Handle to document, as returned by
// FPDF_LoadDocument() or FPDF_CreateNewDocument().
// pFileWrite - A pointer to a custom file write structure.
// flags - The creating flags.
// Return value:
// TRUE for succeed, FALSE for failed.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
FPDF_FILEWRITE* pFileWrite,
FPDF_DWORD flags);
// Function: FPDF_SaveWithVersion
// Same as FPDF_SaveAsCopy(), except the file version of the
// saved document can be specified by the caller.
// Parameters:
// document - Handle to document.
// pFileWrite - A pointer to a custom file write structure.
// flags - The creating flags.
// fileVersion - The PDF file version. File version: 14 for 1.4,
// 15 for 1.5, ...
// Return value:
// TRUE if succeed, FALSE if failed.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_SaveWithVersion(FPDF_DOCUMENT document,
FPDF_FILEWRITE* pFileWrite,
FPDF_DWORD flags,
int fileVersion);
#ifdef __cplusplus
}
#endif
#endif // PUBLIC_FPDF_SAVE_H_

View file

@ -0,0 +1,39 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_SEARCHEX_H_
#define PUBLIC_FPDF_SEARCHEX_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Get the character index in |text_page| internal character list.
//
// text_page - a text page information structure.
// nTextIndex - index of the text returned from FPDFText_GetText().
//
// Returns the index of the character in internal character list. -1 for error.
FPDF_EXPORT int FPDF_CALLCONV
FPDFText_GetCharIndexFromTextIndex(FPDF_TEXTPAGE text_page, int nTextIndex);
// Get the text index in |text_page| internal character list.
//
// text_page - a text page information structure.
// nCharIndex - index of the character in internal character list.
//
// Returns the index of the text returned from FPDFText_GetText(). -1 for error.
FPDF_EXPORT int FPDF_CALLCONV
FPDFText_GetTextIndexFromCharIndex(FPDF_TEXTPAGE text_page, int nCharIndex);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_SEARCHEX_H_

View file

@ -0,0 +1,155 @@
// Copyright 2020 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_FPDF_SIGNATURE_H_
#define PUBLIC_FPDF_SIGNATURE_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Experimental API.
// Function: FPDF_GetSignatureCount
// Get total number of signatures in the document.
// Parameters:
// document - Handle to document. Returned by FPDF_LoadDocument().
// Return value:
// Total number of signatures in the document on success, -1 on error.
FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document);
// Experimental API.
// Function: FPDF_GetSignatureObject
// Get the Nth signature of the document.
// Parameters:
// document - Handle to document. Returned by FPDF_LoadDocument().
// index - Index into the array of signatures of the document.
// Return value:
// Returns the handle to the signature, or NULL on failure. The caller
// does not take ownership of the returned FPDF_SIGNATURE. Instead, it
// remains valid until FPDF_CloseDocument() is called for the document.
FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV
FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index);
// Experimental API.
// Function: FPDFSignatureObj_GetContents
// Get the contents of a signature object.
// Parameters:
// signature - Handle to the signature object. Returned by
// FPDF_GetSignatureObject().
// buffer - The address of a buffer that receives the contents.
// length - The size, in bytes, of |buffer|.
// Return value:
// Returns the number of bytes in the contents on success, 0 on error.
//
// For public-key signatures, |buffer| is either a DER-encoded PKCS#1 binary or
// a DER-encoded PKCS#7 binary. If |length| is less than the returned length, or
// |buffer| is NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFSignatureObj_GetContents(FPDF_SIGNATURE signature,
void* buffer,
unsigned long length);
// Experimental API.
// Function: FPDFSignatureObj_GetByteRange
// Get the byte range of a signature object.
// Parameters:
// signature - Handle to the signature object. Returned by
// FPDF_GetSignatureObject().
// buffer - The address of a buffer that receives the
// byte range.
// length - The size, in ints, of |buffer|.
// Return value:
// Returns the number of ints in the byte range on
// success, 0 on error.
//
// |buffer| is an array of pairs of integers (starting byte offset,
// length in bytes) that describes the exact byte range for the digest
// calculation. If |length| is less than the returned length, or
// |buffer| is NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFSignatureObj_GetByteRange(FPDF_SIGNATURE signature,
int* buffer,
unsigned long length);
// Experimental API.
// Function: FPDFSignatureObj_GetSubFilter
// Get the encoding of the value of a signature object.
// Parameters:
// signature - Handle to the signature object. Returned by
// FPDF_GetSignatureObject().
// buffer - The address of a buffer that receives the encoding.
// length - The size, in bytes, of |buffer|.
// Return value:
// Returns the number of bytes in the encoding name (including the
// trailing NUL character) on success, 0 on error.
//
// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the
// returned length, or |buffer| is NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFSignatureObj_GetSubFilter(FPDF_SIGNATURE signature,
char* buffer,
unsigned long length);
// Experimental API.
// Function: FPDFSignatureObj_GetReason
// Get the reason (comment) of the signature object.
// Parameters:
// signature - Handle to the signature object. Returned by
// FPDF_GetSignatureObject().
// buffer - The address of a buffer that receives the reason.
// length - The size, in bytes, of |buffer|.
// Return value:
// Returns the number of bytes in the reason on success, 0 on error.
//
// Regardless of the platform, the |buffer| is always in UTF-16LE encoding. The
// string is terminated by a UTF16 NUL character. If |length| is less than the
// returned length, or |buffer| is NULL, |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFSignatureObj_GetReason(FPDF_SIGNATURE signature,
void* buffer,
unsigned long length);
// Experimental API.
// Function: FPDFSignatureObj_GetTime
// Get the time of signing of a signature object.
// Parameters:
// signature - Handle to the signature object. Returned by
// FPDF_GetSignatureObject().
// buffer - The address of a buffer that receives the time.
// length - The size, in bytes, of |buffer|.
// Return value:
// Returns the number of bytes in the encoding name (including the
// trailing NUL character) on success, 0 on error.
//
// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the
// returned length, or |buffer| is NULL, |buffer| will not be modified.
//
// The format of time is expected to be D:YYYYMMDDHHMMSS+XX'YY', i.e. it's
// percision is seconds, with timezone information. This value should be used
// only when the time of signing is not available in the (PKCS#7 binary)
// signature.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature,
char* buffer,
unsigned long length);
// Experimental API.
// Function: FPDFSignatureObj_GetDocMDPPermission
// Get the DocMDP permission of a signature object.
// Parameters:
// signature - Handle to the signature object. Returned by
// FPDF_GetSignatureObject().
// Return value:
// Returns the permission (1, 2 or 3) on success, 0 on error.
FPDF_EXPORT unsigned int FPDF_CALLCONV
FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // PUBLIC_FPDF_SIGNATURE_H_

View file

@ -0,0 +1,524 @@
// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_STRUCTTREE_H_
#define PUBLIC_FPDF_STRUCTTREE_H_
// clang-format off
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif
// Function: FPDF_StructTree_GetForPage
// Get the structure tree for a page.
// Parameters:
// page - Handle to the page, as returned by FPDF_LoadPage().
// Return value:
// A handle to the structure tree or NULL on error. The caller owns the
// returned handle and must use FPDF_StructTree_Close() to release it.
// The handle should be released before |page| gets released.
FPDF_EXPORT FPDF_STRUCTTREE FPDF_CALLCONV
FPDF_StructTree_GetForPage(FPDF_PAGE page);
// Function: FPDF_StructTree_Close
// Release a resource allocated by FPDF_StructTree_GetForPage().
// Parameters:
// struct_tree - Handle to the structure tree, as returned by
// FPDF_StructTree_LoadPage().
// Return value:
// None.
FPDF_EXPORT void FPDF_CALLCONV
FPDF_StructTree_Close(FPDF_STRUCTTREE struct_tree);
// Function: FPDF_StructTree_CountChildren
// Count the number of children for the structure tree.
// Parameters:
// struct_tree - Handle to the structure tree, as returned by
// FPDF_StructTree_LoadPage().
// Return value:
// The number of children, or -1 on error.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructTree_CountChildren(FPDF_STRUCTTREE struct_tree);
// Function: FPDF_StructTree_GetChildAtIndex
// Get a child in the structure tree.
// Parameters:
// struct_tree - Handle to the structure tree, as returned by
// FPDF_StructTree_LoadPage().
// index - The index for the child, 0-based.
// Return value:
// The child at the n-th index or NULL on error. The caller does not
// own the handle. The handle remains valid as long as |struct_tree|
// remains valid.
// Comments:
// The |index| must be less than the FPDF_StructTree_CountChildren()
// return value.
FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructTree_GetChildAtIndex(FPDF_STRUCTTREE struct_tree, int index);
// Function: FPDF_StructElement_GetAltText
// Get the alt text for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output the alt text. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the alt text, including the terminating NUL
// character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Experimental API.
// Function: FPDF_StructElement_GetActualText
// Get the actual text for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output the actual text. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the actual text, including the terminating
// NUL character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Function: FPDF_StructElement_GetID
// Get the ID for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output the ID string. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the ID string, including the terminating NUL
// character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Experimental API.
// Function: FPDF_StructElement_GetLang
// Get the case-insensitive IETF BCP 47 language code for an element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output the lang string. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the ID string, including the terminating NUL
// character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Experimental API.
// Function: FPDF_StructElement_GetStringAttribute
// Get a struct element attribute of type "name" or "string".
// Parameters:
// struct_element - Handle to the struct element.
// attr_name - The name of the attribute to retrieve.
// buffer - A buffer for output. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the attribute value, including the
// terminating NUL character. The number of bytes is returned
// regardless of the |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element,
FPDF_BYTESTRING attr_name,
void* buffer,
unsigned long buflen);
// Function: FPDF_StructElement_GetMarkedContentID
// Get the marked content ID for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// Return value:
// The marked content ID of the element. If no ID exists, returns
// -1.
// Comments:
// FPDF_StructElement_GetMarkedContentIdAtIndex() may be able to
// extract more marked content IDs out of |struct_element|. This API
// may be deprecated in the future.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element);
// Function: FPDF_StructElement_GetType
// Get the type (/S) for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the type, including the terminating NUL
// character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetType(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Experimental API.
// Function: FPDF_StructElement_GetObjType
// Get the object type (/Type) for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the object type, including the terminating
// NUL character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Function: FPDF_StructElement_GetTitle
// Get the title (/T) for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// buffer - A buffer for output. May be NULL.
// buflen - The length of the buffer, in bytes. May be 0.
// Return value:
// The number of bytes in the title, including the terminating NUL
// character. The number of bytes is returned regardless of the
// |buffer| and |buflen| parameters.
// Comments:
// Regardless of the platform, the |buffer| is always in UTF-16LE
// encoding. The string is terminated by a UTF16 NUL character. If
// |buflen| is less than the required length, or |buffer| is NULL,
// |buffer| will not be modified.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetTitle(FPDF_STRUCTELEMENT struct_element,
void* buffer,
unsigned long buflen);
// Function: FPDF_StructElement_CountChildren
// Count the number of children for the structure element.
// Parameters:
// struct_element - Handle to the struct element.
// Return value:
// The number of children, or -1 on error.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_CountChildren(FPDF_STRUCTELEMENT struct_element);
// Function: FPDF_StructElement_GetChildAtIndex
// Get a child in the structure element.
// Parameters:
// struct_element - Handle to the struct element.
// index - The index for the child, 0-based.
// Return value:
// The child at the n-th index or NULL on error.
// Comments:
// If the child exists but is not an element, then this function will
// return NULL. This will also return NULL for out of bounds indices.
// The |index| must be less than the FPDF_StructElement_CountChildren()
// return value.
FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element,
int index);
// Experimental API.
// Function: FPDF_StructElement_GetChildMarkedContentID
// Get the child's content id
// Parameters:
// struct_element - Handle to the struct element.
// index - The index for the child, 0-based.
// Return value:
// The marked content ID of the child. If no ID exists, returns -1.
// Comments:
// If the child exists but is not a stream or object, then this
// function will return -1. This will also return -1 for out of bounds
// indices. Compared to FPDF_StructElement_GetMarkedContentIdAtIndex,
// it is scoped to the current page.
// The |index| must be less than the FPDF_StructElement_CountChildren()
// return value.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetChildMarkedContentID(FPDF_STRUCTELEMENT struct_element,
int index);
// Experimental API.
// Function: FPDF_StructElement_GetParent
// Get the parent of the structure element.
// Parameters:
// struct_element - Handle to the struct element.
// Return value:
// The parent structure element or NULL on error.
// Comments:
// If structure element is StructTreeRoot, then this function will
// return NULL.
FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element);
// Function: FPDF_StructElement_GetAttributeCount
// Count the number of attributes for the structure element.
// Parameters:
// struct_element - Handle to the struct element.
// Return value:
// The number of attributes, or -1 on error.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element);
// Experimental API.
// Function: FPDF_StructElement_GetAttributeAtIndex
// Get an attribute object in the structure element.
// Parameters:
// struct_element - Handle to the struct element.
// index - The index for the attribute object, 0-based.
// Return value:
// The attribute object at the n-th index or NULL on error.
// Comments:
// If the attribute object exists but is not a dict, then this
// function will return NULL. This will also return NULL for out of
// bounds indices. The caller does not own the handle. The handle
// remains valid as long as |struct_element| remains valid.
// The |index| must be less than the
// FPDF_StructElement_GetAttributeCount() return value.
FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV
FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element, int index);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetCount
// Count the number of attributes in a structure element attribute map.
// Parameters:
// struct_attribute - Handle to the struct element attribute.
// Return value:
// The number of attributes, or -1 on error.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetName
// Get the name of an attribute in a structure element attribute map.
// Parameters:
// struct_attribute - Handle to the struct element attribute.
// index - The index of attribute in the map.
// buffer - A buffer for output. May be NULL. This is only
// modified if |buflen| is longer than the length
// of the key. Optional, pass null to just
// retrieve the size of the buffer needed.
// buflen - The length of the buffer.
// out_buflen - A pointer to variable that will receive the
// minimum buffer size to contain the key. Not
// filled if FALSE is returned.
// Return value:
// TRUE if the operation was successful, FALSE otherwise.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,
int index,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetValue
// Get a handle to a value for an attribute in a structure element
// attribute map.
// Parameters:
// struct_attribute - Handle to the struct element attribute.
// name - The attribute name.
// Return value:
// Returns a handle to the value associated with the input, if any.
// Returns NULL on failure. The caller does not own the handle.
// The handle remains valid as long as |struct_attribute| remains
// valid.
FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR_VALUE FPDF_CALLCONV
FPDF_StructElement_Attr_GetValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
FPDF_BYTESTRING name);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetType
// Get the type of an attribute in a structure element attribute map.
// Parameters:
// value - Handle to the value.
// Return value:
// Returns the type of the value, or FPDF_OBJECT_UNKNOWN in case of
// failure. Note that this will never return FPDF_OBJECT_REFERENCE, as
// references are always dereferenced.
FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR_VALUE value);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetBooleanValue
// Get the value of a boolean attribute in an attribute map as
// FPDF_BOOL. FPDF_StructElement_Attr_GetType() should have returned
// FPDF_OBJECT_BOOLEAN for this property.
// Parameters:
// value - Handle to the value.
// out_value - A pointer to variable that will receive the value. Not
// filled if false is returned.
// Return value:
// Returns TRUE if the attribute maps to a boolean value, FALSE
// otherwise.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetBooleanValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
FPDF_BOOL* out_value);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetNumberValue
// Get the value of a number attribute in an attribute map as float.
// FPDF_StructElement_Attr_GetType() should have returned
// FPDF_OBJECT_NUMBER for this property.
// Parameters:
// value - Handle to the value.
// out_value - A pointer to variable that will receive the value. Not
// filled if false is returned.
// Return value:
// Returns TRUE if the attribute maps to a number value, FALSE
// otherwise.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
float* out_value);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetStringValue
// Get the value of a string attribute in an attribute map as string.
// FPDF_StructElement_Attr_GetType() should have returned
// FPDF_OBJECT_STRING or FPDF_OBJECT_NAME for this property.
// Parameters:
// value - Handle to the value.
// buffer - A buffer for holding the returned key in UTF-16LE.
// This is only modified if |buflen| is longer than the
// length of the key. Optional, pass null to just
// retrieve the size of the buffer needed.
// buflen - The length of the buffer.
// out_buflen - A pointer to variable that will receive the minimum
// buffer size to contain the key. Not filled if FALSE is
// returned.
// Return value:
// Returns TRUE if the attribute maps to a string value, FALSE
// otherwise.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetBlobValue
// Get the value of a blob attribute in an attribute map as string.
// Parameters:
// value - Handle to the value.
// buffer - A buffer for holding the returned value. This is only
// modified if |buflen| is at least as long as the length
// of the value. Optional, pass null to just retrieve the
// size of the buffer needed.
// buflen - The length of the buffer.
// out_buflen - A pointer to variable that will receive the minimum
// buffer size to contain the key. Not filled if FALSE is
// returned.
// Return value:
// Returns TRUE if the attribute maps to a string value, FALSE
// otherwise.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen);
// Experimental API.
// Function: FPDF_StructElement_Attr_CountChildren
// Count the number of children values in an attribute.
// Parameters:
// value - Handle to the value.
// Return value:
// The number of children, or -1 on error.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_Attr_CountChildren(FPDF_STRUCTELEMENT_ATTR_VALUE value);
// Experimental API.
// Function: FPDF_StructElement_Attr_GetChildAtIndex
// Get a child from an attribute.
// Parameters:
// value - Handle to the value.
// index - The index for the child, 0-based.
// Return value:
// The child at the n-th index or NULL on error.
// Comments:
// The |index| must be less than the
// FPDF_StructElement_Attr_CountChildren() return value.
FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR_VALUE FPDF_CALLCONV
FPDF_StructElement_Attr_GetChildAtIndex(FPDF_STRUCTELEMENT_ATTR_VALUE value,
int index);
// Experimental API.
// Function: FPDF_StructElement_GetMarkedContentIdCount
// Get the count of marked content ids for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// Return value:
// The count of marked content ids or -1 if none exists.
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element);
// Experimental API.
// Function: FPDF_StructElement_GetMarkedContentIdAtIndex
// Get the marked content id at a given index for a given element.
// Parameters:
// struct_element - Handle to the struct element.
// index - The index of the marked content id, 0-based.
// Return value:
// The marked content ID of the element. If no ID exists, returns
// -1.
// Comments:
// The |index| must be less than the
// FPDF_StructElement_GetMarkedContentIdCount() return value.
// This will likely supersede FPDF_StructElement_GetMarkedContentID().
FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element,
int index);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // PUBLIC_FPDF_STRUCTTREE_H_

View file

@ -0,0 +1,317 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_SYSFONTINFO_H_
#define PUBLIC_FPDF_SYSFONTINFO_H_
#include <stddef.h>
// clang-format off
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
// Character sets for the font
#define FXFONT_ANSI_CHARSET 0
#define FXFONT_DEFAULT_CHARSET 1
#define FXFONT_SYMBOL_CHARSET 2
#define FXFONT_SHIFTJIS_CHARSET 128
#define FXFONT_HANGEUL_CHARSET 129
#define FXFONT_GB2312_CHARSET 134
#define FXFONT_CHINESEBIG5_CHARSET 136
#define FXFONT_GREEK_CHARSET 161
#define FXFONT_VIETNAMESE_CHARSET 163
#define FXFONT_HEBREW_CHARSET 177
#define FXFONT_ARABIC_CHARSET 178
#define FXFONT_CYRILLIC_CHARSET 204
#define FXFONT_THAI_CHARSET 222
#define FXFONT_EASTERNEUROPEAN_CHARSET 238
// Font pitch and family flags
#define FXFONT_FF_FIXEDPITCH (1 << 0)
#define FXFONT_FF_ROMAN (1 << 4)
#define FXFONT_FF_SCRIPT (4 << 4)
// Typical weight values
#define FXFONT_FW_NORMAL 400
#define FXFONT_FW_BOLD 700
// Exported Functions
#ifdef __cplusplus
extern "C" {
#endif
// Interface: FPDF_SYSFONTINFO
// Interface for getting system font information and font mapping
typedef struct _FPDF_SYSFONTINFO {
// Version number of the interface. Currently must be 1.
int version;
// Method: Release
// Give implementation a chance to release any data after the
// interface is no longer used.
// Interface Version:
// 1
// Implementation Required:
// No
// Parameters:
// pThis - Pointer to the interface structure itself
// Return Value:
// None
// Comments:
// Called by PDFium during the final cleanup process.
void (*Release)(struct _FPDF_SYSFONTINFO* pThis);
// Method: EnumFonts
// Enumerate all fonts installed on the system
// Interface Version:
// 1
// Implementation Required:
// No
// Parameters:
// pThis - Pointer to the interface structure itself
// pMapper - An opaque pointer to internal font mapper, used
// when calling FPDF_AddInstalledFont().
// Return Value:
// None
// Comments:
// Implementations should call FPDF_AddInstalledFont() function for
// each font found. Only TrueType/OpenType and Type1 fonts are
// accepted by PDFium.
void (*EnumFonts)(struct _FPDF_SYSFONTINFO* pThis, void* pMapper);
// Method: MapFont
// Use the system font mapper to get a font handle from requested
// parameters.
// Interface Version:
// 1
// Implementation Required:
// Required if GetFont method is not implemented.
// Parameters:
// pThis - Pointer to the interface structure itself
// weight - Weight of the requested font. 400 is normal and
// 700 is bold.
// bItalic - Italic option of the requested font, TRUE or
// FALSE.
// charset - Character set identifier for the requested font.
// See above defined constants.
// pitch_family - A combination of flags. See above defined
// constants.
// face - Typeface name. Currently use system local encoding
// only.
// bExact - Obsolete: this parameter is now ignored.
// Return Value:
// An opaque pointer for font handle, or NULL if system mapping is
// not supported.
// Comments:
// If the system supports native font mapper (like Windows),
// implementation can implement this method to get a font handle.
// Otherwise, PDFium will do the mapping and then call GetFont
// method. Only TrueType/OpenType and Type1 fonts are accepted
// by PDFium.
void* (*MapFont)(struct _FPDF_SYSFONTINFO* pThis,
int weight,
FPDF_BOOL bItalic,
int charset,
int pitch_family,
const char* face,
FPDF_BOOL* bExact);
// Method: GetFont
// Get a handle to a particular font by its internal ID
// Interface Version:
// 1
// Implementation Required:
// Required if MapFont method is not implemented.
// Return Value:
// An opaque pointer for font handle.
// Parameters:
// pThis - Pointer to the interface structure itself
// face - Typeface name in system local encoding.
// Comments:
// If the system mapping not supported, PDFium will do the font
// mapping and use this method to get a font handle.
void* (*GetFont)(struct _FPDF_SYSFONTINFO* pThis, const char* face);
// Method: GetFontData
// Get font data from a font
// Interface Version:
// 1
// Implementation Required:
// Yes
// Parameters:
// pThis - Pointer to the interface structure itself
// hFont - Font handle returned by MapFont or GetFont method
// table - TrueType/OpenType table identifier (refer to
// TrueType specification), or 0 for the whole file.
// buffer - The buffer receiving the font data. Can be NULL if
// not provided.
// buf_size - Buffer size, can be zero if not provided.
// Return Value:
// Number of bytes needed, if buffer not provided or not large
// enough, or number of bytes written into buffer otherwise.
// Comments:
// Can read either the full font file, or a particular
// TrueType/OpenType table.
unsigned long (*GetFontData)(struct _FPDF_SYSFONTINFO* pThis,
void* hFont,
unsigned int table,
unsigned char* buffer,
unsigned long buf_size);
// Method: GetFaceName
// Get face name from a font handle
// Interface Version:
// 1
// Implementation Required:
// No
// Parameters:
// pThis - Pointer to the interface structure itself
// hFont - Font handle returned by MapFont or GetFont method
// buffer - The buffer receiving the face name. Can be NULL if
// not provided
// buf_size - Buffer size, can be zero if not provided
// Return Value:
// Number of bytes needed, if buffer not provided or not large
// enough, or number of bytes written into buffer otherwise.
unsigned long (*GetFaceName)(struct _FPDF_SYSFONTINFO* pThis,
void* hFont,
char* buffer,
unsigned long buf_size);
// Method: GetFontCharset
// Get character set information for a font handle
// Interface Version:
// 1
// Implementation Required:
// No
// Parameters:
// pThis - Pointer to the interface structure itself
// hFont - Font handle returned by MapFont or GetFont method
// Return Value:
// Character set identifier. See defined constants above.
int (*GetFontCharset)(struct _FPDF_SYSFONTINFO* pThis, void* hFont);
// Method: DeleteFont
// Delete a font handle
// Interface Version:
// 1
// Implementation Required:
// Yes
// Parameters:
// pThis - Pointer to the interface structure itself
// hFont - Font handle returned by MapFont or GetFont method
// Return Value:
// None
void (*DeleteFont)(struct _FPDF_SYSFONTINFO* pThis, void* hFont);
} FPDF_SYSFONTINFO;
// Struct: FPDF_CharsetFontMap
// Provides the name of a font to use for a given charset value.
typedef struct FPDF_CharsetFontMap_ {
int charset; // Character Set Enum value, see FXFONT_*_CHARSET above.
const char* fontname; // Name of default font to use with that charset.
} FPDF_CharsetFontMap;
// Function: FPDF_GetDefaultTTFMap
// Returns a pointer to the default character set to TT Font name map. The
// map is an array of FPDF_CharsetFontMap structs, with its end indicated
// by a { -1, NULL } entry.
// Parameters:
// None.
// Return Value:
// Pointer to the Charset Font Map.
// Note:
// Once FPDF_GetDefaultTTFMapCount() and FPDF_GetDefaultTTFMapEntry() are no
// longer experimental, this API will be marked as deprecated.
// See https://crbug.com/348468114
FPDF_EXPORT const FPDF_CharsetFontMap* FPDF_CALLCONV FPDF_GetDefaultTTFMap();
// Experimental API.
//
// Function: FPDF_GetDefaultTTFMapCount
// Returns the number of entries in the default character set to TT Font name
// map.
// Parameters:
// None.
// Return Value:
// The number of entries in the map.
FPDF_EXPORT size_t FPDF_CALLCONV FPDF_GetDefaultTTFMapCount();
// Experimental API.
//
// Function: FPDF_GetDefaultTTFMapEntry
// Returns an entry in the default character set to TT Font name map.
// Parameters:
// index - The index to the entry in the map to retrieve.
// Return Value:
// A pointer to the entry, if it is in the map, or NULL if the index is out
// of bounds.
FPDF_EXPORT const FPDF_CharsetFontMap* FPDF_CALLCONV
FPDF_GetDefaultTTFMapEntry(size_t index);
// Function: FPDF_AddInstalledFont
// Add a system font to the list in PDFium.
// Comments:
// This function is only called during the system font list building
// process.
// Parameters:
// mapper - Opaque pointer to Foxit font mapper
// face - The font face name
// charset - Font character set. See above defined constants.
// Return Value:
// None.
FPDF_EXPORT void FPDF_CALLCONV FPDF_AddInstalledFont(void* mapper,
const char* face,
int charset);
// Function: FPDF_SetSystemFontInfo
// Set the system font info interface into PDFium
// Parameters:
// pFontInfo - Pointer to a FPDF_SYSFONTINFO structure
// Return Value:
// None
// Comments:
// Platform support implementation should implement required methods of
// FFDF_SYSFONTINFO interface, then call this function during PDFium
// initialization process.
//
// Call this with NULL to tell PDFium to stop using a previously set
// |FPDF_SYSFONTINFO|.
FPDF_EXPORT void FPDF_CALLCONV
FPDF_SetSystemFontInfo(FPDF_SYSFONTINFO* pFontInfo);
// Function: FPDF_GetDefaultSystemFontInfo
// Get default system font info interface for current platform
// Parameters:
// None
// Return Value:
// Pointer to a FPDF_SYSFONTINFO structure describing the default
// interface, or NULL if the platform doesn't have a default interface.
// Application should call FPDF_FreeDefaultSystemFontInfo to free the
// returned pointer.
// Comments:
// For some platforms, PDFium implements a default version of system
// font info interface. The default implementation can be passed to
// FPDF_SetSystemFontInfo().
FPDF_EXPORT FPDF_SYSFONTINFO* FPDF_CALLCONV FPDF_GetDefaultSystemFontInfo();
// Function: FPDF_FreeDefaultSystemFontInfo
// Free a default system font info interface
// Parameters:
// pFontInfo - Pointer to a FPDF_SYSFONTINFO structure
// Return Value:
// None
// Comments:
// This function should be called on the output from
// FPDF_GetDefaultSystemFontInfo() once it is no longer needed.
FPDF_EXPORT void FPDF_CALLCONV
FPDF_FreeDefaultSystemFontInfo(FPDF_SYSFONTINFO* pFontInfo);
#ifdef __cplusplus
}
#endif
#endif // PUBLIC_FPDF_SYSFONTINFO_H_

View file

@ -0,0 +1,685 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_TEXT_H_
#define PUBLIC_FPDF_TEXT_H_
// clang-format off
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
// Exported Functions
#ifdef __cplusplus
extern "C" {
#endif
// Function: FPDFText_LoadPage
// Prepare information about all characters in a page.
// Parameters:
// page - Handle to the page. Returned by FPDF_LoadPage function
// (in FPDFVIEW module).
// Return value:
// A handle to the text page information structure.
// NULL if something goes wrong.
// Comments:
// Application must call FPDFText_ClosePage to release the text page
// information.
//
FPDF_EXPORT FPDF_TEXTPAGE FPDF_CALLCONV FPDFText_LoadPage(FPDF_PAGE page);
// Function: FPDFText_ClosePage
// Release all resources allocated for a text page information
// structure.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// Return Value:
// None.
//
FPDF_EXPORT void FPDF_CALLCONV FPDFText_ClosePage(FPDF_TEXTPAGE text_page);
// Function: FPDFText_CountChars
// Get number of characters in a page.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// Return value:
// Number of characters in the page. Return -1 for error.
// Generated characters, like additional space characters, new line
// characters, are also counted.
// Comments:
// Characters in a page form a "stream", inside the stream, each
// character has an index.
// We will use the index parameters in many of FPDFTEXT functions. The
// first character in the page
// has an index value of zero.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page);
// Function: FPDFText_GetUnicode
// Get Unicode of a character in a page.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// The Unicode of the particular character.
// If a character is not encoded in Unicode and Foxit engine can't
// convert to Unicode,
// the return value will be zero.
//
FPDF_EXPORT unsigned int FPDF_CALLCONV
FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index);
// Experimental API.
// Function: FPDFText_GetTextObject
// Get the FPDF_PAGEOBJECT associated with a given character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// The associated text object for the character at |index|, or NULL on
// error. The returned text object, if non-null, is of type
// |FPDF_PAGEOBJ_TEXT|. The caller does not own the returned object.
//
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFText_GetTextObject(FPDF_TEXTPAGE text_page, int index);
// Experimental API.
// Function: FPDFText_IsGenerated
// Get if a character in a page is generated by PDFium.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// 1 if the character is generated by PDFium.
// 0 if the character is not generated by PDFium.
// -1 if there was an error.
//
FPDF_EXPORT int FPDF_CALLCONV
FPDFText_IsGenerated(FPDF_TEXTPAGE text_page, int index);
// Experimental API.
// Function: FPDFText_IsHyphen
// Get if a character in a page is a hyphen.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// 1 if the character is a hyphen.
// 0 if the character is not a hyphen.
// -1 if there was an error.
//
FPDF_EXPORT int FPDF_CALLCONV
FPDFText_IsHyphen(FPDF_TEXTPAGE text_page, int index);
// Experimental API.
// Function: FPDFText_HasUnicodeMapError
// Get if a character in a page has an invalid unicode mapping.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// 1 if the character has an invalid unicode mapping.
// 0 if the character has no known unicode mapping issues.
// -1 if there was an error.
//
FPDF_EXPORT int FPDF_CALLCONV
FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page, int index);
// Function: FPDFText_GetFontSize
// Get the font size of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// The font size of the particular character, measured in points (about
// 1/72 inch). This is the typographic size of the font (so called
// "em size").
//
FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,
int index);
// Experimental API.
// Function: FPDFText_GetFontInfo
// Get the font name and flags of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// buffer - A buffer receiving the font name.
// buflen - The length of |buffer| in bytes.
// flags - Optional pointer to an int receiving the font flags.
// These flags should be interpreted per PDF spec 1.7
// Section 5.7.1 Font Descriptor Flags.
// Return value:
// On success, return the length of the font name, including the
// trailing NUL character, in bytes. If this length is less than or
// equal to |length|, |buffer| is set to the font name, |flags| is
// set to the font flags. |buffer| is in UTF-8 encoding. Return 0 on
// failure.
//
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFText_GetFontInfo(FPDF_TEXTPAGE text_page,
int index,
void* buffer,
unsigned long buflen,
int* flags);
// Experimental API.
// Function: FPDFText_GetFontWeight
// Get the font weight of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return value:
// On success, return the font weight of the particular character. If
// |text_page| is invalid, if |index| is out of bounds, or if the
// character's text object is undefined, return -1.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetFontWeight(FPDF_TEXTPAGE text_page,
int index);
// Experimental API.
// Function: FPDFText_GetFillColor
// Get the fill color of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// R - Pointer to an unsigned int number receiving the
// red value of the fill color.
// G - Pointer to an unsigned int number receiving the
// green value of the fill color.
// B - Pointer to an unsigned int number receiving the
// blue value of the fill color.
// A - Pointer to an unsigned int number receiving the
// alpha value of the fill color.
// Return value:
// Whether the call succeeded. If false, |R|, |G|, |B| and |A| are
// unchanged.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,
int index,
unsigned int* R,
unsigned int* G,
unsigned int* B,
unsigned int* A);
// Experimental API.
// Function: FPDFText_GetStrokeColor
// Get the stroke color of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// R - Pointer to an unsigned int number receiving the
// red value of the stroke color.
// G - Pointer to an unsigned int number receiving the
// green value of the stroke color.
// B - Pointer to an unsigned int number receiving the
// blue value of the stroke color.
// A - Pointer to an unsigned int number receiving the
// alpha value of the stroke color.
// Return value:
// Whether the call succeeded. If false, |R|, |G|, |B| and |A| are
// unchanged.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,
int index,
unsigned int* R,
unsigned int* G,
unsigned int* B,
unsigned int* A);
// Experimental API.
// Function: FPDFText_GetCharAngle
// Get character rotation angle.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// Return Value:
// On success, return the angle value in radian. Value will always be
// greater or equal to 0. If |text_page| is invalid, or if |index| is
// out of bounds, then return -1.
//
FPDF_EXPORT float FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
int index);
// Function: FPDFText_GetCharBox
// Get bounding box of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// left - Pointer to a double number receiving left position
// of the character box.
// right - Pointer to a double number receiving right position
// of the character box.
// bottom - Pointer to a double number receiving bottom position
// of the character box.
// top - Pointer to a double number receiving top position of
// the character box.
// Return Value:
// On success, return TRUE and fill in |left|, |right|, |bottom|, and
// |top|. If |text_page| is invalid, or if |index| is out of bounds,
// then return FALSE, and the out parameters remain unmodified.
// Comments:
// All positions are measured in PDF "user space".
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetCharBox(FPDF_TEXTPAGE text_page,
int index,
double* left,
double* right,
double* bottom,
double* top);
// Experimental API.
// Function: FPDFText_GetLooseCharBox
// Get a "loose" bounding box of a particular character, i.e., covering
// the entire glyph bounds, without taking the actual glyph shape into
// account.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// rect - Pointer to a FS_RECTF receiving the character box.
// Return Value:
// On success, return TRUE and fill in |rect|. If |text_page| is
// invalid, or if |index| is out of bounds, then return FALSE, and the
// |rect| out parameter remains unmodified.
// Comments:
// All positions are measured in PDF "user space".
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetLooseCharBox(FPDF_TEXTPAGE text_page, int index, FS_RECTF* rect);
// Experimental API.
// Function: FPDFText_GetMatrix
// Get the effective transformation matrix for a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage().
// index - Zero-based index of the character.
// matrix - Pointer to a FS_MATRIX receiving the transformation
// matrix.
// Return Value:
// On success, return TRUE and fill in |matrix|. If |text_page| is
// invalid, or if |index| is out of bounds, or if |matrix| is NULL,
// then return FALSE, and |matrix| remains unmodified.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetMatrix(FPDF_TEXTPAGE text_page,
int index,
FS_MATRIX* matrix);
// Function: FPDFText_GetCharOrigin
// Get origin of a particular character.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// index - Zero-based index of the character.
// x - Pointer to a double number receiving x coordinate of
// the character origin.
// y - Pointer to a double number receiving y coordinate of
// the character origin.
// Return Value:
// Whether the call succeeded. If false, x and y are unchanged.
// Comments:
// All positions are measured in PDF "user space".
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetCharOrigin(FPDF_TEXTPAGE text_page,
int index,
double* x,
double* y);
// Function: FPDFText_GetCharIndexAtPos
// Get the index of a character at or nearby a certain position on the
// page.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// x - X position in PDF "user space".
// y - Y position in PDF "user space".
// xTolerance - An x-axis tolerance value for character hit
// detection, in point units.
// yTolerance - A y-axis tolerance value for character hit
// detection, in point units.
// Return Value:
// The zero-based index of the character at, or nearby the point (x,y).
// If there is no character at or nearby the point, return value will
// be -1. If an error occurs, -3 will be returned.
//
FPDF_EXPORT int FPDF_CALLCONV
FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page,
double x,
double y,
double xTolerance,
double yTolerance);
// Function: FPDFText_GetText
// Extract unicode text string from the page.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// start_index - Index for the start characters.
// count - Number of UCS-2 values to be extracted.
// result - A buffer (allocated by application) receiving the
// extracted UCS-2 values. The buffer must be able to
// hold `count` UCS-2 values plus a terminator.
// Return Value:
// Number of characters written into the result buffer, including the
// trailing terminator.
// Comments:
// This function ignores characters without UCS-2 representations.
// It considers all characters on the page, even those that are not
// visible when the page has a cropbox. To filter out the characters
// outside of the cropbox, use FPDF_GetPageBoundingBox() and
// FPDFText_GetCharBox().
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE text_page,
int start_index,
int count,
unsigned short* result);
// Function: FPDFText_CountRects
// Counts number of rectangular areas occupied by a segment of text,
// and caches the result for subsequent FPDFText_GetRect() calls.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// start_index - Index for the start character.
// count - Number of characters, or -1 for all remaining.
// Return value:
// Number of rectangles, 0 if text_page is null, or -1 on bad
// start_index.
// Comments:
// This function, along with FPDFText_GetRect can be used by
// applications to detect the position on the page for a text segment,
// so proper areas can be highlighted. The FPDFText_* functions will
// automatically merge small character boxes into bigger one if those
// characters are on the same line and use same font settings.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
int start_index,
int count);
// Function: FPDFText_GetRect
// Get a rectangular area from the result generated by
// FPDFText_CountRects.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// rect_index - Zero-based index for the rectangle.
// left - Pointer to a double value receiving the rectangle
// left boundary.
// top - Pointer to a double value receiving the rectangle
// top boundary.
// right - Pointer to a double value receiving the rectangle
// right boundary.
// bottom - Pointer to a double value receiving the rectangle
// bottom boundary.
// Return Value:
// On success, return TRUE and fill in |left|, |top|, |right|, and
// |bottom|. If |text_page| is invalid then return FALSE, and the out
// parameters remain unmodified. If |text_page| is valid but
// |rect_index| is out of bounds, then return FALSE and set the out
// parameters to 0.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page,
int rect_index,
double* left,
double* top,
double* right,
double* bottom);
// Function: FPDFText_GetBoundedText
// Extract unicode text within a rectangular boundary on the page.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// left - Left boundary.
// top - Top boundary.
// right - Right boundary.
// bottom - Bottom boundary.
// buffer - Caller-allocated buffer to receive UTF-16 values.
// buflen - Number of UTF-16 values (not bytes) that `buffer`
// is capable of holding.
// Return Value:
// If buffer is NULL or buflen is zero, return number of UTF-16
// values (not bytes) of text present within the rectangle, excluding
// a terminating NUL. Generally you should pass a buffer at least one
// larger than this if you want a terminating NUL, which will be
// provided if space is available. Otherwise, return number of UTF-16
// values copied into the buffer, including the terminating NUL when
// space for it is available.
// Comment:
// If the buffer is too small, as much text as will fit is copied into
// it. May return a split surrogate in that case.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page,
double left,
double top,
double right,
double bottom,
unsigned short* buffer,
int buflen);
// Flags used by FPDFText_FindStart function.
//
// If not set, it will not match case by default.
#define FPDF_MATCHCASE 0x00000001
// If not set, it will not match the whole word by default.
#define FPDF_MATCHWHOLEWORD 0x00000002
// If not set, it will skip past the current match to look for the next match.
#define FPDF_CONSECUTIVE 0x00000004
// Function: FPDFText_FindStart
// Start a search.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// findwhat - A unicode match pattern.
// flags - Option flags.
// start_index - Start from this character. -1 for end of the page.
// Return Value:
// A handle for the search context. FPDFText_FindClose must be called
// to release this handle.
//
FPDF_EXPORT FPDF_SCHHANDLE FPDF_CALLCONV
FPDFText_FindStart(FPDF_TEXTPAGE text_page,
FPDF_WIDESTRING findwhat,
unsigned long flags,
int start_index);
// Function: FPDFText_FindNext
// Search in the direction from page start to end.
// Parameters:
// handle - A search context handle returned by
// FPDFText_FindStart.
// Return Value:
// Whether a match is found.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindNext(FPDF_SCHHANDLE handle);
// Function: FPDFText_FindPrev
// Search in the direction from page end to start.
// Parameters:
// handle - A search context handle returned by
// FPDFText_FindStart.
// Return Value:
// Whether a match is found.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindPrev(FPDF_SCHHANDLE handle);
// Function: FPDFText_GetSchResultIndex
// Get the starting character index of the search result.
// Parameters:
// handle - A search context handle returned by
// FPDFText_FindStart.
// Return Value:
// Index for the starting character.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetSchResultIndex(FPDF_SCHHANDLE handle);
// Function: FPDFText_GetSchCount
// Get the number of matched characters in the search result.
// Parameters:
// handle - A search context handle returned by
// FPDFText_FindStart.
// Return Value:
// Number of matched characters.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetSchCount(FPDF_SCHHANDLE handle);
// Function: FPDFText_FindClose
// Release a search context.
// Parameters:
// handle - A search context handle returned by
// FPDFText_FindStart.
// Return Value:
// None.
//
FPDF_EXPORT void FPDF_CALLCONV FPDFText_FindClose(FPDF_SCHHANDLE handle);
// Function: FPDFLink_LoadWebLinks
// Prepare information about weblinks in a page.
// Parameters:
// text_page - Handle to a text page information structure.
// Returned by FPDFText_LoadPage function.
// Return Value:
// A handle to the page's links information structure, or
// NULL if something goes wrong.
// Comments:
// Weblinks are those links implicitly embedded in PDF pages. PDF also
// has a type of annotation called "link" (FPDFTEXT doesn't deal with
// that kind of link). FPDFTEXT weblink feature is useful for
// automatically detecting links in the page contents. For aryan,
// things like "https://www.example.com" will be detected, so
// applications can allow user to click on those characters to activate
// the link, even the PDF doesn't come with link annotations.
//
// FPDFLink_CloseWebLinks must be called to release resources.
//
FPDF_EXPORT FPDF_PAGELINK FPDF_CALLCONV
FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page);
// Function: FPDFLink_CountWebLinks
// Count number of detected web links.
// Parameters:
// link_page - Handle returned by FPDFLink_LoadWebLinks.
// Return Value:
// Number of detected web links.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountWebLinks(FPDF_PAGELINK link_page);
// Function: FPDFLink_GetURL
// Fetch the URL information for a detected web link.
// Parameters:
// link_page - Handle returned by FPDFLink_LoadWebLinks.
// link_index - Zero-based index for the link.
// buffer - A unicode buffer for the result.
// buflen - Number of 16-bit code units (not bytes) for the
// buffer, including an additional terminator.
// Return Value:
// If |buffer| is NULL or |buflen| is zero, return the number of 16-bit
// code units (not bytes) needed to buffer the result (an additional
// terminator is included in this count).
// Otherwise, copy the result into |buffer|, truncating at |buflen| if
// the result is too large to fit, and return the number of 16-bit code
// units actually copied into the buffer (the additional terminator is
// also included in this count).
// If |link_index| does not correspond to a valid link, then the result
// is an empty string.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetURL(FPDF_PAGELINK link_page,
int link_index,
unsigned short* buffer,
int buflen);
// Function: FPDFLink_CountRects
// Count number of rectangular areas for the link.
// Parameters:
// link_page - Handle returned by FPDFLink_LoadWebLinks.
// link_index - Zero-based index for the link.
// Return Value:
// Number of rectangular areas for the link. If |link_index| does
// not correspond to a valid link, then 0 is returned.
//
FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountRects(FPDF_PAGELINK link_page,
int link_index);
// Function: FPDFLink_GetRect
// Fetch the boundaries of a rectangle for a link.
// Parameters:
// link_page - Handle returned by FPDFLink_LoadWebLinks.
// link_index - Zero-based index for the link.
// rect_index - Zero-based index for a rectangle.
// left - Pointer to a double value receiving the rectangle
// left boundary.
// top - Pointer to a double value receiving the rectangle
// top boundary.
// right - Pointer to a double value receiving the rectangle
// right boundary.
// bottom - Pointer to a double value receiving the rectangle
// bottom boundary.
// Return Value:
// On success, return TRUE and fill in |left|, |top|, |right|, and
// |bottom|. If |link_page| is invalid or if |link_index| does not
// correspond to a valid link, then return FALSE, and the out
// parameters remain unmodified.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page,
int link_index,
int rect_index,
double* left,
double* top,
double* right,
double* bottom);
// Experimental API.
// Function: FPDFLink_GetTextRange
// Fetch the start char index and char count for a link.
// Parameters:
// link_page - Handle returned by FPDFLink_LoadWebLinks.
// link_index - Zero-based index for the link.
// start_char_index - pointer to int receiving the start char index
// char_count - pointer to int receiving the char count
// Return Value:
// On success, return TRUE and fill in |start_char_index| and
// |char_count|. if |link_page| is invalid or if |link_index| does
// not correspond to a valid link, then return FALSE and the out
// parameters remain unmodified.
//
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFLink_GetTextRange(FPDF_PAGELINK link_page,
int link_index,
int* start_char_index,
int* char_count);
// Function: FPDFLink_CloseWebLinks
// Release resources used by weblink feature.
// Parameters:
// link_page - Handle returned by FPDFLink_LoadWebLinks.
// Return Value:
// None.
//
FPDF_EXPORT void FPDF_CALLCONV FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page);
#ifdef __cplusplus
}
#endif
#endif // PUBLIC_FPDF_TEXT_H_

View file

@ -0,0 +1,59 @@
// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PUBLIC_FPDF_THUMBNAIL_H_
#define PUBLIC_FPDF_THUMBNAIL_H_
#include <stdint.h>
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif
// Experimental API.
// Gets the decoded data from the thumbnail of |page| if it exists.
// This only modifies |buffer| if |buflen| less than or equal to the
// size of the decoded data. Returns the size of the decoded
// data or 0 if thumbnail DNE. Optional, pass null to just retrieve
// the size of the buffer needed.
//
// page - handle to a page.
// buffer - buffer for holding the decoded image data.
// buflen - length of the buffer in bytes.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFPage_GetDecodedThumbnailData(FPDF_PAGE page,
void* buffer,
unsigned long buflen);
// Experimental API.
// Gets the raw data from the thumbnail of |page| if it exists.
// This only modifies |buffer| if |buflen| is less than or equal to
// the size of the raw data. Returns the size of the raw data or 0
// if thumbnail DNE. Optional, pass null to just retrieve the size
// of the buffer needed.
//
// page - handle to a page.
// buffer - buffer for holding the raw image data.
// buflen - length of the buffer in bytes.
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFPage_GetRawThumbnailData(FPDF_PAGE page,
void* buffer,
unsigned long buflen);
// Experimental API.
// Returns the thumbnail of |page| as a FPDF_BITMAP. Returns a nullptr
// if unable to access the thumbnail's stream.
//
// page - handle to a page.
FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
FPDFPage_GetThumbnailAsBitmap(FPDF_PAGE page);
#ifdef __cplusplus
}
#endif
#endif // PUBLIC_FPDF_THUMBNAIL_H_

View file

@ -0,0 +1,308 @@
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#ifndef PUBLIC_FPDF_TRANSFORMPAGE_H_
#define PUBLIC_FPDF_TRANSFORMPAGE_H_
// NOLINTNEXTLINE(build/include)
#include "fpdfview.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Set "MediaBox" entry to the page dictionary.
*
* page - Handle to a page.
* left - The left of the rectangle.
* bottom - The bottom of the rectangle.
* right - The right of the rectangle.
* top - The top of the rectangle.
*/
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page,
float left,
float bottom,
float right,
float top);
/**
* Set "CropBox" entry to the page dictionary.
*
* page - Handle to a page.
* left - The left of the rectangle.
* bottom - The bottom of the rectangle.
* right - The right of the rectangle.
* top - The top of the rectangle.
*/
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page,
float left,
float bottom,
float right,
float top);
/**
* Set "BleedBox" entry to the page dictionary.
*
* page - Handle to a page.
* left - The left of the rectangle.
* bottom - The bottom of the rectangle.
* right - The right of the rectangle.
* top - The top of the rectangle.
*/
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetBleedBox(FPDF_PAGE page,
float left,
float bottom,
float right,
float top);
/**
* Set "TrimBox" entry to the page dictionary.
*
* page - Handle to a page.
* left - The left of the rectangle.
* bottom - The bottom of the rectangle.
* right - The right of the rectangle.
* top - The top of the rectangle.
*/
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetTrimBox(FPDF_PAGE page,
float left,
float bottom,
float right,
float top);
/**
* Set "ArtBox" entry to the page dictionary.
*
* page - Handle to a page.
* left - The left of the rectangle.
* bottom - The bottom of the rectangle.
* right - The right of the rectangle.
* top - The top of the rectangle.
*/
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetArtBox(FPDF_PAGE page,
float left,
float bottom,
float right,
float top);
/**
* Get "MediaBox" entry from the page dictionary.
*
* page - Handle to a page.
* left - Pointer to a float value receiving the left of the rectangle.
* bottom - Pointer to a float value receiving the bottom of the rectangle.
* right - Pointer to a float value receiving the right of the rectangle.
* top - Pointer to a float value receiving the top of the rectangle.
*
* On success, return true and write to the out parameters. Otherwise return
* false and leave the out parameters unmodified.
*/
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page,
float* left,
float* bottom,
float* right,
float* top);
/**
* Get "CropBox" entry from the page dictionary.
*
* page - Handle to a page.
* left - Pointer to a float value receiving the left of the rectangle.
* bottom - Pointer to a float value receiving the bottom of the rectangle.
* right - Pointer to a float value receiving the right of the rectangle.
* top - Pointer to a float value receiving the top of the rectangle.
*
* On success, return true and write to the out parameters. Otherwise return
* false and leave the out parameters unmodified.
*/
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page,
float* left,
float* bottom,
float* right,
float* top);
/**
* Get "BleedBox" entry from the page dictionary.
*
* page - Handle to a page.
* left - Pointer to a float value receiving the left of the rectangle.
* bottom - Pointer to a float value receiving the bottom of the rectangle.
* right - Pointer to a float value receiving the right of the rectangle.
* top - Pointer to a float value receiving the top of the rectangle.
*
* On success, return true and write to the out parameters. Otherwise return
* false and leave the out parameters unmodified.
*/
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetBleedBox(FPDF_PAGE page,
float* left,
float* bottom,
float* right,
float* top);
/**
* Get "TrimBox" entry from the page dictionary.
*
* page - Handle to a page.
* left - Pointer to a float value receiving the left of the rectangle.
* bottom - Pointer to a float value receiving the bottom of the rectangle.
* right - Pointer to a float value receiving the right of the rectangle.
* top - Pointer to a float value receiving the top of the rectangle.
*
* On success, return true and write to the out parameters. Otherwise return
* false and leave the out parameters unmodified.
*/
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetTrimBox(FPDF_PAGE page,
float* left,
float* bottom,
float* right,
float* top);
/**
* Get "ArtBox" entry from the page dictionary.
*
* page - Handle to a page.
* left - Pointer to a float value receiving the left of the rectangle.
* bottom - Pointer to a float value receiving the bottom of the rectangle.
* right - Pointer to a float value receiving the right of the rectangle.
* top - Pointer to a float value receiving the top of the rectangle.
*
* On success, return true and write to the out parameters. Otherwise return
* false and leave the out parameters unmodified.
*/
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetArtBox(FPDF_PAGE page,
float* left,
float* bottom,
float* right,
float* top);
/**
* Apply transforms to |page|.
*
* If |matrix| is provided it will be applied to transform the page.
* If |clipRect| is provided it will be used to clip the resulting page.
* If neither |matrix| or |clipRect| are provided this method returns |false|.
* Returns |true| if transforms are applied.
*
* This function will transform the whole page, and would take effect to all the
* objects in the page.
*
* page - Page handle.
* matrix - Transform matrix.
* clipRect - Clipping rectangle.
*/
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPage_TransFormWithClip(FPDF_PAGE page,
const FS_MATRIX* matrix,
const FS_RECTF* clipRect);
/**
* Transform (scale, rotate, shear, move) the clip path of page object.
* page_object - Handle to a page object. Returned by
* FPDFPageObj_NewImageObj().
*
* a - The coefficient "a" of the matrix.
* b - The coefficient "b" of the matrix.
* c - The coefficient "c" of the matrix.
* d - The coefficient "d" of the matrix.
* e - The coefficient "e" of the matrix.
* f - The coefficient "f" of the matrix.
*/
FPDF_EXPORT void FPDF_CALLCONV
FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
double a,
double b,
double c,
double d,
double e,
double f);
// Experimental API.
// Get the clip path of the page object.
//
// page object - Handle to a page object. Returned by e.g.
// FPDFPage_GetObject().
//
// Returns the handle to the clip path, or NULL on failure. The caller does not
// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until
// FPDF_ClosePage() is called for the page containing |page_object|.
FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object);
// Experimental API.
// Get number of paths inside |clip_path|.
//
// clip_path - handle to a clip_path.
//
// Returns the number of objects in |clip_path| or -1 on failure.
FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path);
// Experimental API.
// Get number of segments inside one path of |clip_path|.
//
// clip_path - handle to a clip_path.
// path_index - index into the array of paths of the clip path.
//
// Returns the number of segments or -1 on failure.
FPDF_EXPORT int FPDF_CALLCONV
FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index);
// Experimental API.
// Get segment in one specific path of |clip_path| at index.
//
// clip_path - handle to a clip_path.
// path_index - the index of a path.
// segment_index - the index of a segment.
//
// Returns the handle to the segment, or NULL on failure. The caller does not
// take ownership of the returned FPDF_PATHSEGMENT. Instead, it remains valid
// until FPDF_ClosePage() is called for the page containing |clip_path|.
FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
int path_index,
int segment_index);
/**
* Create a new clip path, with a rectangle inserted.
*
* Caller takes ownership of the returned FPDF_CLIPPATH. It should be freed with
* FPDF_DestroyClipPath().
*
* left - The left of the clip box.
* bottom - The bottom of the clip box.
* right - The right of the clip box.
* top - The top of the clip box.
*/
FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left,
float bottom,
float right,
float top);
/**
* Destroy the clip path.
*
* clipPath - A handle to the clip path. It will be invalid after this call.
*/
FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath);
/**
* Clip the page content, the page content that outside the clipping region
* become invisible.
*
* A clip path will be inserted before the page content stream or content array.
* In this way, the page content will be clipped by this clip path.
*
* page - A page handle.
* clipPath - A handle to the clip path. (Does not take ownership.)
*/
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page,
FPDF_CLIPPATH clipPath);
#ifdef __cplusplus
}
#endif
#endif // PUBLIC_FPDF_TRANSFORMPAGE_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.
*/
#ifndef ANDROID_ERRORS_H
#define ANDROID_ERRORS_H
#include <sys/types.h>
#include <errno.h>
namespace android {
// use this type to return error codes
#ifdef HAVE_MS_C_RUNTIME
typedef int status_t;
#else
typedef int32_t status_t;
#endif
/* the MS C runtime lacks a few error codes */
/*
* Error codes.
* All error codes are negative values.
*/
// Win32 #defines NO_ERROR as well. It has the same value, so there's no
// real conflict, though it's a bit awkward.
#ifdef _WIN32
# undef NO_ERROR
#endif
enum {
OK = 0, // Everything's swell.
NO_ERROR = 0, // No errors.
UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
NO_MEMORY = -ENOMEM,
INVALID_OPERATION = -ENOSYS,
BAD_VALUE = -EINVAL,
BAD_TYPE = (UNKNOWN_ERROR + 1),
NAME_NOT_FOUND = -ENOENT,
PERMISSION_DENIED = -EPERM,
NO_INIT = -ENODEV,
ALREADY_EXISTS = -EEXIST,
DEAD_OBJECT = -EPIPE,
FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
JPARKS_BROKE_IT = -EPIPE,
#if !defined(HAVE_MS_C_RUNTIME)
BAD_INDEX = -EOVERFLOW,
NOT_ENOUGH_DATA = -ENODATA,
WOULD_BLOCK = -EWOULDBLOCK,
TIMED_OUT = -ETIMEDOUT,
UNKNOWN_TRANSACTION = -EBADMSG,
#else
BAD_INDEX = -E2BIG,
NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3),
WOULD_BLOCK = (UNKNOWN_ERROR + 4),
TIMED_OUT = (UNKNOWN_ERROR + 5),
UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
#endif
FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
};
// Restore define; enumeration is in "android" namespace, so the value defined
// there won't work for Win32 code in a different namespace.
#ifdef _WIN32
# define NO_ERROR 0L
#endif
}; // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_ERRORS_H

View file

@ -0,0 +1,137 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.
*/
#ifndef _LIBS_UTILS_MUTEX_H
#define _LIBS_UTILS_MUTEX_H
#include <stdint.h>
#include <sys/types.h>
#include <time.h>
#if defined(HAVE_PTHREADS)
# include <pthread.h>
#endif
#include "Errors.h"
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
class Condition;
/*
* Simple mutex class. The implementation is system-dependent.
*
* The mutex must be unlocked by the thread that locked it. They are not
* recursive, i.e. the same thread can't lock it multiple times.
*/
class Mutex {
public:
enum {
PRIVATE = 0,
SHARED = 1
};
Mutex();
Mutex(const char* name);
Mutex(int type, const char* name = NULL);
~Mutex();
// lock or unlock the mutex
status_t lock();
void unlock();
// lock if possible; returns 0 on success, error otherwise
status_t tryLock();
// Manages the mutex automatically. It'll be locked when Autolock is
// constructed and released when Autolock goes out of scope.
class Autolock {
public:
inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
inline ~Autolock() { mLock.unlock(); }
private:
Mutex& mLock;
};
private:
friend class Condition;
// A mutex cannot be copied
Mutex(const Mutex&);
Mutex& operator = (const Mutex&);
#if defined(HAVE_PTHREADS)
pthread_mutex_t mMutex;
#else
void _init();
void* mState;
#endif
};
// ---------------------------------------------------------------------------
#if defined(HAVE_PTHREADS)
inline Mutex::Mutex() {
pthread_mutex_init(&mMutex, NULL);
}
inline Mutex::Mutex(__attribute__((unused)) const char* name) {
pthread_mutex_init(&mMutex, NULL);
}
inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
if (type == SHARED) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&mMutex, &attr);
pthread_mutexattr_destroy(&attr);
} else {
pthread_mutex_init(&mMutex, NULL);
}
}
inline Mutex::~Mutex() {
pthread_mutex_destroy(&mMutex);
}
inline status_t Mutex::lock() {
return -pthread_mutex_lock(&mMutex);
}
inline void Mutex::unlock() {
pthread_mutex_unlock(&mMutex);
}
inline status_t Mutex::tryLock() {
return -pthread_mutex_trylock(&mMutex);
}
#endif // HAVE_PTHREADS
// ---------------------------------------------------------------------------
/*
* Automatic mutex. Declare one of these at the top of a function.
* When the function returns, it will go out of scope, and release the
* mutex.
*/
typedef Mutex::Autolock AutoMutex;
// ---------------------------------------------------------------------------
}; // namespace android
// ---------------------------------------------------------------------------
#endif // _LIBS_UTILS_MUTEX_H

File diff suppressed because it is too large Load diff

23
pdfiumandroid/src/main/cpp/util.h vendored Normal file
View file

@ -0,0 +1,23 @@
//
// Created by John Gray on 6/6/23.
//
#ifndef PDFIUMANDROIDKT_UTIL_H
#define PDFIUMANDROIDKT_UTIL_H
#include <jni.h>
extern "C" {
#include <stdlib.h>
}
#include <android/log.h>
#define JNI_FUNC(retType, bindClass, name) JNIEXPORT retType JNICALL Java_com_shockwave_pdfium_##bindClass##_##name
#define JNI_ARGS JNIEnv *env, jobject thiz
#define LOG_TAG "jniPdfium"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#endif //PDFIUMANDROIDKT_UTIL_H

View file

@ -0,0 +1,52 @@
package io.legere.pdfiumandroid
import java.io.Closeable
@Suppress("TooManyFunctions")
class FindResult(
val handle: FindHandle,
) : Closeable {
private external fun nativeFindNext(findHandle: Long): Boolean
private external fun nativeFindPrev(findHandle: Long): Boolean
private external fun nativeGetSchResultIndex(findHandle: Long): Int
private external fun nativeGetSchCount(findHandle: Long): Int
private external fun nativeCloseFind(findHandle: Long)
fun findNext(): Boolean {
synchronized(PdfiumCore.lock) {
return nativeFindNext(handle)
}
}
fun findPrev(): Boolean {
synchronized(PdfiumCore.lock) {
return nativeFindPrev(handle)
}
}
fun getSchResultIndex(): Int {
synchronized(PdfiumCore.lock) {
return nativeGetSchResultIndex(handle)
}
}
fun getSchCount(): Int {
synchronized(PdfiumCore.lock) {
return nativeGetSchCount(handle)
}
}
fun closeFind() {
synchronized(PdfiumCore.lock) {
nativeCloseFind(handle)
}
}
override fun close() {
nativeCloseFind(handle)
}
}

View file

@ -0,0 +1,61 @@
package io.legere.pdfiumandroid
import android.util.Log
import androidx.annotation.Keep
// At the moment we only do debug log with message, or error log with message and throwable
// in the future, we might expand this.
@Keep
interface LoggerInterface {
fun d(
tag: String,
message: String?,
)
fun e(
tag: String,
t: Throwable?,
message: String?,
)
}
@Suppress("MemberNameEqualsClassName")
object Logger : LoggerInterface {
private var logger: LoggerInterface? = null
override fun d(
tag: String,
message: String?,
) {
logger?.d(tag, message)
}
override fun e(
tag: String,
t: Throwable?,
message: String?,
) {
logger?.e(tag, t, message)
}
fun setLogger(logger: LoggerInterface) {
this.logger = logger
}
}
class DefaultLogger : LoggerInterface {
override fun d(
tag: String,
message: String?,
) {
message?.let { Log.d(tag, message) }
}
override fun e(
tag: String,
t: Throwable?,
message: String?,
) {
Log.e(tag, message, t)
}
}

View file

@ -0,0 +1,490 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid
import android.graphics.Matrix
import android.graphics.RectF
import android.os.ParcelFileDescriptor
import android.view.Surface
import io.legere.pdfiumandroid.util.handleAlreadyClosed
import java.io.Closeable
private const val MAX_RECURSION = 16
private const val THREE_BY_THREE = 9
/**
* PdfDocument represents a PDF file and allows you to load pages from it.
*/
@Suppress("TooManyFunctions")
class PdfDocument(
val mNativeDocPtr: Long,
) : Closeable {
private val pageMap = mutableMapOf<Int, PageCount>()
private val textPageMap = mutableMapOf<Int, PageCount>()
@Volatile
var isClosed = false
private set
private external fun nativeGetPageCount(docPtr: Long): Int
private external fun nativeLoadPage(
docPtr: Long,
pageIndex: Int,
): Long
private external fun nativeDeletePage(
docPtr: Long,
pageIndex: Int,
)
private external fun nativeCloseDocument(docPtr: Long)
private external fun nativeLoadPages(
docPtr: Long,
fromIndex: Int,
toIndex: Int,
): LongArray
private external fun nativeGetDocumentMetaText(
docPtr: Long,
tag: String,
): String
private external fun nativeGetFirstChildBookmark(
docPtr: Long,
bookmarkPtr: Long,
): Long
private external fun nativeGetSiblingBookmark(
docPtr: Long,
bookmarkPtr: Long,
): Long
private external fun nativeGetBookmarkDestIndex(
docPtr: Long,
bookmarkPtr: Long,
): Long
private external fun nativeLoadTextPage(
docPtr: Long,
pagePtr: Long,
): Long
private external fun nativeGetBookmarkTitle(bookmarkPtr: Long): String
private external fun nativeSaveAsCopy(
docPtr: Long,
callback: PdfWriteCallback,
flags: Int,
): Boolean
private external fun nativeGetPageCharCounts(docPtr: Long): IntArray
@Suppress("LongParameterList")
private external fun nativeRenderPagesWithMatrix(
pages: LongArray,
bufferPtr: Long,
drawSizeHor: Int,
drawSizeVer: Int,
matrixFloats: FloatArray,
clipFloats: FloatArray,
renderAnnot: Boolean,
textMask: Boolean,
canvasColor: Int,
pageBackgroundColor: Int,
)
@Suppress("LongParameterList")
private external fun nativeRenderPagesSurfaceWithMatrix(
pages: LongArray,
surface: Surface,
matrixFloats: FloatArray,
clipFloats: FloatArray,
renderAnnot: Boolean,
textMask: Boolean,
canvasColor: Int,
pageBackgroundColor: Int,
): Boolean
var parcelFileDescriptor: ParcelFileDescriptor? = null
var source: PdfiumSource? = null
/**
* Get the page count of the PDF document
* @return the number of pages
*/
fun getPageCount(): Int {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return 0
return nativeGetPageCount(mNativeDocPtr)
}
}
/**
* Get the page character counts for every page of the PDF document
* @return an array of character counts
*/
fun getPageCharCounts(): IntArray {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return IntArray(0)
return nativeGetPageCharCounts(mNativeDocPtr)
}
}
/**
* Open page and store native pointer in [PdfDocument]
* @param pageIndex the page index
* @return the opened page [PdfPage]
* @throws IllegalArgumentException if document is closed or the page cannot be loaded,
* RuntimeException if the page cannot be loaded
*/
fun openPage(pageIndex: Int): PdfPage {
synchronized(PdfiumCore.lock) {
check(!isClosed) { "Already closed" }
if (pageMap.containsKey(pageIndex)) {
pageMap[pageIndex]?.let {
it.count++
// Timber.d("from cache openPage: pageIndex: $pageIndex, count: ${it.count}")
return PdfPage(this, pageIndex, it.pagePtr, pageMap)
}
}
// Timber.d("openPage: pageIndex: $pageIndex")
val pagePtr = nativeLoadPage(this.mNativeDocPtr, pageIndex)
pageMap[pageIndex] = PageCount(pagePtr, 1)
return PdfPage(this, pageIndex, pagePtr, pageMap)
}
}
/**
* Delete page
* @param pageIndex the page index
* @throws IllegalArgumentException if document is closed
*/
fun deletePage(pageIndex: Int) {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return
nativeDeletePage(this.mNativeDocPtr, pageIndex)
}
}
/**
* Open range of pages and store native pointers in [PdfDocument]
* @param fromIndex the start index of the range
* @param toIndex the end index of the range
* @return the opened pages [PdfPage]
* @throws IllegalArgumentException if document is closed or the pages cannot be loaded
*/
fun openPages(
fromIndex: Int,
toIndex: Int,
): List<PdfPage> {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return emptyList()
var pagesPtr: LongArray
pagesPtr = nativeLoadPages(this.mNativeDocPtr, fromIndex, toIndex)
var pageIndex = fromIndex
for (page in pagesPtr) {
if (pageIndex > toIndex) break
pageIndex++
}
return pagesPtr.map { PdfPage(this, pageIndex, it, pageMap) }
}
}
/**
* Render page fragment on [Surface].<br></br>
* @param bufferPtr Surface's buffer on which to render page
* @param pages The pages to render
* @param matrices The matrices to map the pages to the surface
* @param clipRects The rectangles to clip the pages to
* @param renderAnnot whether render annotation
* @param textMask whether to render text as image mask - currently ignored
* @param canvasColor The color to fill the canvas with. Use 0 to not fill the canvas.
* @param pageBackgroundColor The color for the page background. Use 0 to not fill the background.
* You almost always want this to be white (the default)
* @throws IllegalStateException If the page or document is closed
*/
@Suppress("LongParameterList")
fun renderPages(
bufferPtr: Long,
drawSizeX: Int,
drawSizeY: Int,
pages: List<PdfPage>,
matrices: List<Matrix>,
clipRects: List<RectF>,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
) {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || pages.any { it.isClosed })) return
val matrixFloats =
matrices
.flatMap { matrix ->
val matrixValues = FloatArray(THREE_BY_THREE)
matrix.getValues(matrixValues)
listOf(
matrixValues[Matrix.MSCALE_X],
matrixValues[Matrix.MTRANS_X],
matrixValues[Matrix.MTRANS_Y],
)
}.toFloatArray()
val clipFloats =
clipRects
.flatMap { rect ->
listOf(
rect.left,
rect.top,
rect.right,
rect.bottom,
)
}.toFloatArray()
nativeRenderPagesWithMatrix(
pages.map { it.pagePtr }.toLongArray(),
bufferPtr,
drawSizeX,
drawSizeY,
matrixFloats,
clipFloats,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
}
}
@Suppress("LongParameterList")
fun renderPages(
surface: Surface,
pages: List<PdfPage>,
matrices: List<Matrix>,
clipRects: List<RectF>,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
): Boolean {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || pages.any { it.isClosed })) return false
val matrixFloats =
matrices
.flatMap { matrix ->
val matrixValues = FloatArray(THREE_BY_THREE)
matrix.getValues(matrixValues)
listOf(
matrixValues[Matrix.MSCALE_X],
matrixValues[Matrix.MTRANS_X],
matrixValues[Matrix.MTRANS_Y],
)
}.toFloatArray()
val clipFloats =
clipRects
.flatMap { rect ->
listOf(
rect.left,
rect.top,
rect.right,
rect.bottom,
)
}.toFloatArray()
return nativeRenderPagesSurfaceWithMatrix(
pages.map { it.pagePtr }.toLongArray(),
surface,
matrixFloats,
clipFloats,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
}
}
/**
* Get metadata for given document
* @return the [Meta] data
* @throws IllegalArgumentException if document is closed
*/
fun getDocumentMeta(): Meta {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return Meta()
val meta = Meta()
meta.title = nativeGetDocumentMetaText(mNativeDocPtr, "Title")
meta.author = nativeGetDocumentMetaText(mNativeDocPtr, "Author")
meta.subject = nativeGetDocumentMetaText(mNativeDocPtr, "Subject")
meta.keywords = nativeGetDocumentMetaText(mNativeDocPtr, "Keywords")
meta.creator = nativeGetDocumentMetaText(mNativeDocPtr, "Creator")
meta.producer = nativeGetDocumentMetaText(mNativeDocPtr, "Producer")
meta.creationDate = nativeGetDocumentMetaText(mNativeDocPtr, "CreationDate")
meta.modDate = nativeGetDocumentMetaText(mNativeDocPtr, "ModDate")
return meta
}
}
private fun recursiveGetBookmark(
tree: MutableList<Bookmark>,
bookmarkPtr: Long,
level: Long,
) {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return
var levelMutable = level
val bookmark = Bookmark()
bookmark.mNativePtr = bookmarkPtr
bookmark.title = nativeGetBookmarkTitle(bookmarkPtr)
bookmark.pageIdx = nativeGetBookmarkDestIndex(mNativeDocPtr, bookmarkPtr)
tree.add(bookmark)
val child = nativeGetFirstChildBookmark(mNativeDocPtr, bookmarkPtr)
if (child != 0L && levelMutable < MAX_RECURSION) {
recursiveGetBookmark(bookmark.children, child, levelMutable++)
}
val sibling = nativeGetSiblingBookmark(mNativeDocPtr, bookmarkPtr)
if (sibling != 0L && levelMutable < MAX_RECURSION) {
recursiveGetBookmark(tree, sibling, levelMutable)
}
}
}
/**
* Get table of contents (bookmarks) for given document
* @return the [Bookmark] list
* @throws IllegalArgumentException if document is closed
*/
fun getTableOfContents(): List<Bookmark> {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return emptyList()
val topLevel: MutableList<Bookmark> =
ArrayList()
val first = nativeGetFirstChildBookmark(this.mNativeDocPtr, 0)
if (first != 0L) {
recursiveGetBookmark(topLevel, first, 1)
}
return topLevel
}
}
/**
* Open a text page
* @param page the [PdfPage]
* @return the opened [PdfTextPage]
* @throws IllegalArgumentException if document is closed or the page cannot be loaded
*/
@Deprecated("Use PdfPage.openTextPage instead", ReplaceWith("page.openTextPage()"))
fun openTextPage(page: PdfPage): PdfTextPage {
synchronized(PdfiumCore.lock) {
check(!isClosed) { "Already closed" }
if (textPageMap.containsKey(page.pageIndex)) {
textPageMap[page.pageIndex]?.let {
it.count++
// Timber.d("from cache openTextPage: pageIndex: ${page.pageIndex}, count: ${it.count}")
return PdfTextPage(this, page.pageIndex, it.pagePtr, textPageMap)
}
}
// Timber.d("openTextPage: pageIndex: ${page.pageIndex}")
val textPagePtr = nativeLoadTextPage(this.mNativeDocPtr, page.pagePtr)
textPageMap[page.pageIndex] = PageCount(textPagePtr, 1)
return PdfTextPage(this, page.pageIndex, textPagePtr, textPageMap)
}
}
/**
* Open a range of text pages
* @param fromIndex the start index of the range
* @param toIndex the end index of the range
* @return the opened [PdfTextPage] list
* @throws IllegalArgumentException if document is closed or the pages cannot be loaded
*/
fun openTextPages(
fromIndex: Int,
toIndex: Int,
): List<PdfTextPage> {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return emptyList()
var textPagesPtr: LongArray
textPagesPtr = nativeLoadPages(mNativeDocPtr, fromIndex, toIndex)
return textPagesPtr.mapIndexed { index: Int, pagePtr: Long ->
PdfTextPage(
this,
fromIndex + index,
pagePtr,
textPageMap,
)
}
}
}
/**
* Save document as a copy
* @param callback the [PdfWriteCallback] to be called with the data
* @param flags must be one of [FPDF_INCREMENTAL], [FPDF_NO_INCREMENTAL] or [FPDF_REMOVE_SECURITY]
* @return true if the document was successfully saved
* @throws IllegalArgumentException if document is closed
*/
fun saveAsCopy(
callback: PdfWriteCallback,
flags: Int = FPDF_NO_INCREMENTAL,
): Boolean {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return false
return nativeSaveAsCopy(mNativeDocPtr, callback, flags)
}
}
/**
* Close the document
* @throws IllegalArgumentException if document is closed
*/
override fun close() {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed)) return
Logger.d(TAG, "PdfDocument.close")
isClosed = true
nativeCloseDocument(mNativeDocPtr)
parcelFileDescriptor?.close()
parcelFileDescriptor = null
source?.close()
source = null
}
}
class Meta {
var title: String? = null
var author: String? = null
var subject: String? = null
var keywords: String? = null
var creator: String? = null
var producer: String? = null
var creationDate: String? = null
var modDate: String? = null
}
class Bookmark {
val children: MutableList<Bookmark> = ArrayList()
var title: String? = null
var pageIdx: Long = 0
var mNativePtr: Long = 0
}
class Link(
val bounds: RectF,
val destPageIdx: Int?,
val uri: String?,
)
data class PageCount(
val pagePtr: Long,
var count: Int,
)
companion object {
private val TAG = PdfDocument::class.java.name
const val FPDF_INCREMENTAL = 1
const val FPDF_NO_INCREMENTAL = 2
const val FPDF_REMOVE_SECURITY = 3
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,110 @@
package io.legere.pdfiumandroid
import android.graphics.RectF
import java.io.Closeable
import java.nio.charset.StandardCharsets
@Suppress("TooManyFunctions")
class PdfPageLink(
private val pageLinkPtr: Long,
) : Closeable {
fun countWebLinks(): Int {
synchronized(PdfiumCore.lock) {
return nativeCountWebLinks(pageLinkPtr)
}
}
@Suppress("TooGenericExceptionCaught", "ReturnCount")
fun getURL(
index: Int,
length: Int,
): String? {
synchronized(PdfiumCore.lock) {
try {
val bytes = ByteArray(length * 2)
val r =
nativeGetURL(
pageLinkPtr,
index,
length,
bytes,
)
if (r <= 0) {
return ""
}
return String(bytes, StandardCharsets.UTF_16LE)
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
}
return null
}
}
fun countRects(index: Int): Int {
synchronized(PdfiumCore.lock) {
return nativeCountRects(pageLinkPtr, index)
}
}
@Suppress("MagicNumber")
fun getRect(
linkIndex: Int,
rectIndex: Int,
): RectF {
synchronized(PdfiumCore.lock) {
return nativeGetRect(pageLinkPtr, linkIndex, rectIndex).let {
RectF(it[0], it[1], it[2], it[3])
}
}
}
fun getTextRange(index: Int): Pair<Int, Int> {
synchronized(PdfiumCore.lock) {
return nativeGetTextRange(pageLinkPtr, index).let {
Pair(it[0], it[1])
}
}
}
override fun close() {
nativeClosePageLink(pageLinkPtr)
}
companion object {
private val TAG = PdfPageLink::class.java.name
@JvmStatic
private external fun nativeClosePageLink(pageLinkPtr: Long)
@JvmStatic
private external fun nativeCountWebLinks(pageLinkPtr: Long): Int
@JvmStatic
private external fun nativeGetURL(
pageLinkPtr: Long,
index: Int,
count: Int,
result: ByteArray,
): Int
@JvmStatic
private external fun nativeCountRects(
pageLinkPtr: Long,
index: Int,
): Int
@JvmStatic
private external fun nativeGetRect(
pageLinkPtr: Long,
linkIndex: Int,
rectIndex: Int,
): FloatArray
@JvmStatic
// needs to return a start and an end
private external fun nativeGetTextRange(pageLinkPtr: Long, index: Int): IntArray
}
}

View file

@ -0,0 +1,12 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid
import java.io.IOException
/**
* PdfPasswordException is thrown when a password is required to open a document
*/
class PdfPasswordException(
msg: String? = null,
) : IOException(msg)

View file

@ -0,0 +1,504 @@
@file:Suppress("unused", "MemberVisibilityCanBePrivate", "TooGenericExceptionCaught")
package io.legere.pdfiumandroid
import android.graphics.RectF
import dalvik.annotation.optimization.FastNative
import io.legere.pdfiumandroid.util.handleAlreadyClosed
import java.io.Closeable
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.charset.StandardCharsets
typealias FindHandle = Long
private const val LEFT_OFFSET = 0
private const val TOP_OFFSET = 1
private const val RIGHT_OFFSET = 2
private const val BOTTOM_OFFSET = 3
private const val RANGE_START_OFFSET = 4
private const val RANGE_LENGTH_OFFSET = 5
private const val RANGE_RECT_DATA_SIZE = 6
/**
* PdfTextPage is a wrapper around the native PdfiumCore text page
* It is used to get text and other information about the text on a page
* @property doc the PdfDocument this page belongs to
* @property pageIndex the index of this page in the document
* @property pagePtr the pointer to the native page
*/
@Suppress("TooManyFunctions")
class PdfTextPage(
val doc: PdfDocument,
val pageIndex: Int,
val pagePtr: Long,
val pageMap: MutableMap<Int, PdfDocument.PageCount>,
) : Closeable {
@Volatile
private var isClosed = false
/**
* Get character count of the page
* @return the number of characters on the page
* @throws IllegalStateException if the page or document is closed
*/
fun textPageCountChars(): Int {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return -1
return nativeTextCountChars(pagePtr)
}
}
/**
* Get the text on the page
* @param startIndex the index of the first character to get
* @param length the number of characters to get
* @return the text
* @throws IllegalStateException if the page or document is closed
*/
@Suppress("ReturnCount")
fun textPageGetTextLegacy(
startIndex: Int,
length: Int,
): String? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
try {
val buf = ShortArray(length + 1)
val r =
nativeTextGetText(
pagePtr,
startIndex,
length,
buf,
)
if (r <= 0) {
return ""
}
val bytes = ByteArray((r - 1) * 2)
val bb = ByteBuffer.wrap(bytes)
bb.order(ByteOrder.LITTLE_ENDIAN)
for (i in 0 until r - 1) {
val s = buf[i]
bb.putShort(s)
}
return String(bytes, StandardCharsets.UTF_16LE)
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
}
return null
}
}
@Suppress("ReturnCount")
fun textPageGetText(
startIndex: Int,
length: Int,
): String? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
try {
val bytes = ByteArray(length * 2)
val r =
nativeTextGetTextByteArray(
pagePtr,
startIndex,
length,
bytes,
)
if (r <= 0) {
return ""
}
return String(bytes, StandardCharsets.UTF_16LE)
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
}
return null
}
}
/**
* Get a unicode character on the page
* @param index the index of the character to get
* @return the character
* @throws IllegalStateException if the page or document is closed
*/
fun textPageGetUnicode(index: Int): Char {
synchronized(PdfiumCore.lock) {
check(!isClosed && !doc.isClosed) { "Already closed" }
return nativeTextGetUnicode(
pagePtr,
index,
).toChar()
}
}
/**
* Get the bounding box of a character on the page
* @param index the index of the character to get
* @return the bounding box
* @throws IllegalStateException if the page or document is closed
*/
@Suppress("ReturnCount", "MagicNumber")
fun textPageGetCharBox(index: Int): RectF? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
try {
val o = nativeTextGetCharBox(pagePtr, index)
// Note these are in an odd order left, right, bottom, top
// what what Pdfium native code returns
val r = RectF()
r.left = o[0].toFloat()
r.right = o[1].toFloat()
r.bottom = o[2].toFloat()
r.top = o[3].toFloat()
return r
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
}
}
return null
}
/**
* Get the index of the character at a given position on the page
* @param x the x position
* @param y the y position
* @param xTolerance the x tolerance
* @param yTolerance the y tolerance
* @return the index of the character at the position
* @throws IllegalStateException if the page or document is closed
*/
@Suppress("ReturnCount")
fun textPageGetCharIndexAtPos(
x: Double,
y: Double,
xTolerance: Double,
yTolerance: Double,
): Int {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return -1
try {
return nativeTextGetCharIndexAtPos(
pagePtr,
x,
y,
xTolerance,
yTolerance,
)
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
}
}
return -1
}
/**
* Get the count of rectangles that bound the text on the page in a given range
* @param startIndex the index of the first character to get
* @param count the number of characters to get
* @return the number of rectangles
* @throws IllegalStateException if the page or document is closed
*/
fun textPageCountRects(
startIndex: Int,
count: Int,
): Int {
synchronized(PdfiumCore.lock) {
check(!isClosed && !doc.isClosed) { "Already closed" }
try {
return nativeTextCountRects(
pagePtr,
startIndex,
count,
)
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
}
}
return -1
}
/**
* Get the bounding box of a text on the page
* @param rectIndex the index of the rectangle to get
* @return the bounding box
* @throws IllegalStateException if the page or document is closed
*/
@Suppress("MagicNumber")
fun textPageGetRect(rectIndex: Int): RectF? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
return try {
val o = nativeTextGetRect(pagePtr, rectIndex)
val r = RectF()
r.left = o[LEFT_OFFSET].toFloat()
r.top = o[TOP_OFFSET].toFloat()
r.right = o[RIGHT_OFFSET].toFloat()
r.bottom = o[BOTTOM_OFFSET].toFloat()
r
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
null
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
null
}
}
}
/**
* Get the bounding box of a range of texts on the page
* @param wordRanges an array of word ranges to get the bounding boxes for.
* Even indices are the start index, odd indices are the length
* @return list of bounding boxes with their start and length
* @throws IllegalStateException if the page or document is closed
*/
@Suppress("ReturnCount")
fun textPageGetRectsForRanges(wordRanges: IntArray): List<WordRangeRect>? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
val data = nativeTextGetRects(pagePtr, wordRanges)
if (data != null) {
val wordRangeRects = mutableListOf<WordRangeRect>()
for (i in data.indices step RANGE_RECT_DATA_SIZE) {
val r = RectF()
r.left = data[i + LEFT_OFFSET].toFloat()
r.top = data[i + TOP_OFFSET].toFloat()
r.right = data[i + RIGHT_OFFSET].toFloat()
r.bottom = data[i + BOTTOM_OFFSET].toFloat()
val rangeStart = data[i + RANGE_START_OFFSET].toInt()
val rangeLength = data[i + RANGE_LENGTH_OFFSET].toInt()
WordRangeRect(rangeStart, rangeLength, r).let {
wordRangeRects.add(it)
}
}
return wordRangeRects
}
}
return null
}
/**
* Get the text bounded by the given rectangle
* @param rect the rectangle to bound the text
* @param length the maximum number of characters to get
* @return the text bounded by the rectangle
* @throws IllegalStateException if the page or document is closed
*/
fun textPageGetBoundedText(
rect: RectF,
length: Int,
): String? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
return try {
val buf = ShortArray(length + 1)
val r =
nativeTextGetBoundedText(
pagePtr,
rect.left.toDouble(),
rect.top.toDouble(),
rect.right.toDouble(),
rect.bottom.toDouble(),
buf,
)
val bytes = ByteArray((r - 1) * 2)
val bb = ByteBuffer.wrap(bytes)
bb.order(ByteOrder.LITTLE_ENDIAN)
for (i in 0 until r - 1) {
val s = buf[i]
bb.putShort(s)
}
String(bytes, StandardCharsets.UTF_16LE)
} catch (e: NullPointerException) {
Logger.e(TAG, e, "mContext may be null")
null
} catch (e: Exception) {
Logger.e(TAG, e, "Exception throw from native")
null
}
}
}
/**
* Get character font size in PostScript points (1/72th of an inch).<br></br>
* @param charIndex the index of the character to get
* @return the font size
* @throws IllegalStateException if the page or document is closed
*/
fun getFontSize(charIndex: Int): Double {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return 0.0
return nativeGetFontSize(pagePtr, charIndex)
}
}
fun findStart(
findWhat: String,
flags: Set<FindFlags>,
startIndex: Int,
): FindResult? {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return null
val apiFlags = flags.fold(0) { acc, flag -> acc or flag.value }
return FindResult(nativeFindStart(pagePtr, findWhat, apiFlags, startIndex))
}
}
fun loadWebLink(): PdfPageLink {
check(!isClosed && !doc.isClosed) { "Already closed" }
val linkPtr = nativeLoadWebLink(pagePtr)
return PdfPageLink(linkPtr)
}
/**
* Close the page and release all resources
*/
override fun close() {
synchronized(PdfiumCore.lock) {
if (handleAlreadyClosed(isClosed || doc.isClosed)) return
pageMap[pageIndex]?.let {
if (it.count > 1) {
it.count--
return
}
pageMap.remove(pageIndex)
isClosed = true
nativeCloseTextPage(pagePtr)
}
}
}
companion object {
private val TAG = PdfTextPage::class.java.name
@JvmStatic
private external fun nativeCloseTextPage(pagePtr: Long)
@JvmStatic
@FastNative
private external fun nativeTextCountChars(textPagePtr: Long): Int
@JvmStatic
@FastNative
private external fun nativeTextGetCharBox(
textPagePtr: Long,
index: Int,
): DoubleArray
@JvmStatic
@FastNative
private external fun nativeTextGetRect(
textPagePtr: Long,
rectIndex: Int,
): DoubleArray
@JvmStatic
@FastNative
private external fun nativeTextGetRects(
textPagePtr: Long,
wordRanges: IntArray,
): DoubleArray?
@Suppress("LongParameterList")
@JvmStatic
@FastNative
private external fun nativeTextGetBoundedText(
textPagePtr: Long,
left: Double,
top: Double,
right: Double,
bottom: Double,
arr: ShortArray,
): Int
@JvmStatic
private external fun nativeFindStart(
textPagePtr: Long,
findWhat: String,
flags: Int,
startIndex: Int,
): Long
@JvmStatic
private external fun nativeLoadWebLink(textPagePtr: Long): Long
@JvmStatic
private external fun nativeTextGetCharIndexAtPos(
textPagePtr: Long,
x: Double,
y: Double,
xTolerance: Double,
yTolerance: Double,
): Int
@JvmStatic
private external fun nativeTextGetText(
textPagePtr: Long,
startIndex: Int,
count: Int,
result: ShortArray,
): Int
//
@JvmStatic
private external fun nativeTextGetTextByteArray(
textPagePtr: Long,
startIndex: Int,
count: Int,
result: ByteArray,
): Int
@JvmStatic
@FastNative
private external fun nativeTextGetUnicode(
textPagePtr: Long,
index: Int,
): Int
@JvmStatic
@FastNative
private external fun nativeTextCountRects(
textPagePtr: Long,
startIndex: Int,
count: Int,
): Int
@JvmStatic
@FastNative
private external fun nativeGetFontSize(
pagePtr: Long,
charIndex: Int,
): Double
}
}
@Suppress("MagicNumber")
enum class FindFlags(
val value: Int,
) {
MatchCase(0x00000001),
MatchWholeWord(0x00000002),
Consecutive(0x00000004),
}
data class WordRangeRect(
val rangeStart: Int,
val rangeLength: Int,
val rect: RectF,
)

View file

@ -0,0 +1,17 @@
package io.legere.pdfiumandroid
/**
* PdfWriteCallback is the calback interface for saveAsCopy
*/
interface PdfWriteCallback {
/**
* WriteBlock is called by native code to write a block of data
* @param data the data to write
*
* note: The name need to be exactly what it is.
* The native call is looking for is as WriteBlock
*
*/
@Suppress("FunctionNaming", "FunctionName")
fun WriteBlock(data: ByteArray?): Int
}

View file

@ -0,0 +1,600 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import android.graphics.RectF
import android.os.ParcelFileDescriptor
import android.util.Log
import android.view.Surface
import io.legere.pdfiumandroid.util.Config
import io.legere.pdfiumandroid.util.InitLock
import io.legere.pdfiumandroid.util.PdfiumNativeSourceBridge
import io.legere.pdfiumandroid.util.Size
import io.legere.pdfiumandroid.util.pdfiumConfig
import kotlinx.coroutines.sync.Mutex
import java.io.IOException
/**
* PdfiumCore is the main entry-point for access to the PDFium API.
*/
@Suppress("TooManyFunctions")
class PdfiumCore(
context: Context? = null,
val config: Config = Config(),
) {
private val mCurrentDpi: Int
init {
pdfiumConfig = config
Logger.setLogger(config.logger)
Logger.d(TAG, "Starting PdfiumAndroid ")
mCurrentDpi = context?.resources?.displayMetrics?.densityDpi ?: -1
isReady.waitForReady()
}
private external fun nativeOpenDocument(
fd: Int,
password: String?,
): Long
private external fun nativeOpenMemDocument(
data: ByteArray?,
password: String?,
): Long
private external fun nativeOpenCustomDocument(
data: PdfiumNativeSourceBridge,
password: String?,
size: Long,
): Long
/**
* Create new document from file
* @param fd opened file descriptor of file
* @return PdfDocument
*/
@Throws(IOException::class)
fun newDocument(fd: ParcelFileDescriptor): PdfDocument = newDocument(fd, null)
/**
* Create new document from file with password
* @param parcelFileDescriptor opened file descriptor of file
* @param password password for decryption
* @return PdfDocument
*/
@Throws(IOException::class)
fun newDocument(
parcelFileDescriptor: ParcelFileDescriptor,
password: String?,
): PdfDocument {
synchronized(lock) {
return PdfDocument(nativeOpenDocument(parcelFileDescriptor.fd, password)).also { document ->
document.parcelFileDescriptor = parcelFileDescriptor
document.source = null
}
}
}
/**
* Create new document from bytearray
* @param data bytearray of pdf file
* @return PdfDocument
*/
@Throws(IOException::class)
fun newDocument(data: ByteArray?): PdfDocument = newDocument(data, null)
/**
* Create new document from bytearray with password
* @param data bytearray of pdf file
* @param password password for decryption
* @return PdfDocument
*/
@Throws(IOException::class)
fun newDocument(
data: ByteArray?,
password: String?,
): PdfDocument {
synchronized(lock) {
return PdfDocument(nativeOpenMemDocument(data, password)).also { document ->
document.parcelFileDescriptor = null
document.source = null
}
}
}
/**
* Create new document from custom data source
* @param data custom data source to read from
* @return PdfDocument
*/
@Throws(IOException::class)
fun newDocument(data: PdfiumSource): PdfDocument = newDocument(data, null)
/**
* Create new document from custom data source with password
* @param data custom data source to read from
* @param password password for decryption
* @return PdfDocument
*/
@Throws(IOException::class)
fun newDocument(
data: PdfiumSource,
password: String?,
): PdfDocument {
synchronized(lock) {
val nativeSourceBridge = PdfiumNativeSourceBridge(data)
return PdfDocument(nativeOpenCustomDocument(nativeSourceBridge, password, data.length)).also { document ->
document.parcelFileDescriptor = null
document.source = data
}
}
}
@Deprecated("Use PdfDocument.getPageCount()", ReplaceWith("pdfDocument.getPageCount()"), DeprecationLevel.WARNING)
fun getPageCount(pdfDocument: PdfDocument): Int = pdfDocument.getPageCount()
@Deprecated("Use PdfDocument.closeDocument()", ReplaceWith("pdfDocument.close()"), DeprecationLevel.WARNING)
fun closeDocument(pdfDocument: PdfDocument) {
pdfDocument.close()
}
@Deprecated(
"Use PdfDocument.getTableOfContents()",
ReplaceWith("pdfDocument.getTableOfContents()"),
DeprecationLevel.WARNING,
)
fun getTableOfContents(pdfDocument: PdfDocument): List<PdfDocument.Bookmark> = pdfDocument.getTableOfContents()
@Suppress("UNUSED_PARAMETER") // Need to keep for compatibility
@Deprecated(
"Use PdfDocument.openTextPage()",
ReplaceWith("pdfDocument.openTextPage(pageIndex)"),
DeprecationLevel.WARNING,
)
fun openTextPage(pdfDocument: PdfDocument, pageIndex: Int): Long = pageIndex.toLong()
@Suppress("UNUSED_PARAMETER") // Need to keep for compatibility
@Deprecated(
"Use PdfDocument.openPage()",
ReplaceWith("pdfDocument.openPage(pageIndex)"),
DeprecationLevel.WARNING,
)
fun openPage(pdfDocument: PdfDocument, pageIndex: Int): Long = pageIndex.toLong()
@Deprecated(
"Use Page.getPageMediaBox()",
ReplaceWith("page.getPageMediaBox()"),
DeprecationLevel.WARNING,
)
fun getPageMediaBox(
pdfDocument: PdfDocument,
pageIndex: Int,
): RectF {
pdfDocument.openPage(pageIndex).use { page ->
return page.getPageMediaBox()
}
}
@Suppress("EmptyMethod")
@Deprecated(
"Use page.close()",
ReplaceWith("page.close()"),
DeprecationLevel.ERROR,
)
fun closePage(
pdfDocument: PdfDocument,
pageIndex: Int,
) {
// empty
}
@Suppress("UNUSED_PARAMETER", "EmptyMethod") // Need to keep for compatibility
@Deprecated(
"Use textPage.close()",
ReplaceWith("textPage.close()"),
DeprecationLevel.ERROR,
)
fun closeTextPage(pdfDocument: PdfDocument, pageIndex: Int) {
// empty
}
@Deprecated(
"Use textPage.textPageCountChars()",
ReplaceWith("textPage.textPageCountChars()"),
DeprecationLevel.WARNING,
)
fun textPageCountChars(
pdfDocument: PdfDocument,
pageIndex: Int,
): Int {
pdfDocument.openPage(pageIndex).use { page ->
page.openTextPage().use { textPage ->
return textPage.textPageCountChars()
}
}
}
@Deprecated(
"Use textPage.textPageGetText(start, count)",
ReplaceWith("textPage.textPageGetText(start, count)"),
DeprecationLevel.WARNING,
)
fun textPageGetText(
pdfDocument: PdfDocument,
pageIndex: Int,
start: Int,
count: Int,
): String? {
pdfDocument.openPage(pageIndex).use { page ->
page.openTextPage().use { textPage ->
return textPage.textPageGetText(start, count)
}
}
}
@Deprecated(
"Use pdfDocument.getDocumentMeta()",
ReplaceWith("pdfDocument.getDocumentMeta()"),
DeprecationLevel.WARNING,
)
fun getDocumentMeta(pdfDocument: PdfDocument): PdfDocument.Meta = pdfDocument.getDocumentMeta()
@Deprecated(
"Use PdfPage.getPageWidthPoint()",
ReplaceWith("page.getPageWidthPoint()"),
DeprecationLevel.WARNING,
)
fun getPageWidthPoint(
pdfDocument: PdfDocument,
pageIndex: Int,
): Int {
pdfDocument.openPage(pageIndex).use { page ->
return page.getPageWidthPoint()
}
}
@Deprecated(
"Use PdfPage.getPageHeightPoint()",
ReplaceWith("page.getPageHeightPoint()"),
DeprecationLevel.WARNING,
)
fun getPageHeightPoint(
pdfDocument: PdfDocument,
pageIndex: Int,
): Int {
pdfDocument.openPage(pageIndex).use { page ->
return page.getPageHeightPoint()
}
}
@Deprecated(
"Use PdfPage.renderPageBitmap(bitmap, startX, startY, drawSizeX, drawSizeY, screenDpi, renderAnnot, textMask)",
ReplaceWith(
"page.renderPageBitmap(bitmap, startX, startY, drawSizeX, drawSizeY, screenDpi, renderAnnot, textMask)",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList")
fun renderPageBitmap(
pdfDocument: PdfDocument,
bitmap: Bitmap?,
pageIndex: Int,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
renderAnnot: Boolean = false,
textMask: Boolean = false,
) {
pdfDocument.openPage(pageIndex).use { page ->
page.renderPageBitmap(bitmap, startX, startY, drawSizeX, drawSizeY, renderAnnot, textMask)
}
}
@Deprecated(
"Use PdfPage.textPageGetRect(index)",
ReplaceWith(
"page.textPageGetRect(index)",
),
DeprecationLevel.WARNING,
)
fun textPageGetRect(
pdfDocument: PdfDocument,
pageIndex: Int,
index: Int,
): RectF? {
pdfDocument.openPage(pageIndex).use { page ->
page.openTextPage().use { textPage ->
return textPage.textPageGetRect(index)
}
}
}
@Deprecated(
"Use PdfPage.textPageGetBoundedText(sourceRect, size)",
ReplaceWith(
"page.textPageGetBoundedText(sourceRect, size)",
),
DeprecationLevel.WARNING,
)
fun textPageGetBoundedText(
pdfDocument: PdfDocument,
pageIndex: Int,
sourceRect: RectF,
size: Int,
): String? {
pdfDocument.openPage(pageIndex).use { page ->
page.openTextPage().use { textPage ->
return textPage.textPageGetBoundedText(sourceRect, size)
}
}
}
@Deprecated(
"Use PdfPage.mapRectToPage(startX, startY, sizeX, sizeY, rotate, coords)",
ReplaceWith(
"page.mapRectToPage(startX, startY, sizeX, sizeY, rotate, coords)",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList")
fun mapRectToPage(
pdfDocument: PdfDocument,
pageIndex: Int,
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
coords: Rect,
): RectF {
pdfDocument.openPage(pageIndex).use { page ->
return page.mapRectToPage(startX, startY, sizeX, sizeY, rotate, coords)
}
}
@Deprecated(
"Use PdfTextPage.textPageCountRects(startIndex, count)",
ReplaceWith(
"textPage.textPageCountRects(startIndex, count)",
),
DeprecationLevel.WARNING,
)
fun textPageCountRects(
pdfDocument: PdfDocument,
pageIndex: Int,
startIndex: Int,
count: Int,
): Int {
pdfDocument.openPage(pageIndex).use { page ->
page.openTextPage().use { textPage ->
return textPage.textPageCountRects(startIndex, count)
}
}
}
@Suppress("UNUSED_PARAMETER") // Need to keep for compatibility
@Deprecated(
"Use PdfDocument.openPage(fromIndex, toIndex)",
ReplaceWith(
"pdfDocument.openPage(fromIndex, toIndex)",
),
DeprecationLevel.ERROR,
)
fun openPage(
pdfDocument: PdfDocument,
fromIndex: Int,
toIndex: Int,
): Array<Long> = (fromIndex.toLong()..toIndex.toLong()).toList().toTypedArray()
@Deprecated(
"Use PdfPage.getPageWidth()",
ReplaceWith(
"page.getPageWidth()",
),
DeprecationLevel.WARNING,
)
fun getPageWidth(
pdfDocument: PdfDocument,
index: Int,
): Int {
pdfDocument.openPage(index).use { page ->
return page.getPageWidth(mCurrentDpi)
}
}
@Deprecated(
"Use PdfPage.getPageHeight()",
ReplaceWith(
"page.getPageHeight()",
),
DeprecationLevel.WARNING,
)
fun getPageHeight(
pdfDocument: PdfDocument,
index: Int,
): Int {
pdfDocument.openPage(index).use { page ->
return page.getPageHeight(mCurrentDpi)
}
}
@Deprecated(
"Use PdfPage.getPageSize()",
ReplaceWith(
"page.getPageSize()",
),
DeprecationLevel.WARNING,
)
fun getPageSize(
pdfDocument: PdfDocument,
index: Int,
): Size {
pdfDocument.openPage(index).use { page ->
return page.getPageSize(mCurrentDpi)
}
}
@Deprecated(
"Use PdfPage.renderPage(surface, startX, startY, drawSizeX, drawSizeY)",
ReplaceWith(
"page.renderPage(surface, startX, startY, drawSizeX, drawSizeY)",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList", "ComplexCondition")
fun renderPage(
pdfDocument: PdfDocument,
surface: Surface?,
pageIndex: Int,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
renderAnnot: Boolean = false,
): Boolean {
var retValue = false
pdfDocument.openPage(pageIndex).use { page ->
val sizes = IntArray(2)
val pointers = LongArray(2)
surface
?.let {
PdfPage.lockSurface(
it,
sizes,
pointers,
)
}
val nativeWindow = pointers[0]
val bufferPtr = pointers[1]
if (bufferPtr == 0L || bufferPtr == -1L || nativeWindow == 0L || nativeWindow == -1L) {
return@use
}
retValue = page.renderPage(bufferPtr, startX, startY, drawSizeX, drawSizeY, renderAnnot)
surface?.let {
PdfPage.unlockSurface(pointers)
}
}
return retValue
}
@Deprecated(
"Use PdfPage.renderPageBitmap(bitmap, startX, startY, drawSizeX, drawSizeY)",
ReplaceWith(
"page.renderPageBitmap(bitmap, startX, startY, drawSizeX, drawSizeY)",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList")
fun renderPageBitmap(
pdfDocument: PdfDocument,
bitmap: Bitmap?,
pageIndex: Int,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
renderAnnot: Boolean = false,
) {
pdfDocument.openPage(pageIndex).use { page ->
page.renderPageBitmap(bitmap, startX, startY, drawSizeX, drawSizeY, renderAnnot)
}
}
@Deprecated(
"Use PdfPage.getPageLinks()",
ReplaceWith(
"page.getPageLinks()",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList")
fun getPageLinks(
pdfDocument: PdfDocument,
pageIndex: Int,
): List<PdfDocument.Link> {
pdfDocument.openPage(pageIndex).use { page ->
return page.getPageLinks()
}
}
@Deprecated(
"Use PdfPage.mapPageCoordsToDevice(startX, startY, sizeX, sizeY, rotate, pageX, pageY)",
ReplaceWith(
"page.mapPageCoordsToDevice(startX, startY, sizeX, sizeY, rotate, pageX, pageY)",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList")
fun mapPageCoordsToDevice(
pdfDocument: PdfDocument,
pageIndex: Int,
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
pageX: Double,
pageY: Double,
): Point {
pdfDocument.openPage(pageIndex).use { page ->
return page.mapPageCoordsToDevice(startX, startY, sizeX, sizeY, rotate, pageX, pageY)
}
}
@Deprecated(
"Use PdfPage.mapRectToDevice(startX, startY, sizeX, sizeY, rotate, coords)",
ReplaceWith(
"page.mapRectToDevice(startX, startY, sizeX, sizeY, rotate, coords)",
),
DeprecationLevel.WARNING,
)
@Suppress("LongParameterList")
fun mapRectToDevice(
pdfDocument: PdfDocument,
pageIndex: Int,
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
coords: RectF,
): Rect {
pdfDocument.openPage(pageIndex).use { page ->
return page.mapRectToDevice(startX, startY, sizeX, sizeY, rotate, coords)
}
}
companion object {
private val TAG = PdfiumCore::class.java.name
// synchronize native methods
val lock = Any()
val surfaceMutex = Mutex()
val isReady = InitLock()
init {
Log.d(TAG, "init")
Thread {
Log.d(TAG, "init thread start")
synchronized(lock) {
Log.d(TAG, "init in lock")
try {
System.loadLibrary("pdfium")
System.loadLibrary("pdfiumandroid")
isReady.markReady()
} catch (e: UnsatisfiedLinkError) {
Logger.e(TAG, e, "Native libraries failed to load")
}
Log.d(TAG, "init in lock")
}
}.start()
}
}
}

View file

@ -0,0 +1,29 @@
package io.legere.pdfiumandroid
/**
* An interface for providing custom data source to Pdfium.
*/
interface PdfiumSource : AutoCloseable {
/**
* Data length, in bytes
*/
val length: Long
/**
* Read data from the source.
*
* The position and size will never go out of range of the data source [length].
* It may be possible for Pdfium to call this function multiple times for the same position.
*
* @param position byte offset from the beginning of the data source
* @param buffer the buffer to read data into. Always have enough space to read [size] bytes.
* It should be filled starting from index 0.
* @param size the number of bytes to read. Never 0.
* @return number of bytes that was read, or a negative value to indicate an error.
*/
fun read(
position: Long,
buffer: ByteArray,
size: Int,
): Int
}

View file

@ -0,0 +1,42 @@
package io.legere.pdfiumandroid.suspend
import io.legere.pdfiumandroid.FindResult
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.Closeable
@Suppress("unused")
class FindResultKt(
private val findResult: FindResult,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
suspend fun findNext(): Boolean =
withContext(dispatcher) {
findResult.findNext()
}
suspend fun findPrev(): Boolean =
withContext(dispatcher) {
findResult.findPrev()
}
suspend fun getSchResultIndex(): Int =
withContext(dispatcher) {
findResult.getSchResultIndex()
}
suspend fun getSchCount(): Int =
withContext(dispatcher) {
findResult.getSchCount()
}
suspend fun closeFind() {
withContext(dispatcher) {
findResult.closeFind()
}
}
override fun close() {
findResult.closeFind()
}
}

View file

@ -0,0 +1,165 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.suspend
import android.graphics.Matrix
import android.graphics.RectF
import android.view.Surface
import androidx.annotation.Keep
import io.legere.pdfiumandroid.Logger
import io.legere.pdfiumandroid.PdfDocument
import io.legere.pdfiumandroid.PdfWriteCallback
import io.legere.pdfiumandroid.PdfiumCore
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.Closeable
/**
* PdfDocumentKt represents a PDF file and allows you to load pages from it.
* @property document the [PdfDocument] to wrap
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
* @constructor create a [PdfDocumentKt] from a [PdfDocument]
*/
@Suppress("TooManyFunctions")
@Keep
class PdfDocumentKt(
val document: PdfDocument,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
/**
* suspend version of [PdfDocument.getPageCount]
*/
suspend fun getPageCount(): Int =
withContext(dispatcher) {
document.getPageCount()
}
/**
* suspend version of [PdfDocument.getPageCharCounts]
*/
suspend fun getPageCharCounts(): IntArray =
withContext(dispatcher) {
document.getPageCharCounts()
}
/**
* suspend version of [PdfDocument.openPage]
*/
suspend fun openPage(pageIndex: Int): PdfPageKt =
withContext(dispatcher) {
PdfPageKt(document.openPage(pageIndex), dispatcher)
}
/**
* suspend version of [PdfDocument.deletePage]
*/
suspend fun deletePage(pageIndex: Int): Unit =
withContext(dispatcher) {
document.deletePage(pageIndex)
}
/**
* suspend version of [PdfDocument.openPages]
*/
suspend fun openPages(
fromIndex: Int,
toIndex: Int,
): List<PdfPageKt> =
withContext(dispatcher) {
document.openPages(fromIndex, toIndex).map { PdfPageKt(it, dispatcher) }
}
/**
* suspend version of [PdfDocument.renderPages]
*/
@Suppress("LongParameterList", "ComplexMethod", "ComplexCondition")
suspend fun renderPages(
surface: Surface,
pages: List<PdfPageKt>,
matrices: List<Matrix>,
clipRects: List<RectF>,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
renderCoroutinesDispatcher: CoroutineDispatcher,
): Boolean {
PdfiumCore.surfaceMutex.withLock {
return withContext(renderCoroutinesDispatcher) {
return@withContext document.renderPages(
surface,
pages.map { it.page },
matrices,
clipRects,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
}
}
}
/**
* suspend version of [PdfDocument.getDocumentMeta]
*/
suspend fun getDocumentMeta(): PdfDocument.Meta =
withContext(dispatcher) {
document.getDocumentMeta()
}
/**
* suspend version of [PdfDocument.getTableOfContents]
*/
suspend fun getTableOfContents(): List<PdfDocument.Bookmark> =
withContext(dispatcher) {
document.getTableOfContents()
}
/**
* suspend version of [PdfDocument.openTextPage]
*/
@Deprecated("use PdfPageKt.openTextPage", ReplaceWith("page.openTextPage()"))
@Suppress("DEPRECATION")
suspend fun openTextPage(page: PdfPageKt): PdfTextPageKt =
withContext(dispatcher) {
PdfTextPageKt(document.openTextPage(page.page), dispatcher)
}
/**
* suspend version of [PdfDocument.openTextPages]
*/
suspend fun openTextPages(
fromIndex: Int,
toIndex: Int,
): List<PdfTextPageKt> =
withContext(dispatcher) {
document.openTextPages(fromIndex, toIndex).map { PdfTextPageKt(it, dispatcher) }
}
/**
* suspend version of [PdfDocument.saveAsCopy]
*/
suspend fun saveAsCopy(callback: PdfWriteCallback): Boolean =
withContext(dispatcher) {
document.saveAsCopy(callback)
}
/**
* Close the document
* @throws IllegalArgumentException if document is closed
*/
override fun close() {
document.close()
}
fun safeClose(): Boolean =
try {
document.close()
true
} catch (e: IllegalStateException) {
Logger.e("PdfDocumentKt", e, "PdfDocumentKt.safeClose")
false
}
}

View file

@ -0,0 +1,402 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.suspend
import android.graphics.Bitmap
import android.graphics.Matrix
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.view.Surface
import androidx.annotation.Keep
import io.legere.pdfiumandroid.Logger
import io.legere.pdfiumandroid.PdfDocument
import io.legere.pdfiumandroid.PdfPage
import io.legere.pdfiumandroid.PdfPageObject
import io.legere.pdfiumandroid.PdfiumCore
import io.legere.pdfiumandroid.util.Size
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.Closeable
/**
* PdfPageKt represents a single page of a PDF file.
* @property page the [PdfPage] to wrap
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
*/
@Suppress("TooManyFunctions")
@Keep
class PdfPageKt(
val page: PdfPage,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
/**
* Open a text page
* @throws IllegalArgumentException if document is closed or the page cannot be loaded
*/
suspend fun openTextPage(): PdfTextPageKt =
withContext(dispatcher) {
PdfTextPageKt(page.openTextPage(), dispatcher)
}
/**
* suspend version of [PdfPage.getPageWidth]
*/
suspend fun getPageWidth(screenDpi: Int): Int =
withContext(dispatcher) {
page.getPageWidth(screenDpi)
}
/**
* suspend version of [PdfPage.getPageHeight]
*/
suspend fun getPageHeight(screenDpi: Int): Int =
withContext(dispatcher) {
page.getPageHeight(screenDpi)
}
/**
* suspend version of [PdfPage.getPageWidthPoint]
*/
suspend fun getPageWidthPoint(): Int =
withContext(dispatcher) {
page.getPageWidthPoint()
}
/**
* suspend version of [PdfPage.getPageHeightPoint]
*/
suspend fun getPageHeightPoint(): Int =
withContext(dispatcher) {
page.getPageHeightPoint()
}
/**
* suspend version of [PdfPage.getPageMatrix]
*/
suspend fun getPageMatrix(): Matrix? =
withContext(dispatcher) {
page.getPageMatrix()
}
/**
* suspend version of [PdfPage.getPageRotation]
*/
suspend fun getPageRotation(): Int =
withContext(dispatcher) {
page.getPageRotation()
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.getPageCropBox]
*/
suspend fun getPageCropBox(): RectF =
withContext(dispatcher) {
page.getPageCropBox()
}
/**
* suspend version of [PdfPage.getPageMediaBox]
*/
suspend fun getPageMediaBox(): RectF =
withContext(dispatcher) {
page.getPageMediaBox()
}
/**
* suspend version of [PdfPage.getPageBleedBox]
*/
suspend fun getPageBleedBox(): RectF =
withContext(dispatcher) {
page.getPageBleedBox()
}
/**
* suspend version of [PdfPage.getPageTrimBox]
*/
suspend fun getPageTrimBox(): RectF =
withContext(dispatcher) {
page.getPageTrimBox()
}
/**
* suspend version of [PdfPage.getPageArtBox]
*/
suspend fun getPageArtBox(): RectF =
withContext(dispatcher) {
page.getPageArtBox()
}
/**
* suspend version of [PdfPage.getPageBoundingBox]
*/
suspend fun getPageBoundingBox(): RectF =
withContext(dispatcher) {
page.getPageBoundingBox()
}
/**
* suspend version of [PdfPage.getPageSize]
*/
suspend fun getPageSize(screenDpi: Int): Size =
withContext(dispatcher) {
page.getPageSize(screenDpi)
}
/**
* suspend version of [PdfPage.renderPage]
*/
@Suppress("LongParameterList", "ComplexMethod", "ComplexCondition")
suspend fun renderPage(
surface: Surface?,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
renderAnnot: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
): Boolean {
var retValue: Boolean
PdfiumCore.surfaceMutex.withLock {
val sizes = IntArray(2)
val pointers = LongArray(2)
withContext(Dispatchers.Main) {
surface?.let {
PdfPage.lockSurface(
it,
sizes,
pointers,
)
}
}
val nativeWindow = pointers[0]
val bufferPtr = pointers[1]
if (bufferPtr == 0L || bufferPtr == -1L || nativeWindow == 0L || nativeWindow == -1L) {
return false
}
withContext(dispatcher) {
retValue =
page.renderPage(
bufferPtr,
startX,
startY,
drawSizeX,
drawSizeY,
renderAnnot,
canvasColor,
pageBackgroundColor,
)
}
withContext(Dispatchers.Main) {
PdfPage.unlockSurface(longArrayOf(nativeWindow, bufferPtr))
}
}
return retValue
}
/**
* suspend version of [PdfPage.renderPage]
*/
@Suppress("LongParameterList", "ComplexMethod", "ComplexCondition")
suspend fun renderPage(
surface: Surface?,
matrix: Matrix,
clipRect: RectF,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
): Boolean {
var retValue: Boolean
PdfiumCore.surfaceMutex.withLock {
val sizes = IntArray(2)
val pointers = LongArray(2)
withContext(Dispatchers.Main) {
surface?.let {
PdfPage.lockSurface(
it,
sizes,
pointers,
)
}
}
val nativeWindow = pointers[0]
val bufferPtr = pointers[1]
val surfaceWidth = sizes[0]
val surfaceHeight = sizes[1]
Logger.d("PdfPageKt", "nativeWindow: $nativeWindow")
if (bufferPtr == 0L || bufferPtr == -1L || nativeWindow == 0L || nativeWindow == -1L) {
return false
}
withContext(dispatcher) {
retValue =
page.renderPage(
bufferPtr,
surfaceWidth,
surfaceHeight,
matrix,
clipRect,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
}
withContext(Dispatchers.Main) {
surface?.let {
PdfPage.unlockSurface(longArrayOf(nativeWindow, bufferPtr))
}
}
}
return retValue
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.renderPageBitmap]
*/
suspend fun renderPageBitmap(
bitmap: Bitmap,
startX: Int,
startY: Int,
drawSizeX: Int,
drawSizeY: Int,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
) =
withContext(dispatcher) {
page.renderPageBitmap(
bitmap,
startX,
startY,
drawSizeX,
drawSizeY,
renderAnnot,
textMask,
canvasColor,
pageBackgroundColor,
)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.renderPageBitmap]
*/
suspend fun renderPageBitmap(
bitmap: Bitmap?,
matrix: Matrix,
clipRect: RectF,
renderAnnot: Boolean = false,
textMask: Boolean = false,
canvasColor: Int = 0xFF848484.toInt(),
pageBackgroundColor: Int = 0xFFFFFFFF.toInt(),
) =
withContext(dispatcher) {
page.renderPageBitmap(bitmap, matrix, clipRect, renderAnnot, textMask, canvasColor, pageBackgroundColor)
}
/**
* suspend version of [PdfPage.getPageLinks]
*/
suspend fun getPageLinks(): List<PdfDocument.Link> =
withContext(dispatcher) {
page.getPageLinks()
}
/**
* suspend version of [PdfPage.getPageObjects]
*/
suspend fun getPageObjects(): List<PdfPageObject> =
withContext(dispatcher) {
page.getPageObjects()
}
/**
* suspend version of [PdfPage.mapPageCoordsToDevice]
*/
@Suppress("LongParameterList")
suspend fun mapPageCoordsToDevice(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
pageX: Double,
pageY: Double,
): Point =
withContext(dispatcher) {
page.mapPageCoordsToDevice(startX, startY, sizeX, sizeY, rotate, pageX, pageY)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.mapDeviceCoordsToPage]
*/
suspend fun mapDeviceCoordsToPage(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
deviceX: Int,
deviceY: Int,
): PointF =
withContext(dispatcher) {
page.mapDeviceCoordsToPage(startX, startY, sizeX, sizeY, rotate, deviceX, deviceY)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.mapRectToDevice]
*/
suspend fun mapRectToDevice(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
coords: RectF,
): Rect =
withContext(dispatcher) {
page.mapRectToDevice(startX, startY, sizeX, sizeY, rotate, coords)
}
@Suppress("LongParameterList")
/**
* suspend version of [PdfPage.mapRectToPage]
*/
suspend fun mapRectToPage(
startX: Int,
startY: Int,
sizeX: Int,
sizeY: Int,
rotate: Int,
coords: Rect,
): RectF =
withContext(dispatcher) {
page.mapRectToPage(startX, startY, sizeX, sizeY, rotate, coords)
}
/**
* Closes the page
*/
override fun close() {
page.close()
}
fun safeClose(): Boolean =
try {
page.close()
true
} catch (e: IllegalStateException) {
Logger.e("PdfPageKt", e, "PdfPageKt.safeClose")
false
}
}

View file

@ -0,0 +1,47 @@
package io.legere.pdfiumandroid.suspend
import android.graphics.RectF
import io.legere.pdfiumandroid.PdfPageLink
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.Closeable
class PdfPageLinkKt(
val pageLink: PdfPageLink,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
suspend fun countWebLinks(): Int =
withContext(dispatcher) {
pageLink.countWebLinks()
}
suspend fun getURL(
index: Int,
length: Int,
): String? =
withContext(dispatcher) {
pageLink.getURL(index, length)
}
suspend fun countRects(index: Int): Int =
withContext(dispatcher) {
pageLink.countRects(index)
}
suspend fun getRect(
linkIndex: Int,
rectIndex: Int,
): RectF =
withContext(dispatcher) {
pageLink.getRect(linkIndex, rectIndex)
}
suspend fun getTextRange(index: Int): Pair<Int, Int> =
withContext(dispatcher) {
pageLink.getTextRange(index)
}
override fun close() {
pageLink.close()
}
}

View file

@ -0,0 +1,154 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.suspend
import android.graphics.RectF
import androidx.annotation.Keep
import io.legere.pdfiumandroid.FindFlags
import io.legere.pdfiumandroid.Logger
import io.legere.pdfiumandroid.PdfTextPage
import io.legere.pdfiumandroid.WordRangeRect
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.Closeable
/**
* PdfTextPageKt represents a single text page of a PDF file.
* @property page the [PdfTextPage] to wrap
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
*/
@Suppress("TooManyFunctions")
@Keep
class PdfTextPageKt(
val page: PdfTextPage,
private val dispatcher: CoroutineDispatcher,
) : Closeable {
/**
* suspend version of [PdfTextPage.textPageCountChars]
*/
suspend fun textPageCountChars(): Int =
withContext(dispatcher) {
page.textPageCountChars()
}
/**
* suspend version of [PdfTextPage.textPageGetText]
*/
suspend fun textPageGetText(
startIndex: Int,
length: Int,
): String? =
withContext(dispatcher) {
page.textPageGetText(startIndex, length)
}
/**
* suspend version of [PdfTextPage.textPageGetUnicode]
*/
suspend fun textPageGetUnicode(index: Int): Char =
withContext(dispatcher) {
page.textPageGetUnicode(index)
}
/**
* suspend version of [PdfTextPage.textPageGetCharBox]
*/
suspend fun textPageGetCharBox(index: Int): RectF? =
withContext(dispatcher) {
page.textPageGetCharBox(index)
}
/**
* suspend version of [PdfTextPage.textPageGetCharIndexAtPos]
*/
suspend fun textPageGetCharIndexAtPos(
x: Double,
y: Double,
xTolerance: Double,
yTolerance: Double,
): Int =
withContext(dispatcher) {
page.textPageGetCharIndexAtPos(x, y, xTolerance, yTolerance)
}
/**
* suspend version of [PdfTextPage.textPageCountRects]
*/
suspend fun textPageCountRects(
startIndex: Int,
count: Int,
): Int =
withContext(dispatcher) {
page.textPageCountRects(startIndex, count)
}
/**
* suspend version of [PdfTextPage.textPageGetRect]
*/
suspend fun textPageGetRect(rectIndex: Int): RectF? =
withContext(dispatcher) {
page.textPageGetRect(rectIndex)
}
/**
* suspend version of [PdfTextPage.textPageGetRectsForRanges]
*/
suspend fun textPageGetRectsForRanges(wordRanges: IntArray): List<WordRangeRect>? =
withContext(dispatcher) {
page.textPageGetRectsForRanges(wordRanges)
}
/**
* suspend version of [PdfTextPage.textPageGetBoundedText]
*/
suspend fun textPageGetBoundedText(
rect: RectF,
length: Int,
): String? =
withContext(dispatcher) {
page.textPageGetBoundedText(rect, length)
}
/**
* suspend version of [PdfTextPage.getFontSize]
*/
suspend fun getFontSize(charIndex: Int): Double =
withContext(dispatcher) {
page.getFontSize(charIndex)
}
suspend fun findStart(
findWhat: String,
flags: Set<FindFlags>,
startIndex: Int,
): FindResultKt? =
withContext(dispatcher) {
val findResult = page.findStart(findWhat, flags, startIndex)
if (findResult == null) {
null
} else {
FindResultKt(findResult, dispatcher)
}
}
suspend fun loadWebLink(): PdfPageLinkKt =
withContext(dispatcher) {
PdfPageLinkKt(page.loadWebLink(), dispatcher)
}
/**
* Close the page and free all resources.
*/
override fun close() {
page.close()
}
fun safeClose(): Boolean =
try {
page.close()
true
} catch (e: IllegalStateException) {
Logger.e("PdfTextPageKt", e, "PdfTextPageKt.safeClose")
false
}
}

View file

@ -0,0 +1,81 @@
@file:Suppress("unused")
package io.legere.pdfiumandroid.suspend
import android.os.ParcelFileDescriptor
import androidx.annotation.Keep
import io.legere.pdfiumandroid.PdfiumCore
import io.legere.pdfiumandroid.PdfiumSource
import io.legere.pdfiumandroid.util.Config
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
/**
* PdfiumCoreKt is the main entry-point for access to the PDFium API.
* @property dispatcher the [CoroutineDispatcher] to use for suspending calls
* @constructor create a [PdfiumCoreKt] from a [PdfiumCore]
*/
@Keep
class PdfiumCoreKt(
private val dispatcher: CoroutineDispatcher,
config: Config = Config(),
) {
private val coreInternal = PdfiumCore(config = config)
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(fd: ParcelFileDescriptor): PdfDocumentKt =
withContext(dispatcher) {
PdfDocumentKt(coreInternal.newDocument(fd), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(
fd: ParcelFileDescriptor,
password: String?,
): PdfDocumentKt =
withContext(dispatcher) {
PdfDocumentKt(coreInternal.newDocument(fd, password), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(data: ByteArray?): PdfDocumentKt =
withContext(dispatcher) {
PdfDocumentKt(coreInternal.newDocument(data), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(
data: ByteArray?,
password: String?,
): PdfDocumentKt =
withContext(dispatcher) {
PdfDocumentKt(coreInternal.newDocument(data, password), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(data: PdfiumSource): PdfDocumentKt =
withContext(dispatcher) {
PdfDocumentKt(coreInternal.newDocument(data), dispatcher)
}
/**
* suspend version of [PdfiumCore.newDocument]
*/
suspend fun newDocument(
data: PdfiumSource,
password: String?,
): PdfDocumentKt =
withContext(dispatcher) {
PdfDocumentKt(coreInternal.newDocument(data, password), dispatcher)
}
}

View file

@ -0,0 +1,33 @@
package io.legere.pdfiumandroid.util
import androidx.annotation.Keep
import io.legere.pdfiumandroid.DefaultLogger
import io.legere.pdfiumandroid.LoggerInterface
var pdfiumConfig = Config()
@Keep
enum class AlreadyClosedBehavior {
EXCEPTION,
IGNORE,
}
@Keep
data class Config(
val logger: LoggerInterface = DefaultLogger(),
val alreadyClosedBehavior: AlreadyClosedBehavior = AlreadyClosedBehavior.EXCEPTION,
)
fun handleAlreadyClosed(isClosed: Boolean): Boolean {
if (isClosed) {
when (pdfiumConfig.alreadyClosedBehavior) {
AlreadyClosedBehavior.EXCEPTION -> error("Already closed")
AlreadyClosedBehavior.IGNORE ->
pdfiumConfig.logger.d(
"PdfiumCore",
"Already closed",
)
}
}
return isClosed
}

View file

@ -0,0 +1,22 @@
package io.legere.pdfiumandroid.util
import java.util.concurrent.Semaphore
class InitLock {
private val semaphore = Semaphore(0)
private var isInitialized = false
fun markReady() {
isInitialized = true
semaphore.release()
}
// We use a mutex to make sure only the
// first thread waits on the semaphore
@Synchronized
fun waitForReady() {
if (!isInitialized) {
semaphore.acquire()
}
}
}

View file

@ -0,0 +1,33 @@
package io.legere.pdfiumandroid.util
import io.legere.pdfiumandroid.Logger
import io.legere.pdfiumandroid.PdfiumSource
internal class PdfiumNativeSourceBridge(
private val source: PdfiumSource,
) {
private var buffer: ByteArray? = null
@Suppress("TooGenericExceptionCaught")
fun read(
position: Long,
size: Long,
): Int =
try {
require(size <= Int.MAX_VALUE) { "size is too large" }
val trimmedSize = size.toInt()
var buffer = buffer
if (buffer == null || buffer.size < size) {
buffer = ByteArray(trimmedSize).also { this.buffer = it }
}
val bytesRead = source.read(position, buffer, trimmedSize)
// Pdfium expects 0 for error while Java/Kotlin usually return a negative value
if (bytesRead <= 0) 0 else bytesRead
} catch (t: Throwable) {
// This is to prevent the exception to go to the native code level
Logger.e("PdfiumNativeSourceBridge", t, "read failed")
0
}
}

View file

@ -0,0 +1,14 @@
package io.legere.pdfiumandroid.util
import androidx.annotation.Keep
/**
* Size is a simple value class that represents a width and height.
* @property width the width
* @property height the height
*/
@Keep
data class Size(
val width: Int,
val height: Int,
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,115 @@
package io.legere.pdfiumandroid.util
import com.google.common.truth.Truth.assertThat
import io.legere.pdfiumandroid.PdfiumSource
import junit.framework.TestCase.fail
import org.junit.Test
class PdfiumNativeSourceBridgeTest {
@Test
@Suppress("SwallowedException")
fun paramsDispatchedCorrectly() {
var lastPosition = -1L
var lastBufferSize = -1
var lastSize = -1
val bridge =
PdfiumNativeSourceBridge(
source =
mockCustomSource { position, buffer, size ->
lastPosition = position
lastBufferSize = buffer.size
lastSize = size
size
},
)
try {
assertThat(bridge.read(1, 2)).isEqualTo(2)
assertThat(lastPosition).isEqualTo(1)
assertThat(lastBufferSize).isEqualTo(2)
assertThat(lastSize).isEqualTo(2)
} catch (t: Exception) {
fail("Should not throw exception")
}
}
@Test
@Suppress("SwallowedException")
fun sizeExceedsIntRange() {
val bridge = PdfiumNativeSourceBridge(mockCustomSource())
try {
assertThat(bridge.read(0, Long.MAX_VALUE)).isEqualTo(0)
} catch (t: Exception) {
fail("Should not throw exception")
}
}
@Test
@Suppress("SwallowedException")
fun sourceThrowsException() {
val bridge = PdfiumNativeSourceBridge(mockCustomSource { _, _, _ -> error("Exception!") })
try {
assertThat(bridge.read(0, 1)).isEqualTo(0)
} catch (t: Exception) {
fail("Should not throw exception")
}
}
@Test
@Suppress("SwallowedException")
fun sourceReturnsNegativeValue() {
val bridge = PdfiumNativeSourceBridge(mockCustomSource { _, _, _ -> -1 })
try {
assertThat(bridge.read(0, 1)).isEqualTo(0)
} catch (t: Exception) {
fail("Should not throw exception")
}
}
@Test
@Suppress("SwallowedException")
fun bufferRescales() {
var lastBufferSize = -1
val bridge =
PdfiumNativeSourceBridge(
source =
mockCustomSource { _, buffer, _ ->
lastBufferSize = buffer.size
buffer.size
},
)
try {
assertThat(bridge.read(0, 1)).isEqualTo(1)
assertThat(lastBufferSize).isEqualTo(1)
assertThat(bridge.read(0, 2)).isEqualTo(2)
assertThat(lastBufferSize).isEqualTo(2)
} catch (t: Exception) {
fail("Should not throw exception")
}
}
// todo: replace with MockK or Mockito when available
private fun mockCustomSource(
length: Long = 0L,
read: (Long, ByteArray, Int) -> Int = { _, _, size -> size },
): PdfiumSource =
object : PdfiumSource {
override val length: Long
get() = length
override fun read(
position: Long,
buffer: ByteArray,
size: Int,
): Int = read(position, buffer, size)
override fun close() {
// nothing to close
}
}
}