diff options
author | Diana Gage <drgage@google.com> | 2017-06-20 11:17:11 -0700 |
---|---|---|
committer | Chromium commit bot <commit-bot@chromium.org> | 2017-06-20 21:41:22 +0000 |
commit | dce2d72f9fbc166ee8eed0e362ab26e1e1a33cdd (patch) | |
tree | 410816fa09ffea9d2db198b5138f45ea55253824 | |
parent | b5c5ec06396158232d7f6955dde7b3512df0ae6e (diff) | |
download | pdfium-dce2d72f9fbc166ee8eed0e362ab26e1e1a33cdd.tar.xz |
Add FORM_GetSelectedText() function.
This function copies the selected text from a form text field or
form combobox text field into the buffer parameter and returns the
length of the selected text string. When buffer is a nullptr or
buflen is less than the length of the selected text, this function
does not modify the buffer and only returns the selected text length.
BUG=chromium:59266
Change-Id: Ie77de38e45bbe6f9ea033826c961435304eedfc7
Reviewed-on: https://pdfium-review.googlesource.com/6413
Reviewed-by: dsinclair <dsinclair@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
26 files changed, 397 insertions, 38 deletions
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.cpp b/fpdfsdk/cpdfsdk_annothandlermgr.cpp index 7ea301c0e4..2b089e5185 100644 --- a/fpdfsdk/cpdfsdk_annothandlermgr.cpp +++ b/fpdfsdk/cpdfsdk_annothandlermgr.cpp @@ -76,6 +76,11 @@ void CPDFSDK_AnnotHandlerMgr::Annot_OnLoad(CPDFSDK_Annot* pAnnot) { GetAnnotHandler(pAnnot)->OnLoad(pAnnot); } +CFX_WideString CPDFSDK_AnnotHandlerMgr::Annot_GetSelectedText( + CPDFSDK_Annot* pAnnot) { + return GetAnnotHandler(pAnnot)->GetSelectedText(pAnnot); +} + IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandler( CPDFSDK_Annot* pAnnot) const { return GetAnnotHandler(pAnnot->GetAnnotSubtype()); diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.h b/fpdfsdk/cpdfsdk_annothandlermgr.h index f36c3fb70e..5f6ff94b40 100644 --- a/fpdfsdk/cpdfsdk_annothandlermgr.h +++ b/fpdfsdk/cpdfsdk_annothandlermgr.h @@ -43,6 +43,8 @@ class CPDFSDK_AnnotHandlerMgr { void Annot_OnCreate(CPDFSDK_Annot* pAnnot); void Annot_OnLoad(CPDFSDK_Annot* pAnnot); + CFX_WideString Annot_GetSelectedText(CPDFSDK_Annot* pAnnot); + IPDFSDK_AnnotHandler* GetAnnotHandler(CPDFSDK_Annot* pAnnot) const; void Annot_OnDraw(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, diff --git a/fpdfsdk/cpdfsdk_baannothandler.cpp b/fpdfsdk/cpdfsdk_baannothandler.cpp index fa83932a81..4984ee93cc 100644 --- a/fpdfsdk/cpdfsdk_baannothandler.cpp +++ b/fpdfsdk/cpdfsdk_baannothandler.cpp @@ -193,6 +193,10 @@ CFX_FloatRect CPDFSDK_BAAnnotHandler::GetViewBBox(CPDFSDK_PageView* pPageView, return pAnnot->GetRect(); } +CFX_WideString CPDFSDK_BAAnnotHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) { + return CFX_WideString(); +} + bool CPDFSDK_BAAnnotHandler::HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) { diff --git a/fpdfsdk/cpdfsdk_baannothandler.h b/fpdfsdk/cpdfsdk_baannothandler.h index d5f170f452..2af2f3ee99 100644 --- a/fpdfsdk/cpdfsdk_baannothandler.h +++ b/fpdfsdk/cpdfsdk_baannothandler.h @@ -37,6 +37,7 @@ class CPDFSDK_BAAnnotHandler : public IPDFSDK_AnnotHandler { void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override; CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot) override; + CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override; bool HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) override; diff --git a/fpdfsdk/cpdfsdk_pageview.cpp b/fpdfsdk/cpdfsdk_pageview.cpp index 982eed1b21..aae245e298 100644 --- a/fpdfsdk/cpdfsdk_pageview.cpp +++ b/fpdfsdk/cpdfsdk_pageview.cpp @@ -241,6 +241,16 @@ CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByXFAWidget(CXFA_FFWidget* hWidget) { } #endif // PDF_ENABLE_XFA +CFX_WideString CPDFSDK_PageView::GetSelectedText() { + if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { + CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = + m_pFormFillEnv->GetAnnotHandlerMgr(); + return pAnnotHandlerMgr->Annot_GetSelectedText(pAnnot); + } + + return CFX_WideString(); +} + bool CPDFSDK_PageView::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { CPDFSDK_Annot::ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); if (!pAnnot) { diff --git a/fpdfsdk/cpdfsdk_pageview.h b/fpdfsdk/cpdfsdk_pageview.h index 8bede08a5d..9eebb6e424 100644 --- a/fpdfsdk/cpdfsdk_pageview.h +++ b/fpdfsdk/cpdfsdk_pageview.h @@ -60,6 +60,8 @@ class CPDFSDK_PageView final : public CPDF_Page::View { return m_pFormFillEnv.Get(); } + CFX_WideString GetSelectedText(); + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag); bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag); #ifdef PDF_ENABLE_XFA diff --git a/fpdfsdk/cpdfsdk_widgethandler.cpp b/fpdfsdk/cpdfsdk_widgethandler.cpp index c34f766793..68d95edb0e 100644 --- a/fpdfsdk/cpdfsdk_widgethandler.cpp +++ b/fpdfsdk/cpdfsdk_widgethandler.cpp @@ -280,6 +280,13 @@ CFX_FloatRect CPDFSDK_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView, return CFX_FloatRect(0, 0, 0, 0); } +CFX_WideString CPDFSDK_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) { + if (!pAnnot->IsSignatureWidget() && m_pFormFiller) + return m_pFormFiller->GetSelectedText(pAnnot); + + return CFX_WideString(); +} + bool CPDFSDK_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) { diff --git a/fpdfsdk/cpdfsdk_widgethandler.h b/fpdfsdk/cpdfsdk_widgethandler.h index 4e8c9ef480..9ef190f54d 100644 --- a/fpdfsdk/cpdfsdk_widgethandler.h +++ b/fpdfsdk/cpdfsdk_widgethandler.h @@ -38,6 +38,7 @@ class CPDFSDK_WidgetHandler : public IPDFSDK_AnnotHandler { void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override; CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot) override; + CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override; bool HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) override; diff --git a/fpdfsdk/cpdfsdk_xfawidgethandler.cpp b/fpdfsdk/cpdfsdk_xfawidgethandler.cpp index 5e258bc4e5..53a7740148 100644 --- a/fpdfsdk/cpdfsdk_xfawidgethandler.cpp +++ b/fpdfsdk/cpdfsdk_xfawidgethandler.cpp @@ -97,6 +97,11 @@ CFX_FloatRect CPDFSDK_XFAWidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView, return rcWidget; } +CFX_WideString CPDFSDK_XFAWidgetHandler::GetSelectedText( + CPDFSDK_Annot* pAnnot) { + return CFX_WideString(); +} + bool CPDFSDK_XFAWidgetHandler::HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) { diff --git a/fpdfsdk/cpdfsdk_xfawidgethandler.h b/fpdfsdk/cpdfsdk_xfawidgethandler.h index 2aea49b366..65e5e452d2 100644 --- a/fpdfsdk/cpdfsdk_xfawidgethandler.h +++ b/fpdfsdk/cpdfsdk_xfawidgethandler.h @@ -33,6 +33,7 @@ class CPDFSDK_XFAWidgetHandler : public IPDFSDK_AnnotHandler { void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override; CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot) override; + CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override; bool HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) override; diff --git a/fpdfsdk/formfiller/cffl_formfiller.cpp b/fpdfsdk/formfiller/cffl_formfiller.cpp index 297d5b78d6..526f4b947d 100644 --- a/fpdfsdk/formfiller/cffl_formfiller.cpp +++ b/fpdfsdk/formfiller/cffl_formfiller.cpp @@ -239,6 +239,17 @@ bool CFFL_FormFiller::OnChar(CPDFSDK_Annot* pAnnot, return false; } +CFX_WideString CFFL_FormFiller::GetSelectedText(CPDFSDK_Annot* pAnnot) { + if (!IsValid()) + return CFX_WideString(); + + CPDFSDK_PageView* pPageView = GetCurPageView(true); + ASSERT(pPageView); + + CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false); + return pWnd ? pWnd->GetSelectedText() : CFX_WideString(); +} + void CFFL_FormFiller::SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag) { CPDFSDK_Widget* pWidget = (CPDFSDK_Widget*)pAnnot; UnderlyingPageType* pPage = pWidget->GetUnderlyingPage(); diff --git a/fpdfsdk/formfiller/cffl_formfiller.h b/fpdfsdk/formfiller/cffl_formfiller.h index 445e86fb3c..2ddbdf2d6e 100644 --- a/fpdfsdk/formfiller/cffl_formfiller.h +++ b/fpdfsdk/formfiller/cffl_formfiller.h @@ -74,6 +74,8 @@ class CFFL_FormFiller : public IPWL_Provider, public CPWL_TimerHandler { uint32_t nFlags); virtual bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags); + CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot); + void SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag); void KillFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag); diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp index 7647fb19ff..69c74bc587 100644 --- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp +++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp @@ -522,6 +522,13 @@ void CFFL_InteractiveFormFiller::RemoveFormFiller(CPDFSDK_Annot* pAnnot) { UnRegisterFormFiller(pAnnot); } +CFX_WideString CFFL_InteractiveFormFiller::GetSelectedText( + CPDFSDK_Annot* pAnnot) { + ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); + CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false); + return pFormFiller ? pFormFiller->GetSelectedText(pAnnot) : CFX_WideString(); +} + void CFFL_InteractiveFormFiller::UnRegisterFormFiller(CPDFSDK_Annot* pAnnot) { auto it = m_Maps.find(pAnnot); if (it == m_Maps.end()) diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.h b/fpdfsdk/formfiller/cffl_interactiveformfiller.h index f72dfbcc61..b7724a32e3 100644 --- a/fpdfsdk/formfiller/cffl_interactiveformfiller.h +++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.h @@ -82,6 +82,8 @@ class CFFL_InteractiveFormFiller : public IPWL_Filler_Notify { CFFL_FormFiller* GetFormFiller(CPDFSDK_Annot* pAnnot, bool bRegister); void RemoveFormFiller(CPDFSDK_Annot* pAnnot); + CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot); + static bool IsVisible(CPDFSDK_Widget* pWidget); static bool IsReadOnly(CPDFSDK_Widget* pWidget); static bool IsFillingAllowed(CPDFSDK_Widget* pWidget); diff --git a/fpdfsdk/fpdfformfill.cpp b/fpdfsdk/fpdfformfill.cpp index 717e9722af..08d3d30847 100644 --- a/fpdfsdk/fpdfformfill.cpp +++ b/fpdfsdk/fpdfformfill.cpp @@ -369,6 +369,24 @@ DLLEXPORT FPDF_BOOL STDCALL FORM_OnChar(FPDF_FORMHANDLE hHandle, return pPageView->OnChar(nChar, modifier); } +DLLEXPORT unsigned long STDCALL FORM_GetSelectedText(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, + void* buffer, + unsigned long buflen) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); + if (!pPageView) + return 0; + + CFX_WideString wide_str_form_text = pPageView->GetSelectedText(); + CFX_ByteString encoded_form_text = wide_str_form_text.UTF16LE_Encode(); + unsigned long form_text_len = encoded_form_text.GetLength(); + + if (buffer && buflen >= form_text_len) + memcpy(buffer, encoded_form_text.c_str(), form_text_len); + + return form_text_len; +} + DLLEXPORT FPDF_BOOL STDCALL FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) { CPDFSDK_FormFillEnvironment* pFormFillEnv = HandleToCPDFSDKEnvironment(hHandle); diff --git a/fpdfsdk/fpdfformfill_embeddertest.cpp b/fpdfsdk/fpdfformfill_embeddertest.cpp index 8718a43d83..f3043983c2 100644 --- a/fpdfsdk/fpdfformfill_embeddertest.cpp +++ b/fpdfsdk/fpdfformfill_embeddertest.cpp @@ -4,10 +4,13 @@ #include <memory> #include <string> +#include <vector> +#include "core/fxcrt/fx_string.h" #include "core/fxcrt/fx_system.h" #include "public/cpp/fpdf_deleters.h" #include "public/fpdf_formfill.h" +#include "public/fpdf_fwlevent.h" #include "testing/embedder_test.h" #include "testing/embedder_test_mock_delegate.h" #include "testing/embedder_test_timer_handling_delegate.h" @@ -17,7 +20,78 @@ using testing::_; using testing::Return; -class FPDFFormFillEmbeddertest : public EmbedderTest, public TestSaver {}; +class FPDFFormFillEmbeddertest : public EmbedderTest, public TestSaver { + protected: + void TypeTextIntoTextfield(FPDF_PAGE page, int num_chars) { + // Click on the textfield in text_form.pdf. + EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, + FPDFPage_HasFormFieldAtPoint(form_handle(), page, 120.0, 120.0)); + FORM_OnMouseMove(form_handle(), page, 0, 120.0, 120.0); + FORM_OnLButtonDown(form_handle(), page, 0, 120.0, 120.0); + FORM_OnLButtonUp(form_handle(), page, 0, 120.0, 120.0); + + // Type text starting with 'A' to as many chars as specified by |num_chars|. + for (int i = 0; i < num_chars; ++i) { + FORM_OnChar(form_handle(), page, 'A' + i, 0); + } + } + + // Navigates to form text field using the mouse and then selects text via the + // shift and specfied left or right arrow key. + void SelectTextWithKeyboard(FPDF_PAGE page, + int num_chars, + int arrow_key, + double x, + double y) { + // Navigate to starting position for selection. + FORM_OnMouseMove(form_handle(), page, 0, x, y); + FORM_OnLButtonDown(form_handle(), page, 0, x, y); + FORM_OnLButtonUp(form_handle(), page, 0, x, y); + + // Hold down shift (and don't release until entire text is selected). + FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Shift, 0); + + // Select text char by char via left or right arrow key. + for (int i = 0; i < num_chars; ++i) { + FORM_OnKeyDown(form_handle(), page, arrow_key, FWL_EVENTFLAG_ShiftKey); + FORM_OnKeyUp(form_handle(), page, arrow_key, FWL_EVENTFLAG_ShiftKey); + } + FORM_OnKeyUp(form_handle(), page, FWL_VKEY_Shift, 0); + } + + // Uses the mouse to navigate to form text field and select text. + void SelectTextWithMouse(FPDF_PAGE page, + double start_x, + double end_x, + double y) { + // Navigate to starting position and click mouse. + FORM_OnMouseMove(form_handle(), page, 0, start_x, y); + FORM_OnLButtonDown(form_handle(), page, 0, start_x, y); + + // Hold down mouse until reach end of desired selection. + FORM_OnMouseMove(form_handle(), page, 0, end_x, y); + FORM_OnLButtonUp(form_handle(), page, 0, end_x, y); + } + + void CheckSelection(FPDF_PAGE page, const CFX_WideString& expected_string) { + // Calculate expected length for selected text. + int num_chars = expected_string.GetLength(); + + // Check actual selection against expected selection. + const unsigned long expected_length = + sizeof(unsigned short) * (num_chars + 1); + unsigned long sel_text_len = + FORM_GetSelectedText(form_handle(), page, nullptr, 0); + ASSERT_EQ(expected_length, sel_text_len); + + std::vector<unsigned short> buf(sel_text_len); + EXPECT_EQ(expected_length, FORM_GetSelectedText(form_handle(), page, + buf.data(), sel_text_len)); + + EXPECT_EQ(expected_string, + CFX_WideString::FromUTF16LE(buf.data(), num_chars)); + } +}; TEST_F(FPDFFormFillEmbeddertest, FirstTest) { EmbedderTestMockDelegate mock; @@ -282,3 +356,100 @@ TEST_F(FPDFFormFillEmbeddertest, FormText) { RenderPage(new_page.get())); CompareBitmap(new_bitmap.get(), 300, 300, md5_3); } + +TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextEmptyAndBasicKeyboard) { + // Open file with form text field. + EXPECT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Test empty selection. + CheckSelection(page, CFX_WideString(L"")); + + // Test basic selection. + TypeTextIntoTextfield(page, 3); + SelectTextWithKeyboard(page, 3, FWL_VKEY_Left, 123.0, 115.5); + CheckSelection(page, CFX_WideString(L"ABC")); + + UnloadPage(page); +} + +TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextEmptyAndBasicMouse) { + // Open file with form text field. + EXPECT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Test empty selection. + CheckSelection(page, CFX_WideString(L"")); + + // Test basic selection. + TypeTextIntoTextfield(page, 3); + SelectTextWithMouse(page, 125.0, 102.0, 115.5); + CheckSelection(page, CFX_WideString(L"ABC")); + + UnloadPage(page); +} + +TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextFragmentsKeyBoard) { + // Open file with form text field. + EXPECT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + TypeTextIntoTextfield(page, 12); + + // Test selecting first character in forward direction. + // Navigate to starting position and click mouse. + SelectTextWithKeyboard(page, 1, FWL_VKEY_Right, 102.0, 115.5); + CheckSelection(page, CFX_WideString(L"A")); + + // Test selecting entire long string in backwards direction. + SelectTextWithKeyboard(page, 12, FWL_VKEY_Left, 191.0, 115.5); + CheckSelection(page, CFX_WideString(L"ABCDEFGHIJKL")); + + // Test selecting middle section in backwards direction. + SelectTextWithKeyboard(page, 6, FWL_VKEY_Left, 170.0, 115.5); + CheckSelection(page, CFX_WideString(L"DEFGHI")); + + // Test selecting middle selection in forward direction. + SelectTextWithKeyboard(page, 6, FWL_VKEY_Right, 125.0, 115.5); + CheckSelection(page, CFX_WideString(L"DEFGHI")); + + // Test selecting last character in backwards direction. + SelectTextWithKeyboard(page, 1, FWL_VKEY_Left, 191.0, 115.5); + CheckSelection(page, CFX_WideString(L"L")); + + UnloadPage(page); +} + +TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextFragmentsMouse) { + // Open file with form text field. + EXPECT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + TypeTextIntoTextfield(page, 12); + + // Test selecting first character in forward direction. + SelectTextWithMouse(page, 102.0, 106.0, 115.5); + CheckSelection(page, CFX_WideString(L"A")); + + // Test selecting entire long string in backwards direction. + SelectTextWithMouse(page, 191.0, 102.0, 115.5); + CheckSelection(page, CFX_WideString(L"ABCDEFGHIJKL")); + + // Test selecting middle section in backwards direction. + SelectTextWithMouse(page, 170.0, 125.0, 115.5); + CheckSelection(page, CFX_WideString(L"DEFGHI")); + + // Test selecting middle selection in forward direction. + SelectTextWithMouse(page, 125.0, 170.0, 115.5); + CheckSelection(page, CFX_WideString(L"DEFGHI")); + + // Test selecting last character in backwards direction. + SelectTextWithMouse(page, 191.0, 186.0, 115.5); + CheckSelection(page, CFX_WideString(L"L")); + + UnloadPage(page); +} diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c index 0deaf9db1e..05242c6fe1 100644 --- a/fpdfsdk/fpdfview_c_api_test.c +++ b/fpdfsdk/fpdfview_c_api_test.c @@ -156,6 +156,7 @@ int CheckPDFiumCApi() { CHK(FORM_OnKeyDown); CHK(FORM_OnKeyUp); CHK(FORM_OnChar); + CHK(FORM_GetSelectedText); CHK(FORM_ForceToKillFocus); CHK(FPDFPage_HasFormFieldAtPoint); CHK(FPDFPage_FormFieldZOrderAtPoint); diff --git a/fpdfsdk/ipdfsdk_annothandler.h b/fpdfsdk/ipdfsdk_annothandler.h index 636d161fea..08008eb996 100644 --- a/fpdfsdk/ipdfsdk_annothandler.h +++ b/fpdfsdk/ipdfsdk_annothandler.h @@ -36,6 +36,7 @@ class IPDFSDK_AnnotHandler { virtual void ReleaseAnnot(CPDFSDK_Annot* pAnnot) = 0; virtual CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot) = 0; + virtual CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) = 0; virtual bool HitTest(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot, const CFX_PointF& point) = 0; diff --git a/fpdfsdk/pdfwindow/cpwl_combo_box.cpp b/fpdfsdk/pdfwindow/cpwl_combo_box.cpp index b69f98906e..2929aac90f 100644 --- a/fpdfsdk/pdfwindow/cpwl_combo_box.cpp +++ b/fpdfsdk/pdfwindow/cpwl_combo_box.cpp @@ -216,6 +216,13 @@ void CPWL_ComboBox::KillFocus() { CPWL_Wnd::KillFocus(); } +CFX_WideString CPWL_ComboBox::GetSelectedText() { + if (m_pEdit) + return m_pEdit->GetSelectedText(); + + return CFX_WideString(); +} + CFX_WideString CPWL_ComboBox::GetText() const { if (m_pEdit) { return m_pEdit->GetText(); diff --git a/fpdfsdk/pdfwindow/cpwl_combo_box.h b/fpdfsdk/pdfwindow/cpwl_combo_box.h index e814ca5d40..549374f3e8 100644 --- a/fpdfsdk/pdfwindow/cpwl_combo_box.h +++ b/fpdfsdk/pdfwindow/cpwl_combo_box.h @@ -69,6 +69,7 @@ class CPWL_ComboBox : public CPWL_Wnd { CFX_FloatRect GetFocusRect() const override; void SetFocus() override; void KillFocus() override; + CFX_WideString GetSelectedText() override; void SetFillerNotify(IPWL_Filler_Notify* pNotify); diff --git a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp index 9c22cead58..3390696e01 100644 --- a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp +++ b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp @@ -53,6 +53,13 @@ void CPWL_EditCtrl::SetCursor() { } } +CFX_WideString CPWL_EditCtrl::GetSelectedText() { + if (m_pEdit) + return m_pEdit->GetSelText(); + + return CFX_WideString(); +} + void CPWL_EditCtrl::RePosChildWnd() { m_pEdit->SetPlateRect(GetClientRect()); } diff --git a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h index 59bb623776..1c4c92507a 100644 --- a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h +++ b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h @@ -61,6 +61,7 @@ class CPWL_EditCtrl : public CPWL_Wnd { void SetFontSize(float fFontSize) override; float GetFontSize() const override; void SetCursor() override; + CFX_WideString GetSelectedText() override; void IOnSetScrollInfoY(float fPlateMin, float fPlateMax, diff --git a/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp b/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp index 4a14b65d0d..e206cd16e3 100644 --- a/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp +++ b/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp @@ -11,51 +11,115 @@ #include "testing/embedder_test.h" #include "testing/gtest/include/gtest/gtest.h" -class CPWLEditEmbeddertest : public EmbedderTest {}; +class CPWLEditEmbeddertest : public EmbedderTest { + protected: + void SetUp() override { + EmbedderTest::SetUp(); + CreateAndInitializeFormPDF(); + } -TEST_F(CPWLEditEmbeddertest, TypeText) { - EXPECT_TRUE(OpenDocument("text_form.pdf")); - FPDF_PAGE page = LoadPage(0); - ASSERT_TRUE(page); - - CPDFSDK_FormFillEnvironment* pFormFillEnv = - static_cast<CPDFSDK_FormFillEnvironment*>(form_handle()); - - CPDFSDK_Annot* pAnnot; - { - CBA_AnnotIterator iter(pFormFillEnv->GetPageView(0), - CPDF_Annot::Subtype::WIDGET); - pAnnot = iter.GetFirstAnnot(); - CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot(); - ASSERT_EQ(pAnnot, pLastAnnot); - ASSERT_TRUE(pAnnot); - ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, pAnnot->GetAnnotSubtype()); + void TearDown() override { + UnloadPage(GetPage()); + EmbedderTest::TearDown(); } - CFFL_InteractiveFormFiller* pInteractiveFormFiller = - pFormFillEnv->GetInteractiveFormFiller(); - { - CPDFSDK_Annot::ObservedPtr pObserved(pAnnot); - EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0)); + void CreateAndInitializeFormPDF() { + EXPECT_TRUE(OpenDocument("text_form.pdf")); + m_page = LoadPage(0); + ASSERT_TRUE(m_page); + + CPDFSDK_FormFillEnvironment* pFormFillEnv = + static_cast<CPDFSDK_FormFillEnvironment*>(form_handle()); + + { + CBA_AnnotIterator iter(pFormFillEnv->GetPageView(0), + CPDF_Annot::Subtype::WIDGET); + m_pAnnot = iter.GetFirstAnnot(); + CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot(); + ASSERT_EQ(m_pAnnot, pLastAnnot); + ASSERT_TRUE(m_pAnnot); + ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnot->GetAnnotSubtype()); + } + + CFFL_InteractiveFormFiller* pInteractiveFormFiller = + pFormFillEnv->GetInteractiveFormFiller(); + { + CPDFSDK_Annot::ObservedPtr pObserved(m_pAnnot); + EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0)); + } + + m_pFormFiller = pInteractiveFormFiller->GetFormFiller(m_pAnnot, false); + ASSERT_TRUE(m_pFormFiller); + + CPWL_Wnd* pWindow = + m_pFormFiller->GetPDFWindow(pFormFillEnv->GetPageView(0), false); + ASSERT_TRUE(pWindow); + ASSERT_EQ(PWL_CLASSNAME_EDIT, pWindow->GetClassName()); + + m_pEdit = static_cast<CPWL_Edit*>(pWindow); } - CFFL_FormFiller* pFormFiller = - pInteractiveFormFiller->GetFormFiller(pAnnot, false); - ASSERT_TRUE(pFormFiller); + FPDF_PAGE GetPage() { return m_page; } + CPWL_Edit* GetCPWLEdit() { return m_pEdit; } + CFFL_FormFiller* GetCFFLFormFiller() { return m_pFormFiller; } + CPDFSDK_Annot* GetCPDFSDKAnnot() { return m_pAnnot; } + + private: + FPDF_PAGE m_page; + CPWL_Edit* m_pEdit; + CFFL_FormFiller* m_pFormFiller; + CPDFSDK_Annot* m_pAnnot; +}; + +TEST_F(CPWLEditEmbeddertest, TypeText) { + EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty()); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0)); + + EXPECT_STREQ(L"abc", GetCPWLEdit()->GetText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, GetSelectedTextEmptyAndBasic) { + // Attempt to set selection before text has been typed to test that + // selection is identified as empty. + // + // Select from character index [0, 3) within form text field. + GetCPWLEdit()->SetSel(0, 3); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); + + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0)); + GetCPWLEdit()->SetSel(0, 2); + + EXPECT_STREQ(L"ab", GetCPWLEdit()->GetSelectedText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, GetSelectedTextFragments) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->SetSel(0, 0); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); + + GetCPWLEdit()->SetSel(0, 1); + EXPECT_STREQ(L"A", GetCPWLEdit()->GetSelectedText().c_str()); - CPWL_Wnd* pWindow = - pFormFiller->GetPDFWindow(pFormFillEnv->GetPageView(0), false); - ASSERT_TRUE(pWindow); - ASSERT_EQ(PWL_CLASSNAME_EDIT, pWindow->GetClassName()); + GetCPWLEdit()->SetSel(0, -1); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLEdit()->GetSelectedText().c_str()); - CPWL_Edit* pEdit = static_cast<CPWL_Edit*>(pWindow); - EXPECT_TRUE(pEdit->GetText().IsEmpty()); + GetCPWLEdit()->SetSel(-8, -1); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); - EXPECT_TRUE(pFormFiller->OnChar(pAnnot, 'a', 0)); - EXPECT_TRUE(pFormFiller->OnChar(pAnnot, 'b', 0)); - EXPECT_TRUE(pFormFiller->OnChar(pAnnot, 'c', 0)); + GetCPWLEdit()->SetSel(23, 12); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str()); - EXPECT_STREQ(L"abc", pEdit->GetText().c_str()); + GetCPWLEdit()->SetSel(12, 23); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str()); - UnloadPage(page); + GetCPWLEdit()->SetSel(49, 50); + EXPECT_STREQ(L"r", GetCPWLEdit()->GetSelectedText().c_str()); } diff --git a/fpdfsdk/pdfwindow/cpwl_wnd.cpp b/fpdfsdk/pdfwindow/cpwl_wnd.cpp index 90a79305eb..d44b219110 100644 --- a/fpdfsdk/pdfwindow/cpwl_wnd.cpp +++ b/fpdfsdk/pdfwindow/cpwl_wnd.cpp @@ -390,6 +390,10 @@ PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonUp) PWL_IMPLEMENT_MOUSE_METHOD(OnMouseMove) #undef PWL_IMPLEMENT_MOUSE_METHOD +CFX_WideString CPWL_Wnd::GetSelectedText() { + return CFX_WideString(); +} + bool CPWL_Wnd::OnMouseWheel(short zDelta, const CFX_PointF& point, uint32_t nFlag) { diff --git a/fpdfsdk/pdfwindow/cpwl_wnd.h b/fpdfsdk/pdfwindow/cpwl_wnd.h index e7a4e23c38..704984932b 100644 --- a/fpdfsdk/pdfwindow/cpwl_wnd.h +++ b/fpdfsdk/pdfwindow/cpwl_wnd.h @@ -229,6 +229,7 @@ class CPWL_Wnd : public CPWL_TimerHandler { virtual void SetFontSize(float fFontSize); virtual float GetFontSize() const; + virtual CFX_WideString GetSelectedText(); virtual CFX_FloatRect GetFocusRect() const; virtual CFX_FloatRect GetClientRect() const; diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h index caefad78c7..5a15cd9a02 100644 --- a/public/fpdf_formfill.h +++ b/public/fpdf_formfill.h @@ -1355,6 +1355,29 @@ DLLEXPORT FPDF_BOOL STDCALL FORM_OnChar(FPDF_FORMHANDLE hHandle, int modifier); /** + * Function: FORM_GetSelectedText + * You can call this function to obtain selected text within + * a form text field or form combobox text field. + * Parameters: + * hHandle - Handle to the form fill module. Returned by + * FPDFDOC_InitFormFillEnvironment. + * page - Handle to the page. Returned by FPDF_LoadPage + * function. + * buffer - Buffer for holding the selected text, encoded + * in UTF16-LE. If NULL, |buffer| is not modified. + * buflen - Length of |buffer| in bytes. If |buflen| + is less than the length of the selected text + string, |buffer| is not modified. + * Return Value: + * Length in bytes of selected text in form text field or form combobox + * text field. + **/ +DLLEXPORT unsigned long STDCALL FORM_GetSelectedText(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, + void* buffer, + unsigned long buflen); + +/** * Function: FORM_ForceToKillFocus. * You can call this member function to force to kill the focus of the *form field which got focus. |