Selecting text in Android PDF viewer
The TextSelectionManager interface provides two listeners:
These listeners enable you to monitor text selection changes. When the user long-presses text, PdfFragment enters text selection mode and shows the TextSelectionPopupToolbar next to the selection. It offers actions such as copy, speak, share, search, highlight, underline, strikeout, redact, link, and annotate. Which of them appear depends on the configuration, the license, and the document’s permissions.
Registering custom listeners
Create custom listeners for TextSelectionManager to handle selection changes and entry into or exit from text selection mode:
class MyActivity : PdfActivity(), TextSelectionManager.OnTextSelectionChangeListener, TextSelectionManager.OnTextSelectionModeChangeListener {
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requirePdfFragment().addOnTextSelectionModeChangeListener(this) requirePdfFragment().addOnTextSelectionChangeListener(this) }
override fun onDestroy() { pdfFragment?.removeOnTextSelectionModeChangeListener(this) pdfFragment?.removeOnTextSelectionChangeListener(this) super.onDestroy() }
override fun onEnterTextSelectionMode(controller: TextSelectionController) { Log.i(TAG, "Text selection mode has started.") }
override fun onExitTextSelectionMode(controller: TextSelectionController) { Log.i(TAG, "Text selection mode has ended.") }
override fun onBeforeTextSelectionChange(newTextSelection: TextSelection?, currentTextSelection: TextSelection?): Boolean { // Return `false` to prevent changes to the current selection state, if needed. return true }
override fun onAfterTextSelectionChange(newTextSelection: TextSelection?, oldTextSelection: TextSelection?) { if (newTextSelection != null) { Log.i(TAG, "Selected text was: ${newTextSelection.text}") } else { Log.i(TAG, "Text selection is cleared.") } }
companion object { private const val TAG = "MyActivity.TextSelection" }}public class MyActivity extends PdfActivity implements TextSelectionManager.OnTextSelectionChangeListener, TextSelectionManager.OnTextSelectionModeChangeListener{
private static final String TAG = "MyActivity.TextSelection";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requirePdfFragment().addOnTextSelectionModeChangeListener(this); requirePdfFragment().addOnTextSelectionChangeListener(this); }
@Override protected void onDestroy() { getPdfFragment().removeOnTextSelectionModeChangeListener(this); getPdfFragment().removeOnTextSelectionChangeListener(this); super.onDestroy(); }
@Override public void onEnterTextSelectionMode(@NonNull TextSelectionController controller) { Log.i(TAG, "Text selection mode has started."); }
@Override public void onExitTextSelectionMode(@NonNull TextSelectionController controller) { Log.i(TAG, "Text selection mode has ended."); }
@Override public boolean onBeforeTextSelectionChange(@Nullable TextSelection newTextSelection, @Nullable TextSelection currentTextSelection) { // Return `false` to prevent changes to the current selection state, if needed. return true; }
@Override public void onAfterTextSelectionChange(@Nullable TextSelection newTextSelection, @Nullable TextSelection oldTextSelection) { if (newTextSelection != null) { Log.i(TAG, String.format("Selected text was: %s", newTextSelection.text)); } else { Log.i(TAG, "Text selection is cleared."); } }}Implement these listeners to respond to text selection events.
These listeners don’t override the default behavior.
To customize toolbar actions, register OnPreparePopupToolbarListener with setOnPreparePopupToolbarListener() on PdfFragment. Edit the toolbar’s menuItems in onPrepareTextSelectionPopupToolbar().
To run actions from your own UI, use the TextSelectionController that onEnterTextSelectionMode() receives.
Customize text selection actions
Register an OnPreparePopupToolbarListener on the fragment. Its onPrepareTextSelectionPopupToolbar callback runs each time the popup is about to show, with the toolbar’s default PopupToolbarMenuItems in menuItems. Replace the list to remove or add actions, and handle taps on your own items with setOnPopupToolbarItemClickedListener:
pdfFragment.setOnPreparePopupToolbarListener(object : OnPreparePopupToolbarListener { override fun onPrepareTextSelectionPopupToolbar(toolbar: TextSelectionPopupToolbar) { // Keep copy and highlight, drop the rest, and add a custom action. val keep = setOf( R.id.pspdf__text_selection_toolbar_item_copy, R.id.pspdf__text_selection_toolbar_item_highlight, ) toolbar.menuItems = toolbar.menuItems.filter { it.id in keep } + PopupToolbarMenuItem(R.id.custom_text_action, R.string.custom_text_action, R.drawable.ic_my_action)
toolbar.setOnPopupToolbarItemClickedListener { item -> if (item.id == R.id.custom_text_action) { val selectedText = pdfFragment.textSelection?.text.orEmpty() handle(selectedText) true // Handled. } else { false // Let the SDK run its default action. } } }})getPdfFragment().setOnPreparePopupToolbarListener(new OnPreparePopupToolbarListener() { @Override public void onPrepareTextSelectionPopupToolbar(@NonNull TextSelectionPopupToolbar toolbar) { final List<PopupToolbarMenuItem> items = new ArrayList<>(); for (PopupToolbarMenuItem item : toolbar.getMenuItems()) { if (item.getId() == R.id.pspdf__text_selection_toolbar_item_copy || item.getId() == R.id.pspdf__text_selection_toolbar_item_highlight) { items.add(item); } } items.add(new PopupToolbarMenuItem(R.id.custom_text_action, R.string.custom_text_action, R.drawable.ic_my_action)); toolbar.setMenuItems(items);
toolbar.setOnPopupToolbarItemClickedListener(item -> { if (item.getId() == R.id.custom_text_action) { handle(getPdfFragment().getTextSelection() != null ? getPdfFragment().getTextSelection().text : ""); return true; } return false; }); }});A PopupToolbarMenuItem carries its own icon, so a different icon for a default action is a replaced item, not a theme attribute:
toolbar.menuItems = toolbar.menuItems.map { item -> if (item.id == R.id.pspdf__text_selection_toolbar_item_copy) item.copy(iconRes = R.drawable.my_copy_icon) else item}The two-argument constructor, PopupToolbarMenuItem(id, title), creates a text-only item. Colors come from the pspdf__popupToolbarStyle theme attribute: pspdf__backgroundColor, pspdf__itemTint, and pspdf__itemTintDisabled.
The same listener has callbacks for the other popup toolbars: onPrepareAnnotationPopupToolbar for a selected annotation, onPrepareLongPressPopupToolbar for a long press on an empty page area, and onPrepareContentEditingPopupToolbar. PopupToolbarCustomItemsExample in the Catalog app customizes all four.
The popup keeps immersive mode active while it’s shown, matching the annotation selection popup.
Selecting text programmatically
You can programmatically select text using the PdfFragment#enterTextSelectionMode method and specifying the text range to be selected. You can retrieve the text of the current page by calling the PdfDocument#getPageText method.
For example, to select the first occurrence of a string on the page, use the following code:
// Search for the position of text that should be selected.val textToSelect = "text to select"val textIndexOnPage = document.getPageText(pageIndex).indexOf(textToSelect)if (textIndexOnPage >= 0) { // Select the text. fragment.enterTextSelectionMode(pageIndex, Range(textIndexOnPage, textToSelect.length))}// Search for the position of text that should be selected.String textToSelect = "text to select";int textIndexOnPage = document.getPageText(pageIndex).indexOf(textToSelect);if (textIndexOnPage >= 0) { // Select the text. fragment.enterTextSelectionMode(pageIndex, new Range(textIndexOnPage, textToSelect.length()));}Alternatively, you can select text by providing a list of rectangles, and the SDK selects the text beneath these rectangles. Create a TextSelectionRectangles object in one of the following ways:
- Provide a list of rectangles (no markup padding applied).
- Provide a list of rectangles with markup padding.
fragment.enterTextSelectionMode(pageIndex, TextSelectionRectangles(rectangles, markupRectangles))fragment.enterTextSelectionMode(pageIndex, new TextSelectionRectangles(rectangles, markupRectangles));Nutrient Android SDK throws an IllegalStateException in the following scenarios:
- Invalid parameters are provided.
- The text range exceeds the available text on the page.
- No text exists beneath the specified rectangles.