Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ internal fun PdfContinuousPage(
onMoveImageToPage: (fromPage: Int, img: PdfMarkup.ImageMarkup, toPage: Int) -> Boolean = { _, _, _ -> false }
) {
var imageDragging by remember { mutableStateOf(false) }
// A dragged text box / shape / note can swing below this page's bottom edge toward the next
// page while still being moved. Compose doesn't clip a page's overlay to its own bounds, and
// the next page is a later LazyColumn item, so without raising this page too it drew on top
// of the markup being dragged over it.
var markupDragging by remember { mutableStateOf(false) }
var draftPoints by remember(page, activeTool) { mutableStateOf<List<Offset>>(emptyList()) }
var draftRectStart by remember(page, activeTool) { mutableStateOf<Offset?>(null) }
var draftRectEnd by remember(page, activeTool) { mutableStateOf<Offset?>(null) }
Expand Down Expand Up @@ -245,9 +250,10 @@ internal fun PdfContinuousPage(

Box(
modifier
// While an image is dragged across a page boundary, this page draws above its
// neighbours so the image isn't hidden behind the next page.
.zIndex(if (imageDragging) 1f else 0f)
// While an image or a selected text box / shape / note is dragged across a page
// boundary, this page draws above its neighbours so the markup isn't hidden behind
// the next page.
.zIndex(if (imageDragging || markupDragging) 1f else 0f)
.fillMaxWidth()
.padding(vertical = 6.dp)
// The placeholder is trimmed too, so the page keeps its height when the bitmap lands.
Expand Down Expand Up @@ -734,6 +740,7 @@ internal fun PdfContinuousPage(
total = Offset.Zero
val pPage = Offset(local.x + (bb?.left ?: 0f) - pad, local.y + (bb?.top ?: 0f) - pad)
mode = if (bb != null && cur.isResizable() && (pPage - bb.bottomRight).getDistance() <= 64f) 2 else 1
markupDragging = true
onInteraction()
},
onDrag = { ch, drag ->
Expand All @@ -745,8 +752,8 @@ internal fun PdfContinuousPage(
marks[selectedMarkupIndex] = if (mode == 2) m0.resizedFrom(b0, total) else m0.translated(total)
onInteraction()
},
onDragEnd = { mode = 0; frozen = null },
onDragCancel = { mode = 0; frozen = null }
onDragEnd = { mode = 0; frozen = null; markupDragging = false },
onDragCancel = { mode = 0; frozen = null; markupDragging = false }
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ class PdfViewerViewModel(private val openPdfUseCase: OpenPdfUseCase) : ViewModel
// gives that race a head start.
private const val PREFETCH_AHEAD = 2
private const val PREFETCH_BEHIND = 1
// Above this, the on-open form/link scan is skipped outright rather than risking PDFBox's
// temp-file-backed load on a huge document for a feature (form fields, in-document links)
// that scanned books essentially never have.
private const val FORM_SCAN_SIZE_LIMIT_BYTES = 150L * 1024 * 1024
}

fun openPdf(context: Context, uri: Uri, password: String? = null) {
Expand Down Expand Up @@ -394,14 +398,24 @@ class PdfViewerViewModel(private val openPdfUseCase: OpenPdfUseCase) : ViewModel
renderedByOfficeEngine = renderedUri.path?.contains("/office-pdf/") == true
)
// Fillable fields and links, read off the main thread in one pass; most PDFs have
// no form and stay empty.
// no form and stay empty. This used to load the whole file into PDFBox's in-memory
// model unconditionally on every open — a multi-hundred-MB scanned book (all image
// XObjects, no forms) OOM'd right after opening, before the user did anything. Above
// FORM_SCAN_SIZE_LIMIT_BYTES skip the scan (forms/links are rare in huge scans and
// not worth the risk); below it, load through a temp-file-backed buffer instead of
// the heap.
val formDocUri = doc.uri
viewModelScope.launch {
val (widgets, links) = withContext(Dispatchers.IO) {
runCatching {
if (doc.sizeBytes > FORM_SCAN_SIZE_LIMIT_BYTES) {
emptyMap<Int, List<com.kyant.pdfcore.form.PdfFormWidget>>() to emptyMap()
} else runCatching {
com.kyant.pdfcore.internal.PdfBox.ensureInitialized(context)
context.contentResolver.openInputStream(formDocUri)?.use { input ->
com.tom_roush.pdfbox.pdmodel.PDDocument.load(input).use { pd ->
com.tom_roush.pdfbox.pdmodel.PDDocument.load(
input,
com.tom_roush.pdfbox.io.MemoryUsageSetting.setupTempFileOnly()
).use { pd ->
val w = runCatching { com.kyant.pdfcore.form.PdfFormWidgets.read(pd) }.getOrDefault(emptyMap())
val l = runCatching { com.kyant.pdfcore.link.PdfLinks.read(pd) }.getOrDefault(emptyMap())
w to l
Expand Down
23 changes: 19 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Read and annotate PDFs, sign forms, work with Office documents, edit spreadsheet

<p>
<a href="https://github.com/Chethan616/ClearPDF/releases/latest"><strong>Download the latest ClearPDF</strong></a>
· <a href="https://apps.obtainium.imranr.dev/redirect?r=https://github.com/Chethan616/ClearPDF"><strong>Add to Obtainium</strong></a>
· <a href="https://github.com/Chethan616/ClearPDF/releases">All releases</a>
· <a href="https://github.com/Chethan616/ClearPDF/issues/new/choose">Report a problem or request a feature</a>
· <a href="CONTRIBUTING.md">Contribute</a>
Expand All @@ -20,6 +21,8 @@ Read and annotate PDFs, sign forms, work with Office documents, edit spreadsheet
<a href="LICENSE"><img src="https://img.shields.io/github/license/Chethan616/ClearPDF?style=flat-square&color=30D158" alt="MIT license"></a>
<img src="https://img.shields.io/badge/Android-6.0%2B-3DDC84?style=flat-square&logo=android&logoColor=white" alt="Android 6.0 or newer">
<a href="https://github.com/Chethan616/ClearPDF/actions/workflows/android.yml"><img src="https://img.shields.io/github/actions/workflow/status/Chethan616/ClearPDF/android.yml?branch=main&style=flat-square&label=Android%20build" alt="Android build status"></a>
<a href="https://apps.obtainium.imranr.dev/redirect?r=https://github.com/Chethan616/ClearPDF"><img src="https://img.shields.io/badge/Obtainium-Add%20repo-1B5E20?style=flat-square" alt="Add to Obtainium"></a>
<img src="https://img.shields.io/badge/GitHub%20Trending-%232-FF375F?style=flat-square" alt="Number 2 on GitHub Trending">
</p>

</div>
Expand All @@ -36,13 +39,17 @@ ClearPDF was featured by HowToMen. [Watch the video from 9:08](https://www.youtu
<img src="screenshots/light%20home%20screen.jpg" width="24%" alt="ClearPDF home screen in light mode">
<img src="screenshots/light%20pdf%20tools.jpg" width="24%" alt="ClearPDF PDF tools in light mode">
<img src="screenshots/light%20settings%20screen.jpg" width="24%" alt="ClearPDF settings in light mode">
<img src="demo/3.jpg" width="24%" alt="ClearPDF PDF editing tools">
<img src="screenshots/docx%20editing.jpg" width="24%" alt="ClearPDF opening a Word document">
</p>
<p align="center">
<img src="screenshots/dark%20home%20screen.jpg" width="24%" alt="ClearPDF home screen in dark mode">
<img src="screenshots/dark%20pdf%20tools.jpg" width="24%" alt="ClearPDF PDF tools in dark mode">
<img src="screenshots/dark%20settings%20screen.jpg" width="24%" alt="ClearPDF settings in dark mode">
<img src="demo/4.jpg" width="24%" alt="ClearPDF annotation tools">
<img src="screenshots/xlsx%20editing.jpg" width="24%" alt="ClearPDF editing a spreadsheet">
</p>

<p align="center">
<img src="screenshots/pdf_scroll.gif" width="40%" alt="Scrolling a PDF in the ClearPDF viewer">
</p>

Short walkthroughs: [onboarding](demo/1.mp4) · [PDF viewer](demo/2.mp4).
Expand Down Expand Up @@ -87,7 +94,7 @@ ClearPDF annotates PDFs and edits form fields, signatures, and pages, but it doe

## Download

The current stable release is **V4 (app version 2.0.0)**. Open the [V4 release page](https://github.com/Chethan616/ClearPDF/releases/tag/v4) and choose the APK that matches your device:
The current stable release is **V5.1 (app version 2.1.1)**. Open the [latest release page](https://github.com/Chethan616/ClearPDF/releases/latest) and choose the APK that matches your device:

| APK suffix | Choose it for |
|---|---|
Expand All @@ -96,7 +103,15 @@ The current stable release is **V4 (app version 2.0.0)**. Open the [V4 release p
| <code>-x86_64.apk</code> or <code>-x86.apk</code> | x86-based emulators and devices |
| <code>-universal.apk</code> | A larger package containing the release's supported ABIs |

The GitHub APKs are the FOSS distribution. The optional Office engine is downloaded only when requested in Settings. For future builds, use the [latest release page](https://github.com/Chethan616/ClearPDF/releases/latest). You can also add the repository to [Obtainium](https://github.com/ImranR98/Obtainium) for release update notifications.
The GitHub APKs are the FOSS distribution. The optional Office engine is downloaded only when requested in Settings.

### Install via Obtainium

[Obtainium](https://github.com/ImranR98/Obtainium) tracks this repository's releases and installs/updates the APK for you, no manual downloads.

1. Open Obtainium → **Add App**.
2. Paste `https://github.com/Chethan616/ClearPDF`, or tap **[Add to Obtainium](https://apps.obtainium.imranr.dev/redirect?r=https://github.com/Chethan616/ClearPDF)** on a device with Obtainium installed.
3. Pick the APK suffix for your device from the table above when prompted.

## Privacy and network use

Expand Down
Binary file modified screenshots/dark home screen.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/dark pdf tools.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/dark settings screen.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/docx editing.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/light home screen.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/light pdf tools.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/light settings screen.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/pdf_scroll.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/social_preview_1280x640.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/xlsx editing.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading