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>
@Vikassekar Vikassekar added pdf viewer PDF viewer component waiting for customer response Cannot make further progress until the customer responds. labels Sep 21, 2026
@AbinayaSF4962

Copy link
Copy Markdown

Hi @ali-pouneh ,

Thank you for sharing your proposal and providing the patch to an issue where unnecessary extractText() calls cause OOM and ANR on large PDFs.
We are unable to reproduce the reported issue on our end. Please find attached the sample that we used to reproduce the issue, along with a video recording.
Could you please review the attached sample and let us know whether the issue still occurs on your end? If so, could you kindly share the modified version of the sample, the document on which the issue occurred.

Please note that, although the Flutter PDF Viewer source code is publicly available on GitHub for transparency, we do not accept direct contributions to the repository. This policy ensures consistency and quality across all releases. Nevertheless, your feedback and suggestions are highly valued, and we encourage you to continue sharing ideas through our official support channels.
Thank you for your understanding and continued support.

sfpdfviewer_gh2560.zip

sfpdfviewer_gh2560.mp4

Regards,
Abinaya E

@ali-pouneh

Copy link
Copy Markdown
Author

Hi @AbinayaSF4962, thanks for looking.

Reproduction depends on document size and device memory, and a small sample won't show it.

The underlying issue doesn't need a repro, though: enableTextSelection: false currently has no effect on the three extractText() calls in _checkVisiblePages(). You can confirm that by reading the diff.

We've already shipped our app with this patch applied via a path override, and it resolved the OOM and ANR for us. So we're unblocked. I'm raising it here because other users setting enableTextSelection: false are paying the same cost without knowing it, and it seems worth fixing upstream.

Two things I'd appreciate a direct answer on:

  • Is it intentional that text extraction runs regardless of enableTextSelection? If so, why?
  • Is _pageTextExtractor consumed by anything other than selection?

@AbinayaSF4962

Copy link
Copy Markdown

Hi @ali-pouneh ,

Please find the details below,

SI.NO Query Response
1 Is it intentional that text extraction runs regardless of enableTextSelection? If so, why? Yes, this is intentional. Text extraction is performed unconditionally on load (on mobile, Windows, and macOS platforms) because it serves a feature other than text selection - specifically, the text search functionality (PdfViewerController.searchText).
2 Is _pageTextExtractor consumed by anything other than selection? Yes — _pageTextExtractor is consumed by another feature: the accessibility (semantic) label of each PDF page.

Regards,
Abinaya E

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pdf viewer PDF viewer component waiting for customer response Cannot make further progress until the customer responds.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants