Skip to content

fix(pdfviewer): skip page text extraction when enableTextSelection is false - #2560

Open
ali-pouneh wants to merge 1 commit into
syncfusion:masterfrom
ali-pouneh:fix/pdfviewer-text-extraction-respects-enable-text-selection
Open

ali-pouneh wants to merge 1 commit into
syncfusion:masterfrom
ali-pouneh:fix/pdfviewer-text-extraction-respects-enable-text-selection

Conversation

@ali-pouneh

Copy link
Copy Markdown

Summary

SfPdfViewerState._checkVisiblePages() calls PdfTextExtractor.extractText() for every visible page unconditionally — the enableTextSelection property is never consulted. On large documents this produces an unrecoverable Dart Out of Memory and an Android ANR.

This PR guards the three extractText() call sites with widget.enableTextSelection.

The problem

In lib/src/pdfviewer.dart, enableTextSelection is referenced in exactly one place (line 3786), where it is forwarded to the page widget for the selection UI. It does not gate extraction. Meanwhile _checkVisiblePages() extracts text at three sites (single-page mode, and the forward/backward visible-page loops), and the results are cached in _pageTextExtractor, which is cleared only when the document changes — so every page visited during a session stays resident.

Extraction runs synchronously on the UI isolate. The combination means an application that sets enableTextSelection: false still pays the full parse cost, and on a large document the allocation eventually fails.

Reproduction

Android release build, Pixel 9 / Android 17, syncfusion_flutter_pdfviewer 33.2.7, ~50-page PDF, PdfPageLayoutMode.continuous, enableTextSelection: false. Paging through the document with PdfViewerController.nextPage() reliably reproduces it.

E flutter : [ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: Out of Memory
E flutter : #0      List._allocateData (dart:core-patch/growable_array.dart)
E flutter : #1      List._grow (dart:core-patch/growable_array.dart)
E flutter : #2      List.addAll (dart:core-patch/growable_array.dart)
E flutter : #3      new PdfStream (package:syncfusion_flutter_pdf/.../primitives/pdf_stream.dart)
E flutter : #4      PdfParser._readStream (package:syncfusion_flutter_pdf/.../io/pdf_parser.dart)
E flutter : #13     PageResourceLoader.getFormResources (.../pdf_text_extractor/page_resource_loader.dart)
E flutter : #17     PageResourceLoader.getPageResources (.../pdf_text_extractor/page_resource_loader.dart)
E flutter : #18     PdfTextExtractor._getText (.../pdf_text_extractor/pdf_text_extractor.dart)
E flutter : #19     PdfTextExtractor._extractText (.../pdf_text_extractor/pdf_text_extractor.dart)
E flutter : #20     SfPdfViewerState._checkVisiblePages (package:syncfusion_flutter_pdfviewer/src/pdfviewer.dart)
E flutter : #21     SfPdfViewerState._getTileImage (package:syncfusion_flutter_pdfviewer/src/pdfviewer.dart)
E flutter : #22     PdfScrollableState.jumpTo (package:syncfusion_flutter_pdfviewer/src/control/pdf_scrollable.dart)
E flutter : #26     PdfViewerController.nextPage (package:syncfusion_flutter_pdfviewer/src/pdfviewer.dart)

Followed by:

I Choreographer: Skipped 1081 frames!  The application may be doing too much work on its main thread.
E ActivityManager: ANR in <app> (<app>/.MainActivity)
E ActivityManager: Reason: Input dispatching timed out ... Waited 5002ms for MotionEvent

Note frames #20#26: the extraction is reached from the UI isolate via a tap handler, which is what turns the memory problem into an ANR.

Measured effect

adb shell dumpsys meminfo, same device, same document, same navigation, release builds:

Before After
Unknown (Dart/native) 1,322,722 KB 40,620 KB
TOTAL PSS 1,667,174 KB 464,484 KB
Out of Memory occurrences 3 0
ANRs 2 0

Page rendering is also visibly faster, since the UI isolate no longer parses content streams for every page that scrolls into view.

Scope and compatibility

enableTextSelection defaults to true, so existing behaviour is unchanged for applications that do not opt out. Only applications that explicitly set enableTextSelection: false are affected, and for those the extracted text was already unusable for selection.

One thing worth a maintainer's eye: if _pageTextExtractor is consumed by any path other than selection when the flag is off, that path would now see an empty cache. I could not find one, but I do not know the codebase as well as you do.

Separately, and not addressed here: _pageTextExtractor has no eviction policy while a document is open, so even with enableTextSelection: true memory grows with the number of pages visited. A bounded cache would likely help large documents independently of this change.

Verification

Verified on a patched local copy of 33.2.7 consumed through a dependency_overrides path override, in a release build on a physical device. The same three call sites are present and unguarded in 34.1.29 (this branch) and in the published 34.2.8.

… false

_checkVisiblePages() calls PdfTextExtractor.extractText() unconditionally
for every visible page, regardless of the enableTextSelection property.
The extraction runs synchronously on the UI isolate and its results are
cached in _pageTextExtractor, which is only cleared when the document
changes.

On large documents this causes an unrecoverable "Out of Memory" in the
Dart heap and an Android ANR, because the parse allocates growable byte
lists per content stream while the cache keeps every previously visited
page alive.

Guard the three extractText() call sites with widget.enableTextSelection
so applications that disable text selection do not pay for extraction
they cannot use.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant