diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/pdf/pdfium | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/pdf/pdfium')
-rw-r--r-- | chromium/pdf/pdfium/accessibility_unittest.cc | 9 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc | 35 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h | 80 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_assert_matching_enums.cc | 15 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_engine.cc | 234 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_engine.h | 28 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_engine_unittest.cc | 156 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_form_filler.cc | 15 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_page.cc | 180 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_page.h | 157 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_page_unittest.cc | 166 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_permissions.cc | 2 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_print.cc | 2 | ||||
-rw-r--r-- | chromium/pdf/pdfium/pdfium_test_base.cc | 3 |
14 files changed, 831 insertions, 251 deletions
diff --git a/chromium/pdf/pdfium/accessibility_unittest.cc b/chromium/pdf/pdfium/accessibility_unittest.cc index f23e103ffde..98eb154f5ae 100644 --- a/chromium/pdf/pdfium/accessibility_unittest.cc +++ b/chromium/pdf/pdfium/accessibility_unittest.cc @@ -485,7 +485,7 @@ TEST_F(AccessibilityTest, GetAccessibilityHighlightInfo) { constexpr uint32_t kHighlightNoColor = MakeARGB(0, 0, 0, 0); static const pp::PDF::PrivateAccessibilityHighlightInfo kExpectedHighlightInfo[] = { - {"", 0, 0, 1, {{5, 196}, {49, 26}}, kHighlightDefaultColor}, + {"Text Note", 0, 0, 1, {{5, 196}, {49, 26}}, kHighlightDefaultColor}, {"", 1, 2, 1, {{110, 196}, {77, 26}}, kHighlightRedColor}, {"", 2, 3, 1, {{192, 196}, {13, 26}}, kHighlightNoColor}}; @@ -520,6 +520,7 @@ TEST_F(AccessibilityTest, GetAccessibilityHighlightInfo) { EXPECT_EQ(highlight_info.text_run_count, kExpectedHighlightInfo[i].text_run_count); EXPECT_EQ(highlight_info.color, kExpectedHighlightInfo[i].color); + EXPECT_EQ(highlight_info.note_text, kExpectedHighlightInfo[i].note_text); } } @@ -563,12 +564,12 @@ TEST_F(AccessibilityTest, GetAccessibilityTextFieldInfo) { CompareRect(kExpectedPageRect, page_info.bounds); EXPECT_EQ(text_runs.size(), page_info.text_run_count); EXPECT_EQ(chars.size(), page_info.char_count); - ASSERT_EQ(page_objects.text_fields.size(), + ASSERT_EQ(page_objects.form_fields.text_fields.size(), base::size(kExpectedTextFieldInfo)); - for (size_t i = 0; i < page_objects.text_fields.size(); ++i) { + for (size_t i = 0; i < page_objects.form_fields.text_fields.size(); ++i) { const pp::PDF::PrivateAccessibilityTextFieldInfo& text_field_info = - page_objects.text_fields[i]; + page_objects.form_fields.text_fields[i]; EXPECT_EQ(kExpectedTextFieldInfo[i].name, text_field_info.name); EXPECT_EQ(kExpectedTextFieldInfo[i].value, text_field_info.value); EXPECT_EQ(kExpectedTextFieldInfo[i].is_read_only, diff --git a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc index 382b3a9385c..9e02e34906f 100644 --- a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc +++ b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc @@ -6,14 +6,13 @@ #include <stddef.h> -#include <string> - #include "base/check_op.h" -#include "base/strings/string16.h" #include "base/strings/string_util.h" namespace chrome_pdf { +namespace internal { + template <class StringType> PDFiumAPIStringBufferAdapter<StringType>::PDFiumAPIStringBufferAdapter( StringType* str, @@ -52,36 +51,30 @@ void PDFiumAPIStringBufferAdapter<StringType>::Close(size_t actual_size) { } } -template <class StringType> -PDFiumAPIStringBufferSizeInBytesAdapter<StringType>:: - PDFiumAPIStringBufferSizeInBytesAdapter(StringType* str, +PDFiumAPIStringBufferSizeInBytesAdapter:: + PDFiumAPIStringBufferSizeInBytesAdapter(base::string16* str, size_t expected_size, bool check_expected_size) - : adapter_(str, - expected_size / sizeof(typename StringType::value_type), - check_expected_size) { - DCHECK(expected_size % sizeof(typename StringType::value_type) == 0); + : adapter_(str, expected_size / sizeof(base::char16), check_expected_size) { + DCHECK(expected_size % sizeof(base::char16) == 0); } -template <class StringType> -PDFiumAPIStringBufferSizeInBytesAdapter< - StringType>::~PDFiumAPIStringBufferSizeInBytesAdapter() = default; +PDFiumAPIStringBufferSizeInBytesAdapter:: + ~PDFiumAPIStringBufferSizeInBytesAdapter() = default; -template <class StringType> -void* PDFiumAPIStringBufferSizeInBytesAdapter<StringType>::GetData() { +void* PDFiumAPIStringBufferSizeInBytesAdapter::GetData() { return adapter_.GetData(); } -template <class StringType> -void PDFiumAPIStringBufferSizeInBytesAdapter<StringType>::Close( - size_t actual_size) { - DCHECK(actual_size % sizeof(typename StringType::value_type) == 0); - adapter_.Close(actual_size / sizeof(typename StringType::value_type)); +void PDFiumAPIStringBufferSizeInBytesAdapter::Close(size_t actual_size) { + DCHECK(actual_size % sizeof(base::char16) == 0); + adapter_.Close(actual_size / sizeof(base::char16)); } // explicit instantiations template class PDFiumAPIStringBufferAdapter<std::string>; template class PDFiumAPIStringBufferAdapter<base::string16>; -template class PDFiumAPIStringBufferSizeInBytesAdapter<base::string16>; + +} // namespace internal } // namespace chrome_pdf diff --git a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h index d3a413cd5a7..9c2587a19bf 100644 --- a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h +++ b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h @@ -7,16 +7,21 @@ #include <stddef.h> +#include <string> + +#include "base/callback.h" #include "base/macros.h" #include "base/numerics/safe_math.h" +#include "base/strings/string16.h" namespace chrome_pdf { +namespace internal { + // Helper to deal with the fact that many PDFium APIs write the null-terminator -// into string buffers that are passed to them, but the PDF plugin likes to pass -// in std::strings / base::string16s, where one should not count on the internal +// into string buffers that are passed to them, but the PDF code likes to use +// std::strings / base::string16s, where one should not count on the internal // string buffers to be null-terminated. - template <class StringType> class PDFiumAPIStringBufferAdapter { public: @@ -56,11 +61,11 @@ class PDFiumAPIStringBufferAdapter { }; // Helper to deal with the fact that many PDFium APIs write the null-terminator -// into string buffers that are passed to them, but the PDF plugin likes to pass -// in std::strings / base::string16s, where one should not count on the internal +// into string buffers that are passed to them, but the PDF code likes to use +// std::strings / base::string16s, where one should not count on the internal // string buffers to be null-terminated. This version is suitable for APIs that -// work in terms of number of bytes instead of the number of characters. -template <class StringType> +// work in terms of number of bytes instead of the number of characters. Though +// for std::strings, PDFiumAPIStringBufferAdapter is equivalent. class PDFiumAPIStringBufferSizeInBytesAdapter { public: // |str| is the string to write into. @@ -69,19 +74,19 @@ class PDFiumAPIStringBufferSizeInBytesAdapter { // character in bytes. // |check_expected_size| whether to check the actual number of bytes // written into |str| against |expected_size| when calling Close(). - PDFiumAPIStringBufferSizeInBytesAdapter(StringType* str, + PDFiumAPIStringBufferSizeInBytesAdapter(base::string16* str, size_t expected_size, bool check_expected_size); ~PDFiumAPIStringBufferSizeInBytesAdapter(); // Returns a pointer to |str_|'s buffer. The buffer's size is large enough to - // hold |expected_size_| + sizeof(StringType::value_type) bytes, so the PDFium - // API that uses the pointer has space to write a null-terminator. + // hold |expected_size_| + sizeof(base::char16) bytes, so the PDFium API that + // uses the pointer has space to write a null-terminator. void* GetData(); - // Resizes |str_| to |actual_size| - sizeof(StringType::value_type) bytes, - // thereby removing the extra null-terminator. This must be called prior to - // the adapter's destruction. The pointer returned by GetData() should be + // Resizes |str_| to |actual_size| - sizeof(base::char16) bytes, thereby + // removing the extra null-terminator. This must be called prior to the + // adapter's destruction. The pointer returned by GetData() should be // considered invalid. void Close(size_t actual_size); @@ -91,9 +96,56 @@ class PDFiumAPIStringBufferSizeInBytesAdapter { } private: - PDFiumAPIStringBufferAdapter<StringType> adapter_; + PDFiumAPIStringBufferAdapter<base::string16> adapter_; }; +template <class AdapterType, + class StringType, + typename BufferType, + typename ReturnType> +StringType CallPDFiumStringBufferApi( + base::RepeatingCallback<ReturnType(BufferType*, ReturnType)> api, + bool check_expected_size) { + StringType str; + ReturnType expected_size = api.Run(nullptr, 0); + if (expected_size > 0) { + AdapterType api_string_adapter(&str, expected_size, check_expected_size); + auto* data = reinterpret_cast<BufferType*>(api_string_adapter.GetData()); + api_string_adapter.Close(api.Run(data, expected_size)); + } + return str; +} + +} // namespace internal + +// Helper function to call PDFium APIs where the output buffer is expected to +// hold UTF-16 data, and the buffer length is specified in bytes. +template <typename BufferType> +base::string16 CallPDFiumWideStringBufferApi( + base::RepeatingCallback<unsigned long(BufferType*, unsigned long)> api, + bool check_expected_size) { + using adapter_type = internal::PDFiumAPIStringBufferSizeInBytesAdapter; + return internal::CallPDFiumStringBufferApi<adapter_type, base::string16>( + api, check_expected_size); +} + +// Helper function to call PDFium APIs where the output buffer is expected to +// hold ASCII or UTF-8 data, and the buffer length is specified in bytes. +template <typename BufferType, typename ReturnType> +std::string CallPDFiumStringBufferApi( + base::RepeatingCallback<ReturnType(BufferType*, ReturnType)> api, + bool check_expected_size) { + using adapter_type = internal::PDFiumAPIStringBufferAdapter<std::string>; + return internal::CallPDFiumStringBufferApi<adapter_type, std::string>( + api, check_expected_size); +} + +// Expose internal::PDFiumAPIStringBufferAdapter for special cases that cannot +// use the CallPDFiumStringBuffer* functions above. +template <class StringType> +using PDFiumAPIStringBufferAdapter = + internal::PDFiumAPIStringBufferAdapter<StringType>; + } // namespace chrome_pdf #endif // PDF_PDFIUM_PDFIUM_API_STRING_BUFFER_ADAPTER_H_ diff --git a/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc b/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc index a58ecff0daa..fc52f804e9c 100644 --- a/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc +++ b/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc @@ -4,10 +4,12 @@ #include "build/build_config.h" #include "pdf/pdf.h" +#include "pdf/pdf_engine.h" #include "ppapi/c/pp_input_event.h" #include "ppapi/c/private/ppb_pdf.h" #include "ppapi/c/private/ppp_pdf.h" #include "third_party/pdfium/public/fpdf_edit.h" +#include "third_party/pdfium/public/fpdf_formfill.h" #include "third_party/pdfium/public/fpdf_fwlevent.h" #include "third_party/pdfium/public/fpdf_sysfontinfo.h" #include "third_party/pdfium/public/fpdfview.h" @@ -229,6 +231,19 @@ STATIC_ASSERT_ENUM(PP_TEXTRENDERINGMODE_FILLSTROKECLIP, FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP); STATIC_ASSERT_ENUM(PP_TEXTRENDERINGMODE_CLIP, FPDF_TEXTRENDERMODE_CLIP); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kNone, FORMTYPE_NONE); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kAcroForm, + FORMTYPE_ACRO_FORM); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kXFAFull, + FORMTYPE_XFA_FULL); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kXFAForeground, + FORMTYPE_XFA_FOREGROUND); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kCount, FORMTYPE_COUNT); + +STATIC_ASSERT_ENUM(PP_PRIVATEBUTTON_PUSHBUTTON, FPDF_FORMFIELD_PUSHBUTTON); +STATIC_ASSERT_ENUM(PP_PRIVATEBUTTON_CHECKBOX, FPDF_FORMFIELD_CHECKBOX); +STATIC_ASSERT_ENUM(PP_PRIVATEBUTTON_RADIOBUTTON, FPDF_FORMFIELD_RADIOBUTTON); + #if defined(OS_WIN) STATIC_ASSERT_ENUM(chrome_pdf::kEmf, FPDF_PRINTMODE_EMF); STATIC_ASSERT_ENUM(chrome_pdf::kTextOnly, FPDF_PRINTMODE_TEXTONLY); diff --git a/chromium/pdf/pdfium/pdfium_engine.cc b/chromium/pdf/pdfium/pdfium_engine.cc index 090a8edcbdf..66f988b568b 100644 --- a/chromium/pdf/pdfium/pdfium_engine.cc +++ b/chromium/pdf/pdfium/pdfium_engine.cc @@ -28,6 +28,7 @@ #include "gin/array_buffer.h" #include "gin/public/gin_embedders.h" #include "gin/public/isolate_holder.h" +#include "gin/public/v8_platform.h" #include "pdf/document_loader_impl.h" #include "pdf/draw_utils/coordinates.h" #include "pdf/draw_utils/shadow.h" @@ -68,20 +69,6 @@ using printing::kPixelsPerInch; namespace chrome_pdf { -static_assert(static_cast<int>(PDFEngine::FormType::kNone) == FORMTYPE_NONE, - "None form types must match"); -static_assert(static_cast<int>(PDFEngine::FormType::kAcroForm) == - FORMTYPE_ACRO_FORM, - "AcroForm form types must match"); -static_assert(static_cast<int>(PDFEngine::FormType::kXFAFull) == - FORMTYPE_XFA_FULL, - "XFA full form types must match"); -static_assert(static_cast<int>(PDFEngine::FormType::kXFAForeground) == - FORMTYPE_XFA_FOREGROUND, - "XFA foreground form types must match"); -static_assert(static_cast<int>(PDFEngine::FormType::kCount) == FORMTYPE_COUNT, - "Form type counts must match"); - namespace { constexpr int32_t kHighlightColorR = 153; @@ -234,8 +221,10 @@ bool IsV8Initialized() { void SetUpV8() { const char* recommended = FPDF_GetRecommendedV8Flags(); v8::V8::SetFlagsFromString(recommended, strlen(recommended)); - gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode, - gin::ArrayBufferAllocator::SharedInstance()); + gin::IsolateHolder::Initialize( + gin::IsolateHolder::kNonStrictMode, + static_cast<v8::ArrayBuffer::Allocator*>( + FPDF_GetArrayBufferAllocatorSharedInstance())); DCHECK(!g_isolate_holder); g_isolate_holder = new gin::IsolateHolder( base::ThreadTaskRunnerHandle::Get(), gin::IsolateHolder::kSingleThread, @@ -365,18 +354,44 @@ void SetLinkUnderCursor(pp::Instance* instance, pp::PDF::SetLinkUnderCursor(instance, link_under_cursor.c_str()); } +base::string16 GetAttachmentAttribute(FPDF_ATTACHMENT attachment, + FPDF_BYTESTRING field) { + return CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAttachment_GetStringValue, attachment, field), + /*check_expected_size=*/true); +} + +unsigned long GetAttachmentFileLengthInBytes(FPDF_ATTACHMENT attachment) { + unsigned long actual_length_bytes; + bool is_attachment_readable = FPDFAttachment_GetFile( + attachment, /*buffer=*/nullptr, /*buflen=*/0, &actual_length_bytes); + DCHECK(is_attachment_readable); + return actual_length_bytes; +} + +base::string16 GetAttachmentName(FPDF_ATTACHMENT attachment) { + return CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAttachment_GetName, attachment), + /*check_expected_size=*/true); +} + } // namespace void InitializeSDK(bool enable_v8) { FPDF_LIBRARY_CONFIG config; - config.version = 2; + config.version = 3; config.m_pUserFontPaths = nullptr; if (enable_v8) { SetUpV8(); config.m_pIsolate = v8::Isolate::GetCurrent(); + // NOTE: static_cast<> prior to assigning to (void*) is safer since it + // will manipulate the pointer value should gin::V8Platform someday have + // multiple base classes. + config.m_pPlatform = static_cast<v8::Platform*>(gin::V8Platform::Get()); } else { config.m_pIsolate = nullptr; + config.m_pPlatform = nullptr; } config.m_v8EmbedderSlot = gin::kEmbedderPDFium; FPDF_InitLibraryWithConfig(&config); @@ -746,6 +761,8 @@ void PDFiumEngine::FinishLoadingDocument() { if (need_update) LoadPageInfo(); + LoadDocumentAttachmentInfoList(); + LoadDocumentMetadata(); if (called_do_document_action_) @@ -948,7 +965,7 @@ void PDFiumEngine::KillFormFocus() { void PDFiumEngine::UpdateFocus(bool has_focus) { base::AutoReset<bool> updating_focus_guard(&updating_focus_, true); if (has_focus) { - focus_item_type_ = last_focused_item_type_; + UpdateFocusItemType(last_focused_item_type_); if (focus_item_type_ == FocusElementType::kPage && PageIndexInBounds(last_focused_page_) && last_focused_annot_index_ != -1) { @@ -962,7 +979,7 @@ void PDFiumEngine::UpdateFocus(bool has_focus) { } else { last_focused_item_type_ = focus_item_type_; if (focus_item_type_ == FocusElementType::kDocument) { - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); } else if (focus_item_type_ == FocusElementType::kPage) { FPDF_ANNOTATION last_focused_annot = nullptr; FPDF_BOOL ret = FORM_GetFocusedAnnot(form(), &last_focused_page_, @@ -991,21 +1008,15 @@ bool PDFiumEngine::ReadLoadedBytes(uint32_t length, void* buffer) { void PDFiumEngine::SetFormSelectedText(FPDF_FORMHANDLE form_handle, FPDF_PAGE page) { - unsigned long form_sel_text_len = - FORM_GetSelectedText(form_handle, page, nullptr, 0); + base::string16 selected_form_text16 = CallPDFiumWideStringBufferApi( + base::BindRepeating(&FORM_GetSelectedText, form_handle, page), + /*check_expected_size=*/false); // If form selected text is empty and there was no previous form text - // selection, exit early because nothing has changed. When |form_sel_text_len| - // is 2, that represents a wide string with just a NUL-terminator. - if (form_sel_text_len <= 2 && selected_form_text_.empty()) + // selection, exit early because nothing has changed. + if (selected_form_text16.empty() && selected_form_text_.empty()) return; - base::string16 selected_form_text16; - PDFiumAPIStringBufferSizeInBytesAdapter<base::string16> string_adapter( - &selected_form_text16, form_sel_text_len, false); - string_adapter.Close(FORM_GetSelectedText( - form_handle, page, string_adapter.GetData(), form_sel_text_len)); - // Update previous and current selections, then compare them to check if // selection has changed. If so, set plugin text selection. std::string selected_form_text = selected_form_text_; @@ -1098,6 +1109,9 @@ void PDFiumEngine::OnMultipleClick(int click_count, selection_.push_back(PDFiumRange(pages_[page_index].get(), start_index, end_index - start_index)); + + if (handling_long_press_) + client_->NotifyTouchSelectionOccurred(); } bool PDFiumEngine::OnLeftMouseDown(const pp::MouseInputEvent& event) { @@ -1123,7 +1137,7 @@ bool PDFiumEngine::OnLeftMouseDown(const pp::MouseInputEvent& event) { return true; if (page_index != -1) { - focus_item_type_ = FocusElementType::kPage; + UpdateFocusItemType(FocusElementType::kPage); last_focused_page_ = page_index; double page_x; double page_y; @@ -1554,6 +1568,17 @@ bool PDFiumEngine::OnKeyDown(const pp::KeyboardInputEvent& event) { OnChar(synthesized); } +#if !defined(OS_MACOSX) + // macOS doesn't have keyboard-triggered context menus. + // Scroll focused annotation into view when context menu is invoked through + // keyboard <Shift-F10>. + if (event.GetKeyCode() == FWL_VKEY_F10 && + (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_SHIFTKEY)) { + DCHECK(!rv); + ScrollFocusedAnnotationIntoView(); + } +#endif + return rv; } @@ -1574,8 +1599,17 @@ bool PDFiumEngine::OnChar(const pp::KeyboardInputEvent& event) { return false; base::string16 str = base::UTF8ToUTF16(event.GetCharacterText().AsString()); - return !!FORM_OnChar(form(), pages_[last_focused_page_]->GetPage(), str[0], - event.GetModifiers()); + bool rv = !!FORM_OnChar(form(), pages_[last_focused_page_]->GetPage(), str[0], + event.GetModifiers()); + + // Scroll editable form text into view on char events. We should not scroll + // focused annotation on escape char event since escape char is used to + // dismiss focus from form controls. + if (rv && editable_form_text_area_ && event.GetKeyCode() != ui::VKEY_ESCAPE) { + ScrollFocusedAnnotationIntoView(); + } + + return rv; } void PDFiumEngine::StartFind(const std::string& text, bool case_sensitive) { @@ -1974,6 +2008,14 @@ void PDFiumEngine::SetTwoUpView(bool enable) { ProposeNextDocumentLayout(); } +void PDFiumEngine::DisplayAnnotations(bool display) { + if (render_annots_ == display) + return; + + render_annots_ = display; + InvalidateAllPages(); +} + void PDFiumEngine::InvalidateAllPages() { CancelPaints(); StopFind(); @@ -2124,6 +2166,12 @@ void PDFiumEngine::SelectAll() { } } +const std::vector<DocumentAttachmentInfo>& +PDFiumEngine::GetDocumentAttachmentInfoList() const { + DCHECK(document_loaded_); + return doc_attachment_info_list_; +} + const DocumentMetadata& PDFiumEngine::GetDocumentMetadata() const { DCHECK(document_loaded_); return doc_metadata_; @@ -2142,14 +2190,9 @@ pp::VarArray PDFiumEngine::GetBookmarks() { pp::VarDictionary PDFiumEngine::TraverseBookmarks(FPDF_BOOKMARK bookmark, unsigned int depth) { pp::VarDictionary dict; - base::string16 title; - unsigned long buffer_size = FPDFBookmark_GetTitle(bookmark, nullptr, 0); - if (buffer_size > 0) { - PDFiumAPIStringBufferSizeInBytesAdapter<base::string16> api_string_adapter( - &title, buffer_size, true); - api_string_adapter.Close(FPDFBookmark_GetTitle( - bookmark, api_string_adapter.GetData(), buffer_size)); - } + base::string16 title = CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFBookmark_GetTitle, bookmark), + /*check_expected_size=*/true); dict.Set(pp::Var("title"), pp::Var(base::UTF16ToUTF8(title))); FPDF_DEST dest = FPDFBookmark_GetDest(doc(), bookmark); @@ -2173,15 +2216,11 @@ pp::VarDictionary PDFiumEngine::TraverseBookmarks(FPDF_BOOKMARK bookmark, } else { // Extract URI for bookmarks linking to an external page. FPDF_ACTION action = FPDFBookmark_GetAction(bookmark); - buffer_size = FPDFAction_GetURIPath(doc(), action, nullptr, 0); - if (buffer_size > 0) { - std::string uri; - PDFiumAPIStringBufferAdapter<std::string> api_string_adapter( - &uri, buffer_size, true); - api_string_adapter.Close(FPDFAction_GetURIPath( - doc(), action, api_string_adapter.GetData(), buffer_size)); + std::string uri = CallPDFiumStringBufferApi( + base::BindRepeating(&FPDFAction_GetURIPath, doc(), action), + /*check_expected_size=*/true); + if (!uri.empty()) dict.Set(pp::Var("uri"), pp::Var(uri)); - } } pp::VarArray children; @@ -2328,6 +2367,7 @@ void PDFiumEngine::SetGrayscale(bool grayscale) { } void PDFiumEngine::HandleLongPress(const pp::TouchInputEvent& event) { + base::AutoReset<bool> handling_long_press_guard(&handling_long_press_, true); pp::FloatPoint fp = event.GetTouchByIndex(PP_TOUCHLIST_TYPE_TARGETTOUCHES, 0).position(); pp::Point point; @@ -2719,7 +2759,8 @@ void PDFiumEngine::LoadForm() { kFormHighlightColor); FPDF_SetFormFieldHighlightAlpha(form(), kFormHighlightAlpha); - if (base::FeatureList::IsEnabled(features::kTabAcrossPDFAnnotations)) { + if (base::FeatureList::IsEnabled(features::kTabAcrossPDFAnnotations) && + !client_->IsPrintPreview()) { static constexpr FPDF_ANNOTATION_SUBTYPE kFocusableAnnotSubtypes[] = { FPDF_ANNOT_LINK, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_WIDGET}; FPDF_BOOL ret = FPDFAnnot_SetFocusableSubtypes( @@ -3558,12 +3599,12 @@ void PDFiumEngine::SetSelecting(bool selecting) { client_->IsSelectingChanged(selecting); } -void PDFiumEngine::SetEditMode(bool edit_mode) { - if (edit_mode_ == edit_mode) +void PDFiumEngine::EnteredEditMode() { + if (edit_mode_) return; - edit_mode_ = edit_mode; - client_->IsEditModeChanged(edit_mode_); + edit_mode_ = true; + client_->EnteredEditMode(); } void PDFiumEngine::SetInFormTextArea(bool in_form_text_area) { @@ -3671,7 +3712,30 @@ void PDFiumEngine::SetSelection( } } -void PDFiumEngine::ScrollIntoView(const pp::Rect& rect) { +void PDFiumEngine::ScrollFocusedAnnotationIntoView() { + FPDF_ANNOTATION annot; + int page_index; + if (!FORM_GetFocusedAnnot(form(), &page_index, &annot)) + return; + + ScrollAnnotationIntoView(annot, page_index); + FPDFPage_CloseAnnot(annot); +} + +void PDFiumEngine::ScrollAnnotationIntoView(FPDF_ANNOTATION annot, + int page_index) { + if (!PageIndexInBounds(page_index)) + return; + + FS_RECTF annot_rect; + if (!FPDFAnnot_GetRect(annot, &annot_rect)) + return; + + pp::Rect rect = pages_[page_index]->PageToScreen( + pp::Point(), /*zoom=*/1.0, annot_rect.left, annot_rect.top, + annot_rect.right, annot_rect.bottom, + layout_.options().default_page_orientation()); + pp::Rect visible_rect = GetVisibleRect(); if (visible_rect.Contains(rect)) return; @@ -3778,6 +3842,28 @@ void PDFiumEngine::GetSelection(uint32_t* selection_start_page_index, } } +void PDFiumEngine::LoadDocumentAttachmentInfoList() { + DCHECK(document_loaded_); + + int attachment_count = FPDFDoc_GetAttachmentCount(doc()); + if (attachment_count <= 0) + return; + + doc_attachment_info_list_.resize(attachment_count); + for (int i = 0; i < attachment_count; ++i) { + FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc(), i); + DCHECK(attachment); + + doc_attachment_info_list_[i].name = GetAttachmentName(attachment); + doc_attachment_info_list_[i].size_bytes = + GetAttachmentFileLengthInBytes(attachment); + doc_attachment_info_list_[i].creation_date = + GetAttachmentAttribute(attachment, "CreationDate"); + doc_attachment_info_list_[i].modified_date = + GetAttachmentAttribute(attachment, "ModDate"); + } +} + void PDFiumEngine::LoadDocumentMetadata() { DCHECK(document_loaded_); @@ -3793,17 +3879,9 @@ void PDFiumEngine::LoadDocumentMetadata() { std::string PDFiumEngine::GetMetadataByField(FPDF_BYTESTRING field) const { DCHECK(doc()); - size_t size = - FPDF_GetMetaText(doc(), field, /*buffer=*/nullptr, /*buflen=*/0); - if (size == 0) - return std::string(); - - base::string16 value; - PDFiumAPIStringBufferSizeInBytesAdapter<base::string16> string_adapter( - &value, size, /*check_expected_size=*/false); - string_adapter.Close( - FPDF_GetMetaText(doc(), field, string_adapter.GetData(), size)); - return base::UTF16ToUTF8(value); + return base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDF_GetMetaText, doc(), field), + /*check_expected_size=*/false)); } PdfVersion PDFiumEngine::GetDocumentVersion() const { @@ -3869,7 +3947,7 @@ bool PDFiumEngine::HandleTabEventWithModifiers(uint32_t modifiers) { bool PDFiumEngine::HandleTabForward(uint32_t modifiers) { if (focus_item_type_ == FocusElementType::kNone) { - focus_item_type_ = FocusElementType::kDocument; + UpdateFocusItemType(FocusElementType::kDocument); return true; } @@ -3887,17 +3965,17 @@ bool PDFiumEngine::HandleTabForward(uint32_t modifiers) { if (did_tab_forward) { last_focused_page_ = page_index; - focus_item_type_ = FocusElementType::kPage; + UpdateFocusItemType(FocusElementType::kPage); } else { last_focused_page_ = -1; - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); } return did_tab_forward; } bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { if (focus_item_type_ == FocusElementType::kDocument) { - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); return false; } @@ -3915,7 +3993,7 @@ bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { if (did_tab_backward) { last_focused_page_ = page_index; - focus_item_type_ = FocusElementType::kPage; + UpdateFocusItemType(FocusElementType::kPage); } else { // No focusable annotation found in pages. Possible scenarios: // Case 1: |focus_item_type_| is None. Since no object in any page can take @@ -3928,11 +4006,11 @@ bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { case FocusElementType::kNone: did_tab_backward = true; last_focused_page_ = -1; - focus_item_type_ = FocusElementType::kDocument; + UpdateFocusItemType(FocusElementType::kDocument); KillFormFocus(); break; case FocusElementType::kDocument: - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); break; default: NOTREACHED(); @@ -3942,6 +4020,16 @@ bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { return did_tab_backward; } +void PDFiumEngine::UpdateFocusItemType(FocusElementType focus_item_type) { + if (focus_item_type_ == focus_item_type) + return; + if (focus_item_type_ == FocusElementType::kDocument) + client_->DocumentFocusChanged(false); + focus_item_type_ = focus_item_type; + if (focus_item_type_ == FocusElementType::kDocument) + client_->DocumentFocusChanged(true); +} + #if defined(PDF_ENABLE_XFA) void PDFiumEngine::UpdatePageCount() { InvalidateAllPages(); diff --git a/chromium/pdf/pdfium/pdfium_engine.h b/chromium/pdf/pdfium/pdfium_engine.h index cf507e8d469..2261904a961 100644 --- a/chromium/pdf/pdfium/pdfium_engine.h +++ b/chromium/pdf/pdfium/pdfium_engine.h @@ -17,6 +17,7 @@ #include "base/optional.h" #include "base/time/time.h" #include "base/timer/timer.h" +#include "pdf/document_attachment_info.h" #include "pdf/document_layout.h" #include "pdf/document_loader.h" #include "pdf/document_metadata.h" @@ -99,6 +100,7 @@ class PDFiumEngine : public PDFEngine, void RotateClockwise() override; void RotateCounterclockwise() override; void SetTwoUpView(bool enable) override; + void DisplayAnnotations(bool display) override; pp::Size ApplyDocumentLayout(const DocumentLayout::Options& options) override; std::string GetSelectedText() override; bool CanEditText() override; @@ -113,6 +115,8 @@ class PDFiumEngine : public PDFEngine, std::string GetLinkAtPosition(const pp::Point& point) override; bool HasPermission(DocumentPermission permission) const override; void SelectAll() override; + const std::vector<DocumentAttachmentInfo>& GetDocumentAttachmentInfoList() + const override; const DocumentMetadata& GetDocumentMetadata() const override; int GetNumberOfPages() override; pp::VarArray GetBookmarks() override; @@ -564,7 +568,7 @@ class PDFiumEngine : public PDFEngine, const pp::Point& global_point); // Set if the document has any local edits. - void SetEditMode(bool edit_mode); + void EnteredEditMode(); // Navigates to a link destination depending on the type of destination. // Returns false if |area| is not a link. @@ -580,12 +584,18 @@ class PDFiumEngine : public PDFEngine, void SetSelection(const PP_PdfPageCharacterIndex& selection_start_index, const PP_PdfPageCharacterIndex& selection_end_index); - // Given |rect| in document coordinates, scroll the |rect| into view if not - // already in view. - void ScrollIntoView(const pp::Rect& rect); + // Scroll the current focused annotation into view if not already in view. + void ScrollFocusedAnnotationIntoView(); + + // Given |annot|, scroll the |annot| into view if not already in view. + void ScrollAnnotationIntoView(FPDF_ANNOTATION annot, int page_index); void OnFocusedAnnotationUpdated(FPDF_ANNOTATION annot, int page_index); + // Read the attachments' information inside the PDF document, and set + // |doc_attachment_info_list_|. To be called after the document is loaded. + void LoadDocumentAttachmentInfoList(); + // Fetches and populates the fields of |doc_metadata_|. To be called after the // document is loaded. void LoadDocumentMetadata(); @@ -606,6 +616,10 @@ class PDFiumEngine : public PDFEngine, bool HandleTabForward(uint32_t modifiers); bool HandleTabBackward(uint32_t modifiers); + // Updates the currently focused object stored in |focus_item_type_|. Notifies + // |client_| about document focus change, if any. + void UpdateFocusItemType(FocusElementType focus_item_type); + void UpdateLinkUnderCursor(const std::string& target_url); void SetLinkUnderCursorForAnnotation(FPDF_ANNOTATION annot, int page_index); @@ -710,6 +724,9 @@ class PDFiumEngine : public PDFEngine, // Timer for touch long press detection. base::OneShotTimer touch_timer_; + // Set to true when handling long touch press. + bool handling_long_press_ = false; + // Set to true when updating plugin focus. bool updating_focus_ = false; @@ -792,6 +809,9 @@ class PDFiumEngine : public PDFEngine, // Shadow matrix for generating the page shadow bitmap. std::unique_ptr<draw_utils::ShadowMatrix> page_shadow_; + // A list of information of document attachments. + std::vector<DocumentAttachmentInfo> doc_attachment_info_list_; + // Stores parsed document metadata. DocumentMetadata doc_metadata_; diff --git a/chromium/pdf/pdfium/pdfium_engine_unittest.cc b/chromium/pdf/pdfium/pdfium_engine_unittest.cc index 4aa29dd8451..978be627972 100644 --- a/chromium/pdf/pdfium/pdfium_engine_unittest.cc +++ b/chromium/pdf/pdfium/pdfium_engine_unittest.cc @@ -4,8 +4,10 @@ #include "pdf/pdfium/pdfium_engine.h" +#include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "pdf/document_attachment_info.h" #include "pdf/document_layout.h" #include "pdf/document_metadata.h" #include "pdf/pdf_features.h" @@ -185,6 +187,53 @@ TEST_F(PDFiumEngineTest, ApplyDocumentLayoutAvoidsInfiniteLoop) { CompareSize({343, 1463}, engine->ApplyDocumentLayout(options)); } +TEST_F(PDFiumEngineTest, GetDocumentAttachmentInfo) { + NiceMock<MockTestClient> client; + std::unique_ptr<PDFiumEngine> engine = + InitializeEngine(&client, FILE_PATH_LITERAL("embedded_attachments.pdf")); + ASSERT_TRUE(engine); + + const std::vector<DocumentAttachmentInfo>& attachments = + engine->GetDocumentAttachmentInfoList(); + ASSERT_EQ(3u, attachments.size()); + + { + const DocumentAttachmentInfo& attachment = attachments[0]; + EXPECT_EQ("1.txt", base::UTF16ToUTF8(attachment.name)); + EXPECT_EQ(4u, attachment.size_bytes); + EXPECT_EQ("D:20170712214438-07'00'", + base::UTF16ToUTF8(attachment.creation_date)); + EXPECT_EQ("D:20160115091400", base::UTF16ToUTF8(attachment.modified_date)); + } + + { + const DocumentAttachmentInfo& attachment = attachments[1]; + EXPECT_EQ("attached.pdf", base::UTF16ToUTF8(attachment.name)); + EXPECT_EQ(5869u, attachment.size_bytes); + EXPECT_EQ("D:20170712214443-07'00'", + base::UTF16ToUTF8(attachment.creation_date)); + EXPECT_EQ("D:20170712214410", base::UTF16ToUTF8(attachment.modified_date)); + } + + { + // Test attachments with no creation date or last modified date. + const DocumentAttachmentInfo& attachment = attachments[2]; + EXPECT_EQ("附錄.txt", base::UTF16ToUTF8(attachment.name)); + EXPECT_EQ(5u, attachment.size_bytes); + EXPECT_THAT(attachment.creation_date, IsEmpty()); + EXPECT_THAT(attachment.modified_date, IsEmpty()); + } +} + +TEST_F(PDFiumEngineTest, NoDocumentAttachmentInfo) { + NiceMock<MockTestClient> client; + std::unique_ptr<PDFiumEngine> engine = + InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf")); + ASSERT_TRUE(engine); + + EXPECT_EQ(0u, engine->GetDocumentAttachmentInfoList().size()); +} + TEST_F(PDFiumEngineTest, GetDocumentMetadata) { NiceMock<MockTestClient> client; std::unique_ptr<PDFiumEngine> engine = @@ -229,6 +278,17 @@ TEST_F(PDFiumEngineTest, GetBadPdfVersion) { } // namespace +class TabbingTestClient : public TestClient { + public: + TabbingTestClient() = default; + ~TabbingTestClient() override = default; + TabbingTestClient(const TabbingTestClient&) = delete; + TabbingTestClient& operator=(const TabbingTestClient&) = delete; + + // Mock PDFEngine::Client methods. + MOCK_METHOD1(DocumentFocusChanged, void(bool)); +}; + class PDFiumEngineTabbingTest : public PDFiumTestBase { public: PDFiumEngineTabbingTest() = default; @@ -269,6 +329,10 @@ class PDFiumEngineTabbingTest : public PDFiumTestBase { return engine->link_under_cursor_; } + void ScrollFocusedAnnotationIntoView(PDFiumEngine* engine) { + engine->ScrollFocusedAnnotationIntoView(); + } + protected: base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; @@ -378,13 +442,20 @@ TEST_F(PDFiumEngineTabbingTest, TabbingForwardTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr<PDFiumEngine> engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + ASSERT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -423,13 +494,20 @@ TEST_F(PDFiumEngineTabbingTest, TabbingBackwardTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr<PDFiumEngine> engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + ASSERT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -512,13 +590,20 @@ TEST_F(PDFiumEngineTabbingTest, NoFocusableItemTabbingTest) { * ++ Page 1 * ++ Page 2 */ - TestClient client; + TabbingTestClient client; std::unique_ptr<PDFiumEngine> engine = InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false, true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + ASSERT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -552,13 +637,20 @@ TEST_F(PDFiumEngineTabbingTest, RestoringDocumentFocusTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr<PDFiumEngine> engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false, true}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + EXPECT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -590,13 +682,20 @@ TEST_F(PDFiumEngineTabbingTest, RestoringAnnotFocusTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr<PDFiumEngine> engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + EXPECT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -773,4 +872,51 @@ TEST_F(PDFiumEngineTabbingTest, MaintainViewportWhenFocusIsUpdated) { EXPECT_EQ(0, GetLastFocusedPage(engine.get())); } +TEST_F(PDFiumEngineTabbingTest, ScrollFocusedAnnotationIntoView) { + StrictMock<ScrollingTestClient> client; + std::unique_ptr<PDFiumEngine> engine = InitializeEngine( + &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); + ASSERT_TRUE(engine); + ASSERT_EQ(2, engine->GetNumberOfPages()); + engine->PluginSizeUpdated(pp::Size(60, 40)); + + { + InSequence sequence; + static constexpr PP_Point kScrollValues[] = {{510, 478}, {510, 478}}; + + for (const auto& scroll_value : kScrollValues) { + EXPECT_CALL(client, ScrollToY(scroll_value.y, false)) + .WillOnce(Invoke([&engine, &scroll_value]() { + engine->ScrolledToYPosition(scroll_value.y); + })); + EXPECT_CALL(client, ScrollToX(scroll_value.x)) + .WillOnce(Invoke([&engine, &scroll_value]() { + engine->ScrolledToXPosition(scroll_value.x); + })); + } + } + + EXPECT_EQ(PDFiumEngine::FocusElementType::kNone, + GetFocusedElementType(engine.get())); + EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); + + // Tabbing to bring the document into focus. + ASSERT_TRUE(HandleTabEvent(engine.get(), 0)); + EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument, + GetFocusedElementType(engine.get())); + + // Tab to an annotation. + ASSERT_TRUE(HandleTabEvent(engine.get(), 0)); + EXPECT_EQ(PDFiumEngine::FocusElementType::kPage, + GetFocusedElementType(engine.get())); + + // Scroll focused annotation out of viewport. + static constexpr PP_Point kScrollPosition = {242, 746}; + engine->ScrolledToXPosition(kScrollPosition.x); + engine->ScrolledToYPosition(kScrollPosition.y); + + // Scroll the focused annotation into view. + ScrollFocusedAnnotationIntoView(engine.get()); +} + } // namespace chrome_pdf diff --git a/chromium/pdf/pdfium/pdfium_form_filler.cc b/chromium/pdf/pdfium/pdfium_form_filler.cc index 4a142b4cba5..d85bd2ceef1 100644 --- a/chromium/pdf/pdfium/pdfium_form_filler.cc +++ b/chromium/pdf/pdfium/pdfium_form_filler.cc @@ -194,7 +194,7 @@ FPDF_SYSTEMTIME PDFiumFormFiller::Form_GetLocalTime(FPDF_FORMFILLINFO* param) { // static void PDFiumFormFiller::Form_OnChange(FPDF_FORMFILLINFO* param) { PDFiumEngine* engine = GetEngine(param); - engine->SetEditMode(true); + engine->EnteredEditMode(); } // static @@ -285,17 +285,8 @@ void PDFiumFormFiller::Form_OnFocusChange(FPDF_FORMFILLINFO* param, // Maintain viewport if we are updating focus. This is to ensure that we don't // scroll the focused annotation into view when focus is regained. - if (!engine->updating_focus_) { - FS_RECTF annot_rect; - if (!FPDFAnnot_GetRect(annot, &annot_rect)) - return; - - pp::Rect screen_rect = engine->pages_[page_index]->PageToScreen( - pp::Point(), /*zoom=*/1.0, annot_rect.left, annot_rect.top, - annot_rect.right, annot_rect.bottom, - engine->layout_.options().default_page_orientation()); - engine->ScrollIntoView(screen_rect); - } + if (!engine->updating_focus_) + engine->ScrollAnnotationIntoView(annot, page_index); engine->OnFocusedAnnotationUpdated(annot, page_index); } diff --git a/chromium/pdf/pdfium/pdfium_page.cc b/chromium/pdf/pdfium/pdfium_page.cc index 4f5f5af65b8..6f95affddc8 100644 --- a/chromium/pdf/pdfium/pdfium_page.cc +++ b/chromium/pdf/pdfium/pdfium_page.cc @@ -186,24 +186,6 @@ bool FloatEquals(float f1, float f2) { kEpsilonScale * fmaxf(fmaxf(fabsf(f1), fabsf(f2)), kEpsilonScale); } -using GetFormFieldPropertyFunction = - base::RepeatingCallback<unsigned long(unsigned short* buffer, - unsigned long buflen)>; - -// Helper method to fetch string properties of form fields. -std::string GetFormFieldProperty(GetFormFieldPropertyFunction function) { - base::string16 data; - size_t buffer_size = function.Run(nullptr, 0); - if (buffer_size > 0) { - PDFiumAPIStringBufferSizeInBytesAdapter<base::string16> api_string_adapter( - &data, buffer_size, true); - api_string_adapter.Close(function.Run( - reinterpret_cast<unsigned short*>(api_string_adapter.GetData()), - buffer_size)); - } - return base::UTF16ToUTF8(data); -} - // Count overlaps across text annotations. template <typename T, typename U> uint32_t CountOverlaps(const std::vector<T>& first_set, @@ -472,6 +454,18 @@ PDFiumPage::GetTextRunInfo(int start_char_index) { info.direction = PP_PRIVATEDIRECTION_NONE; return info; } + + // If the first character in a text run is a space, we need to start + // |text_run_bounds| from the space character instead of the first + // non-space unicode character. + pp::FloatRect text_run_bounds = + actual_start_char_index > start_char_index + ? GetFloatCharRectInPixels(page, text_page, start_char_index) + : pp::FloatRect(); + + // Pdfium trims more than 1 consecutive spaces to 1 space. + DCHECK_LE(actual_start_char_index - start_char_index, 1); + int char_index = actual_start_char_index; // Set text run's style info from the first character of the text run. @@ -494,8 +488,8 @@ PDFiumPage::GetTextRunInfo(int start_char_index) { AddCharSizeToAverageCharSize(start_char_rect.Floatsize(), &avg_char_size, &non_whitespace_chars_count); - // Add first char to text run. - pp::FloatRect text_run_bounds = start_char_rect; + // Add first non-space char to text run. + text_run_bounds = text_run_bounds.Union(start_char_rect); PP_PrivateDirection char_direction = GetDirectionFromAngle(FPDFText_GetCharAngle(text_page, char_index)); if (char_index < chars_count) @@ -676,6 +670,7 @@ PDFiumPage::GetHighlightInfo() { highlight.bounding_rect.x(), highlight.bounding_rect.y(), highlight.bounding_rect.width(), highlight.bounding_rect.height()); cur_info.color = highlight.color; + cur_info.note_text = highlight.note_text; highlight_info.push_back(std::move(cur_info)); } return highlight_info; @@ -900,16 +895,11 @@ gfx::PointF PDFiumPage::TransformPageToScreenXY(const gfx::PointF& xy) { PDFiumPage::Area PDFiumPage::GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const { if (target) { - size_t buffer_size = - FPDFAction_GetURIPath(engine_->doc(), uri_action, nullptr, 0); - if (buffer_size > 0) { - PDFiumAPIStringBufferAdapter<std::string> api_string_adapter( - &target->url, buffer_size, true); - void* data = api_string_adapter.GetData(); - size_t bytes_written = - FPDFAction_GetURIPath(engine_->doc(), uri_action, data, buffer_size); - api_string_adapter.Close(bytes_written); - } + std::string url = CallPDFiumStringBufferApi( + base::BindRepeating(&FPDFAction_GetURIPath, engine_->doc(), uri_action), + /*check_expected_size=*/true); + if (!url.empty()) + target->url = url; } return WEBLINK_AREA; } @@ -956,6 +946,8 @@ void PDFiumPage::PopulateWebLinks() { ScopedFPDFPageLink links(FPDFLink_LoadWebLinks(GetTextPage())); int count = FPDFLink_CountWebLinks(links.get()); for (int i = 0; i < count; ++i) { + // WARNING: FPDFLink_GetURL() is not compatible with + // CallPDFiumWideStringBufferApi(). base::string16 url; int url_length = FPDFLink_GetURL(links.get(), i, nullptr, 0); if (url_length > 0) { @@ -1141,16 +1133,11 @@ void PDFiumPage::PopulateImageAltTextForStructElement( auto it = marked_content_id_image_map.find(marked_content_id); if (it != marked_content_id_image_map.end() && images_[it->second].alt_text.empty()) { - size_t buffer_size = - FPDF_StructElement_GetAltText(current_element, nullptr, 0); - if (buffer_size > 0) { - base::string16 alt_text; - PDFiumAPIStringBufferSizeInBytesAdapter<base::string16> - api_string_adapter(&alt_text, buffer_size, true); - api_string_adapter.Close(FPDF_StructElement_GetAltText( - current_element, api_string_adapter.GetData(), buffer_size)); - images_[it->second].alt_text = base::UTF16ToUTF8(alt_text); - } + images_[it->second].alt_text = + base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDF_StructElement_GetAltText, + current_element), + /*check_expected_size=*/true)); } } int children_count = FPDF_StructElement_CountChildren(current_element); @@ -1182,11 +1169,7 @@ void PDFiumPage::PopulateAnnotations() { break; } case FPDF_ANNOT_WIDGET: { - // TODO(crbug.com/1030242): Populate other types of form fields too. - if (FPDFAnnot_GetFormFieldType(engine_->form(), annot.get()) == - FPDF_FORMFIELD_TEXTFIELD) { - PopulateTextField(annot.get()); - } + PopulateFormField(annot.get()); break; } default: @@ -1230,6 +1213,14 @@ void PDFiumPage::PopulateHighlight(FPDF_ANNOTATION annot) { highlight.color = MakeARGB(255, 255, 255, 0); } + // Retrieve the contents of the popup note associated with highlight. + // See table 164 in ISO 32000-1 standard for more details around "Contents" + // key in a highlight annotation. + static constexpr char kContents[] = "Contents"; + highlight.note_text = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetStringValue, annot, kContents), + /*check_expected_size=*/true)); + highlights_.push_back(std::move(highlight)); } @@ -1239,21 +1230,81 @@ void PDFiumPage::PopulateTextField(FPDF_ANNOTATION annot) { DCHECK_EQ(FPDFAnnot_GetFormFieldType(form_handle, annot), FPDF_FORMFIELD_TEXTFIELD); + TextField text_field; + if (!PopulateFormFieldProperties(annot, &text_field)) + return; + + text_field.value = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetFormFieldValue, form_handle, annot), + /*check_expected_size=*/true)); + text_fields_.push_back(std::move(text_field)); +} + +void PDFiumPage::PopulateChoiceField(FPDF_ANNOTATION annot) { + DCHECK(annot); + FPDF_FORMHANDLE form_handle = engine_->form(); + int form_field_type = FPDFAnnot_GetFormFieldType(form_handle, annot); + DCHECK(form_field_type == FPDF_FORMFIELD_LISTBOX || + form_field_type == FPDF_FORMFIELD_COMBOBOX); + + ChoiceField choice_field; + if (!PopulateFormFieldProperties(annot, &choice_field)) + return; + + int options_count = FPDFAnnot_GetOptionCount(form_handle, annot); + if (options_count < 0) + return; + + choice_field.options.resize(options_count); + for (int i = 0; i < options_count; ++i) { + choice_field.options[i].name = + base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetOptionLabel, form_handle, annot, + i), + /*check_expected_size=*/true)); + choice_field.options[i].is_selected = + FPDFAnnot_IsOptionSelected(form_handle, annot, i); + } + choice_fields_.push_back(std::move(choice_field)); +} + +void PDFiumPage::PopulateFormField(FPDF_ANNOTATION annot) { + DCHECK_EQ(FPDFAnnot_GetSubtype(annot), FPDF_ANNOT_WIDGET); + int form_field_type = FPDFAnnot_GetFormFieldType(engine_->form(), annot); + + // TODO(crbug.com/1030242): Populate other types of form fields too. + switch (form_field_type) { + case FPDF_FORMFIELD_COMBOBOX: + case FPDF_FORMFIELD_LISTBOX: { + PopulateChoiceField(annot); + break; + } + case FPDF_FORMFIELD_TEXTFIELD: { + PopulateTextField(annot); + break; + } + default: + break; + } +} + +bool PDFiumPage::PopulateFormFieldProperties(FPDF_ANNOTATION annot, + FormField* form_field) { + DCHECK(annot); FS_RECTF rect; if (!FPDFAnnot_GetRect(annot, &rect)) - return; + return false; - TextField text_field; - // We use the bounding box of the text field as the bounding rect. - text_field.bounding_rect = + // We use the bounding box of the form field as the bounding rect. + form_field->bounding_rect = PageToScreen(pp::Point(), 1.0, rect.left, rect.top, rect.right, rect.bottom, PageOrientation::kOriginal); - text_field.value = GetFormFieldProperty( - base::BindRepeating(FPDFAnnot_GetFormFieldValue, form_handle, annot)); - text_field.name = GetFormFieldProperty( - base::BindRepeating(FPDFAnnot_GetFormFieldName, form_handle, annot)); - text_field.flags = FPDFAnnot_GetFormFieldFlags(form_handle, annot); - text_fields_.push_back(std::move(text_field)); + FPDF_FORMHANDLE form_handle = engine_->form(); + form_field->name = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetFormFieldName, form_handle, annot), + /*check_expected_size=*/true)); + form_field->flags = FPDFAnnot_GetFormFieldFlags(form_handle, annot); + return true; } bool PDFiumPage::GetUnderlyingTextRangeForRect(const pp::FloatRect& rect, @@ -1385,12 +1436,31 @@ PDFiumPage::Highlight::Highlight(const Highlight& that) = default; PDFiumPage::Highlight::~Highlight() = default; +PDFiumPage::FormField::FormField() = default; + +PDFiumPage::FormField::FormField(const FormField& that) = default; + +PDFiumPage::FormField::~FormField() = default; + PDFiumPage::TextField::TextField() = default; PDFiumPage::TextField::TextField(const TextField& that) = default; PDFiumPage::TextField::~TextField() = default; +PDFiumPage::ChoiceFieldOption::ChoiceFieldOption() = default; + +PDFiumPage::ChoiceFieldOption::ChoiceFieldOption( + const ChoiceFieldOption& that) = default; + +PDFiumPage::ChoiceFieldOption::~ChoiceFieldOption() = default; + +PDFiumPage::ChoiceField::ChoiceField() = default; + +PDFiumPage::ChoiceField::ChoiceField(const ChoiceField& that) = default; + +PDFiumPage::ChoiceField::~ChoiceField() = default; + // static uint32_t PDFiumPage::CountLinkHighlightOverlaps( const std::vector<Link>& links, diff --git a/chromium/pdf/pdfium/pdfium_page.h b/chromium/pdf/pdfium/pdfium_page.h index 5a3aefae9da..baaec24e081 100644 --- a/chromium/pdf/pdfium/pdfium_page.h +++ b/chromium/pdf/pdfium/pdfium_page.h @@ -178,63 +178,10 @@ class PDFiumPage { FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, TestLinkGeneration); FRIEND_TEST_ALL_PREFIXES(PDFiumPageHighlightTest, TestPopulateHighlights); FRIEND_TEST_ALL_PREFIXES(PDFiumPageTextFieldTest, TestPopulateTextFields); + FRIEND_TEST_ALL_PREFIXES(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields); FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountPartialOverlaps); FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountCompleteOverlaps); - // Returns a link index if the given character index is over a link, or -1 - // otherwise. - int GetLink(int char_index, LinkTarget* target); - // Calculate the locations of any links on the page. - void CalculateLinks(); - // Populates weblinks on the page. - void PopulateWebLinks(); - // Populates annotation links on the page. - void PopulateAnnotationLinks(); - // Calculate the locations of images on the page. - void CalculateImages(); - // Populate annotations like highlight and text field on the page. - void PopulateAnnotations(); - // Populate |highlights_| with |annot|. - void PopulateHighlight(FPDF_ANNOTATION annot); - // Populate |text_fields_| with |annot|. - void PopulateTextField(FPDF_ANNOTATION annot); - // Returns link type and fills target associated with a destination. Returns - // NONSELECTABLE_AREA if detection failed. - Area GetDestinationTarget(FPDF_DEST destination, LinkTarget* target); - // Returns link type and fills target associated with a URI action. Returns - // NONSELECTABLE_AREA if detection failed. - Area GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const; - // Calculates the set of character indices on which text runs need to be - // broken for page objects such as links and images. - void CalculatePageObjectTextRunBreaks(); - // Set text run style information based on a character of the text run. - void CalculateTextRunStyleInfo( - int char_index, - pp::PDF::PrivateAccessibilityTextStyleInfo* style_info); - // Returns a boolean indicating if the character at index |char_index| has the - // same text style as the text run. - bool AreTextStyleEqual( - int char_index, - const pp::PDF::PrivateAccessibilityTextStyleInfo& style); - - // Key : Marked content id for the image element as specified in the - // struct tree. - // Value : Index of image in the |images_| vector. - using MarkedContentIdToImageMap = std::map<int, size_t>; - // Traverses the entire struct tree of the page recursively and extracts the - // alt text from struct tree elements corresponding to the marked content IDs - // present in |marked_content_id_image_map|. - void PopulateImageAltText( - const MarkedContentIdToImageMap& marked_content_id_image_map); - // Traverses a struct element and its sub-tree recursively and extracts the - // alt text from struct elements corresponding to the marked content IDs - // present in |marked_content_id_image_map|. Uses |visited_elements| to guard - // against malformed struct trees. - void PopulateImageAltTextForStructElement( - const MarkedContentIdToImageMap& marked_content_id_image_map, - FPDF_STRUCTELEMENT current_element, - std::set<FPDF_STRUCTELEMENT>* visited_elements); - class ScopedUnloadPreventer { public: explicit ScopedUnloadPreventer(PDFiumPage* page); @@ -284,25 +231,114 @@ class PDFiumPage { // Color of the highlight in ARGB. Alpha is stored in the first 8 MSBs. RGB // follows after it with each using 8 bytes. uint32_t color; + + // Text of the popup note associated with highlight. + std::string note_text; + }; + + // Represents a form field within the page. + struct FormField { + FormField(); + FormField(const FormField& other); + ~FormField(); + + pp::Rect bounding_rect; + // Represents the name of form field as defined in the field dictionary. + std::string name; + // Represents the flags of form field as defined in the field dictionary. + int flags; }; // Represents a text field within the page. - struct TextField { + struct TextField : FormField { TextField(); TextField(const TextField& other); ~TextField(); - // Represents the name of form field as defined in the field dictionary. - std::string name; std::string value; - pp::Rect bounding_rect; - // Represents the flags of form field as defined in the field dictionary. - int flags; }; + // Represents a choice field option. + struct ChoiceFieldOption { + ChoiceFieldOption(); + ChoiceFieldOption(const ChoiceFieldOption& other); + ~ChoiceFieldOption(); + + std::string name; + bool is_selected; + }; + + // Represents a choice field within the page. + struct ChoiceField : FormField { + ChoiceField(); + ChoiceField(const ChoiceField& other); + ~ChoiceField(); + + std::vector<ChoiceFieldOption> options; + }; + + // Returns a link index if the given character index is over a link, or -1 + // otherwise. + int GetLink(int char_index, LinkTarget* target); + // Calculate the locations of any links on the page. + void CalculateLinks(); + // Populates weblinks on the page. + void PopulateWebLinks(); + // Populates annotation links on the page. + void PopulateAnnotationLinks(); + // Calculate the locations of images on the page. + void CalculateImages(); + // Populate annotations like highlight and text field on the page. + void PopulateAnnotations(); + // Populate |highlights_| with |annot|. + void PopulateHighlight(FPDF_ANNOTATION annot); + // Populate |text_fields_| with |annot|. + void PopulateTextField(FPDF_ANNOTATION annot); + // Populate |choice_fields_| with |annot|. + void PopulateChoiceField(FPDF_ANNOTATION annot); + // Populate form fields like text field and choice field on the page. + void PopulateFormField(FPDF_ANNOTATION annot); + // Returns link type and fills target associated with a destination. Returns + // NONSELECTABLE_AREA if detection failed. + Area GetDestinationTarget(FPDF_DEST destination, LinkTarget* target); + // Returns link type and fills target associated with a URI action. Returns + // NONSELECTABLE_AREA if detection failed. + Area GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const; + // Calculates the set of character indices on which text runs need to be + // broken for page objects such as links and images. + void CalculatePageObjectTextRunBreaks(); + // Set text run style information based on a character of the text run. + void CalculateTextRunStyleInfo( + int char_index, + pp::PDF::PrivateAccessibilityTextStyleInfo* style_info); + // Returns a boolean indicating if the character at index |char_index| has the + // same text style as the text run. + bool AreTextStyleEqual( + int char_index, + const pp::PDF::PrivateAccessibilityTextStyleInfo& style); + + // Key : Marked content id for the image element as specified in the + // struct tree. + // Value : Index of image in the |images_| vector. + using MarkedContentIdToImageMap = std::map<int, size_t>; + // Traverses the entire struct tree of the page recursively and extracts the + // alt text from struct tree elements corresponding to the marked content IDs + // present in |marked_content_id_image_map|. + void PopulateImageAltText( + const MarkedContentIdToImageMap& marked_content_id_image_map); + // Traverses a struct element and its sub-tree recursively and extracts the + // alt text from struct elements corresponding to the marked content IDs + // present in |marked_content_id_image_map|. Uses |visited_elements| to guard + // against malformed struct trees. + void PopulateImageAltTextForStructElement( + const MarkedContentIdToImageMap& marked_content_id_image_map, + FPDF_STRUCTELEMENT current_element, + std::set<FPDF_STRUCTELEMENT>* visited_elements); static uint32_t CountLinkHighlightOverlaps( const std::vector<Link>& links, const std::vector<Highlight>& highlights); + bool PopulateFormFieldProperties(FPDF_ANNOTATION annot, + FormField* form_field); PDFiumEngine* engine_; ScopedFPDFPage page_; @@ -317,6 +353,7 @@ class PDFiumPage { bool calculated_annotations_ = false; std::vector<Highlight> highlights_; std::vector<TextField> text_fields_; + std::vector<ChoiceField> choice_fields_; bool logged_overlapping_annotations_ = false; bool calculated_page_object_text_run_breaks_ = false; // The set of character indices on which text runs need to be broken for page diff --git a/chromium/pdf/pdfium/pdfium_page_unittest.cc b/chromium/pdf/pdfium/pdfium_page_unittest.cc index 7906515599d..fae64ca8f2e 100644 --- a/chromium/pdf/pdfium/pdfium_page_unittest.cc +++ b/chromium/pdf/pdfium/pdfium_page_unittest.cc @@ -8,6 +8,8 @@ #include <vector> #include "base/check.h" +#include "base/optional.h" +#include "base/strings/string_util.h" #include "base/test/gtest_util.h" #include "pdf/pdfium/pdfium_engine.h" #include "pdf/pdfium/pdfium_test_base.h" @@ -234,6 +236,80 @@ TEST_F(PDFiumPageImageTest, TestImageAltText) { using PDFiumPageTextTest = PDFiumTestBase; +TEST_F(PDFiumPageTextTest, TestTextRunBounds) { + TestClient client; + std::unique_ptr<PDFiumEngine> engine = InitializeEngine( + &client, FILE_PATH_LITERAL("leading_trailing_spaces_per_text_run.pdf")); + ASSERT_TRUE(engine); + + constexpr int kFirstRunStartIndex = 0; + constexpr int kFirstRunEndIndex = 20; + constexpr int kPageIndex = 0; + base::Optional<pp::PDF::PrivateAccessibilityTextRunInfo> text_run_info_1 = + engine->GetTextRunInfo(kPageIndex, kFirstRunStartIndex); + ASSERT_TRUE(text_run_info_1.has_value()); + + const auto& actual_text_run_1 = text_run_info_1.value(); + EXPECT_EQ(21u, actual_text_run_1.len); + + EXPECT_TRUE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kFirstRunStartIndex))); + pp::FloatRect text_run_bounds = actual_text_run_1.bounds; + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kFirstRunStartIndex))); + + // Last non-space character should fall in the bounding box of the text run. + // Text run looks like this: + // " Hello, world! \r\n "<17 characters><first Tj> + // " \r\n "<4 characters><second Tj> + // " "<1 character><third Tj starting spaces> + // Finally generated text run: " Hello, world! \r\n \r\n " + constexpr int kFirstRunLastNonSpaceCharIndex = 13; + EXPECT_FALSE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kFirstRunLastNonSpaceCharIndex))); + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kFirstRunLastNonSpaceCharIndex))); + + EXPECT_TRUE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kFirstRunEndIndex))); + pp::FloatRect end_char_rect = + engine->GetCharBounds(kPageIndex, kFirstRunEndIndex); + EXPECT_FALSE(text_run_bounds.Contains(end_char_rect)); + // Equals to the length of the previous text run. + constexpr int kSecondRunStartIndex = 21; + constexpr int kSecondRunEndIndex = 36; + // Test the properties of second text run. + // Note: The leading spaces in second text run are accounted for in the end + // of first text run. Hence we won't see a space leading the second text run. + base::Optional<pp::PDF::PrivateAccessibilityTextRunInfo> text_run_info_2 = + engine->GetTextRunInfo(kPageIndex, kSecondRunStartIndex); + ASSERT_TRUE(text_run_info_2.has_value()); + + const auto& actual_text_run_2 = text_run_info_2.value(); + EXPECT_EQ(16u, actual_text_run_2.len); + + EXPECT_FALSE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kSecondRunStartIndex))); + text_run_bounds = actual_text_run_2.bounds; + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kSecondRunStartIndex))); + + // Last non-space character should fall in the bounding box of the text run. + // Text run looks like this: + // "Goodbye, world! "<19 characters><first Tj> + // Finally generated text run: "Goodbye, world! " + constexpr int kSecondRunLastNonSpaceCharIndex = 35; + EXPECT_FALSE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kSecondRunLastNonSpaceCharIndex))); + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kSecondRunLastNonSpaceCharIndex))); + + EXPECT_TRUE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kSecondRunEndIndex))); + EXPECT_FALSE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kSecondRunEndIndex))); +} + TEST_F(PDFiumPageTextTest, GetTextRunInfo) { TestClient client; std::unique_ptr<PDFiumEngine> engine = @@ -333,7 +409,7 @@ TEST_F(PDFiumPageTextTest, TestHighlightTextRunInfo) { {7, PP_MakeFloatRectFromXYWH(106.66666f, 198.66667f, 73.333336f, 18.666672f), PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR, kExpectedStyle}, - {2, PP_MakeFloatRectFromXYWH(188.0f, 202.66667f, 9.333333f, 14.666667f), + {2, PP_MakeFloatRectFromXYWH(181.33333f, 192.0f, 16.0f, 25.333344f), PP_PrivateDirection::PP_PRIVATEDIRECTION_NONE, kExpectedStyle}, {2, PP_MakeFloatRectFromXYWH(198.66667f, 202.66667f, 21.333328f, 10.666672f), @@ -437,6 +513,94 @@ TEST_F(PDFiumPageTextFieldTest, TestPopulateTextFields) { } } +using PDFiumPageChoiceFieldTest = PDFiumTestBase; + +TEST_F(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields) { + struct ExpectedChoiceFieldOption { + const char* name; + bool is_selected; + }; + + struct ExpectedChoiceField { + const char* name; + std::vector<struct ExpectedChoiceFieldOption> options; + pp::Rect bounding_rect; + int flags; + }; + + static const ExpectedChoiceField kExpectedChoiceFields[] = { + {"Listbox_SingleSelect", + {{"Foo", false}, {"Bar", false}, {"Qux", false}}, + {138, 296, 135, 41}, + 0}, + {"Combo1", + {{"Apple", false}, {"Banana", true}, {"Cherry", false}}, + {138, 230, 135, 41}, + 131072}, + {"Listbox_ReadOnly", + {{"Dog", false}, {"Elephant", false}, {"Frog", false}}, + {138, 96, 135, 41}, + 1}, + {"Listbox_MultiSelectMultipleIndices", + { + {"Albania", false}, + {"Belgium", true}, + {"Croatia", false}, + {"Denmark", true}, + {"Estonia", false}, + }, + {138, 430, 135, 41}, + 2097152}, + {"Listbox_MultiSelectMultipleValues", + { + {"Alpha", false}, + {"Beta", false}, + {"Gamma", true}, + {"Delta", false}, + {"Epsilon", true}, + }, + {138, 496, 135, 41}, + 2097152}, + {"Listbox_MultiSelectMultipleMismatch", + { + {"Alligator", true}, + {"Bear", false}, + {"Cougar", true}, + {"Deer", false}, + {"Echidna", false}, + }, + {138, 563, 135, 41}, + 2097152}}; + + TestClient client; + std::unique_ptr<PDFiumEngine> engine = + InitializeEngine(&client, FILE_PATH_LITERAL("form_choice_fields.pdf")); + ASSERT_TRUE(engine); + ASSERT_EQ(1, engine->GetNumberOfPages()); + + PDFiumPage* page = GetPDFiumPageForTest(engine.get(), 0); + ASSERT_TRUE(page); + page->PopulateAnnotations(); + size_t choice_fields_count = page->choice_fields_.size(); + ASSERT_EQ(base::size(kExpectedChoiceFields), choice_fields_count); + + for (size_t i = 0; i < choice_fields_count; ++i) { + EXPECT_EQ(kExpectedChoiceFields[i].name, page->choice_fields_[i].name); + size_t choice_field_options_count = page->choice_fields_[i].options.size(); + ASSERT_EQ(base::size(kExpectedChoiceFields[i].options), + choice_field_options_count); + for (size_t j = 0; j < choice_field_options_count; ++j) { + EXPECT_EQ(kExpectedChoiceFields[i].options[j].name, + page->choice_fields_[i].options[j].name); + EXPECT_EQ(kExpectedChoiceFields[i].options[j].is_selected, + page->choice_fields_[i].options[j].is_selected); + } + CompareRect(kExpectedChoiceFields[i].bounding_rect, + page->choice_fields_[i].bounding_rect); + EXPECT_EQ(kExpectedChoiceFields[i].flags, page->choice_fields_[i].flags); + } +} + using PDFiumPageOverlappingTest = PDFiumTestBase; // The following scenarios are covered across both test cases: diff --git a/chromium/pdf/pdfium/pdfium_permissions.cc b/chromium/pdf/pdfium/pdfium_permissions.cc index db3c98032b7..b24c6f42a58 100644 --- a/chromium/pdf/pdfium/pdfium_permissions.cc +++ b/chromium/pdf/pdfium/pdfium_permissions.cc @@ -4,6 +4,8 @@ #include "pdf/pdfium/pdfium_permissions.h" +#include "base/notreached.h" + namespace chrome_pdf { // static diff --git a/chromium/pdf/pdfium/pdfium_print.cc b/chromium/pdf/pdfium/pdfium_print.cc index 5cb79dce728..be5dd656dda 100644 --- a/chromium/pdf/pdfium/pdfium_print.cc +++ b/chromium/pdf/pdfium/pdfium_print.cc @@ -9,11 +9,11 @@ #include <utility> #include "base/strings/string_number_conversions.h" -#include "pdf/geometry_conversions.h" #include "pdf/pdf_transform.h" #include "pdf/pdfium/pdfium_engine.h" #include "pdf/pdfium/pdfium_mem_buffer_file_read.h" #include "pdf/pdfium/pdfium_mem_buffer_file_write.h" +#include "pdf/ppapi_migration/geometry_conversions.h" #include "ppapi/c/dev/ppp_printing_dev.h" #include "ppapi/c/private/ppp_pdf.h" #include "printing/nup_parameters.h" diff --git a/chromium/pdf/pdfium/pdfium_test_base.cc b/chromium/pdf/pdfium/pdfium_test_base.cc index 95c48c11c9b..b220fb92535 100644 --- a/chromium/pdf/pdfium/pdfium_test_base.cc +++ b/chromium/pdf/pdfium/pdfium_test_base.cc @@ -103,10 +103,11 @@ PDFiumTestBase::InitializeEngineWithoutLoading( void PDFiumTestBase::InitializePDFium() { FPDF_LIBRARY_CONFIG config; - config.version = 2; + config.version = 3; config.m_pUserFontPaths = nullptr; config.m_pIsolate = nullptr; config.m_v8EmbedderSlot = 0; + config.m_pPlatform = nullptr; FPDF_InitLibraryWithConfig(&config); } |