Add FORM_ReplaceAndKeepSelection for IME composition
This API is used to simulate IME composition with selection. This will
be sufficient for most CJK IMEs.
Bug: chromium:1253665
Change-Id: Ifa5f878a0d299b9c697b0c616f682b1c031998a4
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/103256
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/cpdfsdk_annot.h b/fpdfsdk/cpdfsdk_annot.h
index 3b1d022..1357594 100644
--- a/fpdfsdk/cpdfsdk_annot.h
+++ b/fpdfsdk/cpdfsdk_annot.h
@@ -75,6 +75,7 @@
virtual bool Redo() = 0;
virtual WideString GetText() = 0;
virtual WideString GetSelectedText() = 0;
+ virtual void ReplaceAndKeepSelection(const WideString& text) = 0;
virtual void ReplaceSelection(const WideString& text) = 0;
virtual bool SelectAllText() = 0;
virtual bool SetIndexSelected(int index, bool selected) = 0;
diff --git a/fpdfsdk/cpdfsdk_baannot.cpp b/fpdfsdk/cpdfsdk_baannot.cpp
index 04ad812..a1751f9 100644
--- a/fpdfsdk/cpdfsdk_baannot.cpp
+++ b/fpdfsdk/cpdfsdk_baannot.cpp
@@ -403,6 +403,8 @@
return WideString();
}
+void CPDFSDK_BAAnnot::ReplaceAndKeepSelection(const WideString& text) {}
+
void CPDFSDK_BAAnnot::ReplaceSelection(const WideString& text) {}
bool CPDFSDK_BAAnnot::SelectAllText() {
diff --git a/fpdfsdk/cpdfsdk_baannot.h b/fpdfsdk/cpdfsdk_baannot.h
index 2be9e14..0da13d8 100644
--- a/fpdfsdk/cpdfsdk_baannot.h
+++ b/fpdfsdk/cpdfsdk_baannot.h
@@ -44,6 +44,7 @@
bool Redo() override;
WideString GetText() override;
WideString GetSelectedText() override;
+ void ReplaceAndKeepSelection(const WideString& text) override;
void ReplaceSelection(const WideString& text) override;
bool SelectAllText() override;
bool SetIndexSelected(int index, bool selected) override;
diff --git a/fpdfsdk/cpdfsdk_pageview.cpp b/fpdfsdk/cpdfsdk_pageview.cpp
index 83d7783..4c82e7f 100644
--- a/fpdfsdk/cpdfsdk_pageview.cpp
+++ b/fpdfsdk/cpdfsdk_pageview.cpp
@@ -286,6 +286,12 @@
return annot->GetSelectedText();
}
+void CPDFSDK_PageView::ReplaceAndKeepSelection(const WideString& text) {
+ CPDFSDK_Annot* annot = GetFocusAnnot();
+ if (annot)
+ annot->ReplaceAndKeepSelection(text);
+}
+
void CPDFSDK_PageView::ReplaceSelection(const WideString& text) {
CPDFSDK_Annot* annot = GetFocusAnnot();
if (annot)
diff --git a/fpdfsdk/cpdfsdk_pageview.h b/fpdfsdk/cpdfsdk_pageview.h
index 675600f..b95f844 100644
--- a/fpdfsdk/cpdfsdk_pageview.h
+++ b/fpdfsdk/cpdfsdk_pageview.h
@@ -66,6 +66,7 @@
WideString GetFocusedFormText();
WideString GetSelectedText();
+ void ReplaceAndKeepSelection(const WideString& text);
void ReplaceSelection(const WideString& text);
bool SelectAllText();
diff --git a/fpdfsdk/cpdfsdk_widget.cpp b/fpdfsdk/cpdfsdk_widget.cpp
index 22b8be6..4cf176f 100644
--- a/fpdfsdk/cpdfsdk_widget.cpp
+++ b/fpdfsdk/cpdfsdk_widget.cpp
@@ -877,6 +877,13 @@
return GetInteractiveFormFiller()->GetSelectedText(this);
}
+void CPDFSDK_Widget::ReplaceAndKeepSelection(const WideString& text) {
+ if (IsSignatureWidget())
+ return;
+
+ GetInteractiveFormFiller()->ReplaceAndKeepSelection(this, text);
+}
+
void CPDFSDK_Widget::ReplaceSelection(const WideString& text) {
if (IsSignatureWidget())
return;
diff --git a/fpdfsdk/cpdfsdk_widget.h b/fpdfsdk/cpdfsdk_widget.h
index f63e8a4..ce68cfb 100644
--- a/fpdfsdk/cpdfsdk_widget.h
+++ b/fpdfsdk/cpdfsdk_widget.h
@@ -65,6 +65,7 @@
bool Redo() override;
WideString GetText() override;
WideString GetSelectedText() override;
+ void ReplaceAndKeepSelection(const WideString& text) override;
void ReplaceSelection(const WideString& text) override;
bool SelectAllText() override;
bool SetIndexSelected(int index, bool selected) override;
diff --git a/fpdfsdk/formfiller/cffl_formfield.cpp b/fpdfsdk/formfiller/cffl_formfield.cpp
index 063a919..88fb16a 100644
--- a/fpdfsdk/formfiller/cffl_formfield.cpp
+++ b/fpdfsdk/formfiller/cffl_formfield.cpp
@@ -203,6 +203,17 @@
return pWnd ? pWnd->GetSelectedText() : WideString();
}
+void CFFL_FormField::ReplaceAndKeepSelection(const WideString& text) {
+ if (!IsValid())
+ return;
+
+ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView());
+ if (!pWnd)
+ return;
+
+ pWnd->ReplaceAndKeepSelection(text);
+}
+
void CFFL_FormField::ReplaceSelection(const WideString& text) {
if (!IsValid())
return;
diff --git a/fpdfsdk/formfiller/cffl_formfield.h b/fpdfsdk/formfiller/cffl_formfield.h
index bceb9b0..1ddf730 100644
--- a/fpdfsdk/formfiller/cffl_formfield.h
+++ b/fpdfsdk/formfiller/cffl_formfield.h
@@ -78,6 +78,7 @@
WideString GetText();
WideString GetSelectedText();
+ void ReplaceAndKeepSelection(const WideString& text);
void ReplaceSelection(const WideString& text);
bool SelectAllText();
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
index f4b0b94..5137429 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
@@ -531,6 +531,16 @@
return pFormField ? pFormField->GetSelectedText() : WideString();
}
+void CFFL_InteractiveFormFiller::ReplaceAndKeepSelection(
+ CPDFSDK_Widget* pWidget,
+ const WideString& text) {
+ CFFL_FormField* pFormField = GetFormField(pWidget);
+ if (!pFormField)
+ return;
+
+ pFormField->ReplaceAndKeepSelection(text);
+}
+
void CFFL_InteractiveFormFiller::ReplaceSelection(CPDFSDK_Widget* pWidget,
const WideString& text) {
CFFL_FormField* pFormField = GetFormField(pWidget);
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.h b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
index cbfa695..bbf757d 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.h
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
@@ -126,6 +126,7 @@
WideString GetText(CPDFSDK_Widget* pWidget);
WideString GetSelectedText(CPDFSDK_Widget* pWidget);
+ void ReplaceAndKeepSelection(CPDFSDK_Widget* pWidget, const WideString& text);
void ReplaceSelection(CPDFSDK_Widget* pWidget, const WideString& text);
bool SelectAllText(CPDFSDK_Widget* pWidget);
diff --git a/fpdfsdk/fpdf_formfill.cpp b/fpdfsdk/fpdf_formfill.cpp
index 56c1326..8c1a083 100644
--- a/fpdfsdk/fpdf_formfill.cpp
+++ b/fpdfsdk/fpdf_formfill.cpp
@@ -531,6 +531,17 @@
buffer, buflen);
}
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle,
+ FPDF_PAGE page,
+ FPDF_WIDESTRING wsText) {
+ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+ if (!pPageView)
+ return;
+
+ pPageView->ReplaceAndKeepSelection(WideStringFromFPDFWideString(wsText));
+}
+
FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle,
FPDF_PAGE page,
FPDF_WIDESTRING wsText) {
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index 6b63f4e..ea4af57 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -3197,6 +3197,39 @@
}
}
+TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceAndKeepSelection) {
+ ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ");
+ ClickOnFormFieldAtPoint(RegularFormBegin());
+ CheckCanUndo(false);
+ CheckCanRedo(false);
+
+ TypeTextIntoTextField(2, RegularFormBegin());
+ CheckFocusedFieldText(L"AB");
+ CheckSelection(L"");
+ SelectTextWithKeyboard(1, FWL_VKEY_Right, RegularFormBegin());
+ CheckSelection(L"A");
+
+ FORM_ReplaceAndKeepSelection(form_handle(), page(), text_to_insert.get());
+ CheckFocusedFieldText(L"XYZB");
+ CheckSelection(L"XYZ");
+ CheckCanUndo(true);
+ CheckCanRedo(false);
+
+ PerformUndo();
+ CheckFocusedFieldText(L"AB");
+ CheckCanUndo(true);
+ CheckCanRedo(true);
+
+ SelectTextWithKeyboard(1, FWL_VKEY_Left, RegularFormEnd());
+ CheckSelection(L"B");
+
+ FORM_ReplaceAndKeepSelection(form_handle(), page(), text_to_insert.get());
+ CheckFocusedFieldText(L"AXYZ");
+ CheckSelection(L"XYZ");
+ CheckCanUndo(true);
+ CheckCanRedo(false);
+}
+
TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceSelection) {
ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ");
ClickOnFormFieldAtPoint(RegularFormBegin());
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index e85db77..e9231d7 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -299,6 +299,7 @@
CHK(FORM_OnRButtonDown);
CHK(FORM_OnRButtonUp);
CHK(FORM_Redo);
+ CHK(FORM_ReplaceAndKeepSelection);
CHK(FORM_ReplaceSelection);
CHK(FORM_SelectAllText);
CHK(FORM_SetFocusedAnnot);
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
index b0ef53b..1367c1a 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
@@ -411,6 +411,12 @@
return widget_handler->GetSelectedText(GetXFAFFWidget());
}
+void CPDFXFA_Widget::ReplaceAndKeepSelection(const WideString& text) {
+ // XFA does not seem to support IME input at all. Therefore we don't bother
+ // to keep selection for IMEs.
+ ReplaceSelection(text);
+}
+
void CPDFXFA_Widget::ReplaceSelection(const WideString& text) {
CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler();
if (widget_handler)
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
index 38d6537..1a74213 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
@@ -38,6 +38,7 @@
bool Redo() override;
WideString GetText() override;
WideString GetSelectedText() override;
+ void ReplaceAndKeepSelection(const WideString& text) override;
void ReplaceSelection(const WideString& text) override;
bool SelectAllText() override;
bool SetIndexSelected(int index, bool selected) override;
diff --git a/fpdfsdk/pwl/cpwl_combo_box.cpp b/fpdfsdk/pwl/cpwl_combo_box.cpp
index dde7652..50c19f4 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box.cpp
@@ -62,6 +62,11 @@
return WideString();
}
+void CPWL_ComboBox::ReplaceAndKeepSelection(const WideString& text) {
+ if (m_pEdit)
+ m_pEdit->ReplaceAndKeepSelection(text);
+}
+
void CPWL_ComboBox::ReplaceSelection(const WideString& text) {
if (m_pEdit)
m_pEdit->ReplaceSelection(text);
diff --git a/fpdfsdk/pwl/cpwl_combo_box.h b/fpdfsdk/pwl/cpwl_combo_box.h
index 6d74a97..bb8fa0c 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.h
+++ b/fpdfsdk/pwl/cpwl_combo_box.h
@@ -40,6 +40,7 @@
void KillFocus() override;
WideString GetText() override;
WideString GetSelectedText() override;
+ void ReplaceAndKeepSelection(const WideString& text) override;
void ReplaceSelection(const WideString& text) override;
bool SelectAllText() override;
bool CanUndo() override;
diff --git a/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp
index 529d9ad..3a9d8ef 100644
--- a/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp
@@ -265,3 +265,19 @@
GetCPWLComboBox()->ReplaceSelection(L"Hello");
EXPECT_STREQ(L"ABCDEHello", GetCPWLComboBox()->GetText().c_str());
}
+
+TEST_F(CPWLComboBoxEditEmbedderTest, ReplaceAndKeepSelection) {
+ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
+ TypeTextIntoTextField(10);
+
+ GetCPWLComboBox()->SetEditSelection(1, 3);
+ EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+ GetCPWLComboBox()->ReplaceAndKeepSelection(L"xyz");
+ EXPECT_STREQ(L"AxyzDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+ EXPECT_STREQ(L"xyz", GetCPWLComboBox()->GetSelectedText().c_str());
+
+ GetCPWLComboBox()->SetEditSelection(4, 1);
+ GetCPWLComboBox()->ReplaceAndKeepSelection(L"12");
+ EXPECT_STREQ(L"A12DEFGHIJ", GetCPWLComboBox()->GetText().c_str());
+ EXPECT_STREQ(L"12", GetCPWLComboBox()->GetSelectedText().c_str());
+}
diff --git a/fpdfsdk/pwl/cpwl_edit.cpp b/fpdfsdk/pwl/cpwl_edit.cpp
index 9fce555..f0b4d35 100644
--- a/fpdfsdk/pwl/cpwl_edit.cpp
+++ b/fpdfsdk/pwl/cpwl_edit.cpp
@@ -487,6 +487,10 @@
return m_pEditImpl->GetSelectedText();
}
+void CPWL_Edit::ReplaceAndKeepSelection(const WideString& text) {
+ m_pEditImpl->ReplaceAndKeepSelection(text);
+}
+
void CPWL_Edit::ReplaceSelection(const WideString& text) {
m_pEditImpl->ReplaceSelection(text);
}
diff --git a/fpdfsdk/pwl/cpwl_edit.h b/fpdfsdk/pwl/cpwl_edit.h
index c879148..696e32c 100644
--- a/fpdfsdk/pwl/cpwl_edit.h
+++ b/fpdfsdk/pwl/cpwl_edit.h
@@ -63,6 +63,7 @@
void SetCursor() override;
WideString GetText() override;
WideString GetSelectedText() override;
+ void ReplaceAndKeepSelection(const WideString& text) override;
void ReplaceSelection(const WideString& text) override;
bool SelectAllText() override;
bool CanUndo() override;
diff --git a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
index a076924..f0e13d2 100644
--- a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
@@ -4,6 +4,8 @@
#include "fpdfsdk/pwl/cpwl_edit.h"
+#include <utility>
+
#include "fpdfsdk/cpdfsdk_annotiterator.h"
#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
@@ -420,3 +422,21 @@
GetCPWLEdit()->SetText(L"Foo\n\rBar");
EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str());
}
+
+TEST_F(CPWLEditEmbedderTest, ReplaceAndKeepSelection) {
+ FormFillerAndWindowSetup(GetCPDFSDKAnnot());
+ TypeTextIntoTextField(10);
+
+ GetCPWLEdit()->SetSelection(1, 3);
+ EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLEdit()->GetText().c_str());
+ GetCPWLEdit()->ReplaceAndKeepSelection(L"xyz");
+ EXPECT_STREQ(L"AxyzDEFGHIJ", GetCPWLEdit()->GetText().c_str());
+ EXPECT_STREQ(L"xyz", GetCPWLEdit()->GetSelectedText().c_str());
+ EXPECT_EQ(GetCPWLEdit()->GetSelection(), std::make_pair(1, 4));
+
+ GetCPWLEdit()->SetSelection(4, 1);
+ GetCPWLEdit()->ReplaceAndKeepSelection(L"12");
+ EXPECT_STREQ(L"A12DEFGHIJ", GetCPWLEdit()->GetText().c_str());
+ EXPECT_STREQ(L"12", GetCPWLEdit()->GetSelectedText().c_str());
+ EXPECT_EQ(GetCPWLEdit()->GetSelection(), std::make_pair(1, 3));
+}
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp
index 2cc10cb..50e2c42 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp
@@ -1761,6 +1761,19 @@
}
}
+void CPWL_EditImpl::ReplaceAndKeepSelection(const WideString& text) {
+ AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, false));
+ ClearSelection();
+
+ // Select the inserted text.
+ CPVT_WordPlace caret_before_insert = m_wpCaret;
+ InsertText(text, FX_Charset::kDefault);
+ CPVT_WordPlace caret_after_insert = m_wpCaret;
+ m_SelState.Set(caret_before_insert, caret_after_insert);
+
+ AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, true));
+}
+
void CPWL_EditImpl::ReplaceSelection(const WideString& text) {
AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, false));
ClearSelection();
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.h b/fpdfsdk/pwl/cpwl_edit_impl.h
index b4735c6..0b3bc76 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.h
+++ b/fpdfsdk/pwl/cpwl_edit_impl.h
@@ -99,6 +99,7 @@
bool Delete();
bool ClearSelection();
bool InsertText(const WideString& sText, FX_Charset charset);
+ void ReplaceAndKeepSelection(const WideString& text);
void ReplaceSelection(const WideString& text);
bool Redo();
bool Undo();
diff --git a/fpdfsdk/pwl/cpwl_wnd.cpp b/fpdfsdk/pwl/cpwl_wnd.cpp
index 9c585ed..755fcbb 100644
--- a/fpdfsdk/pwl/cpwl_wnd.cpp
+++ b/fpdfsdk/pwl/cpwl_wnd.cpp
@@ -345,6 +345,8 @@
return WideString();
}
+void CPWL_Wnd::ReplaceAndKeepSelection(const WideString& text) {}
+
void CPWL_Wnd::ReplaceSelection(const WideString& text) {}
bool CPWL_Wnd::SelectAllText() {
diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h
index 262116a..b10b59a 100644
--- a/fpdfsdk/pwl/cpwl_wnd.h
+++ b/fpdfsdk/pwl/cpwl_wnd.h
@@ -169,6 +169,7 @@
virtual WideString GetText();
virtual WideString GetSelectedText();
+ virtual void ReplaceAndKeepSelection(const WideString& text);
virtual void ReplaceSelection(const WideString& text);
virtual bool SelectAllText();
diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h
index dabb3d3..2bcf7f7 100644
--- a/public/fpdf_formfill.h
+++ b/public/fpdf_formfill.h
@@ -1560,12 +1560,35 @@
unsigned long buflen);
/*
+ * Experimental API
+ * Function: FORM_ReplaceAndKeepSelection
+ * Call this function to replace the selected text in a form
+ * text field or user-editable form combobox text field with another
+ * text string (which can be empty or non-empty). If there is no
+ * selected text, this function will append the replacement text after
+ * the current caret position. After the insertion, the inserted text
+ * will be selected.
+ * Parameters:
+ * hHandle - Handle to the form fill module, as returned by
+ * FPDFDOC_InitFormFillEnvironment().
+ * page - Handle to the page, as Returned by FPDF_LoadPage().
+ * wsText - The text to be inserted, in UTF-16LE format.
+ * Return Value:
+ * None.
+ */
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle,
+ FPDF_PAGE page,
+ FPDF_WIDESTRING wsText);
+
+/*
* Function: FORM_ReplaceSelection
* Call this function to replace the selected text in a form
* text field or user-editable form combobox text field with another
* text string (which can be empty or non-empty). If there is no
* selected text, this function will append the replacement text after
- * the current caret position.
+ * the current caret position. After the insertion, the selection range
+ * will be set to empty.
* Parameters:
* hHandle - Handle to the form fill module, as returned by
* FPDFDOC_InitFormFillEnvironment().