Avi Drissman | db497b3 | 2022-09-15 19:47:28 | [diff] [blame] | 1 | // Copyright 2010 The Chromium Authors |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef PDF_PDFIUM_PDFIUM_PAGE_H_ |
| 6 | #define PDF_PDFIUM_PDFIUM_PAGE_H_ |
| 7 | |
Pratish Kumar | 8f9d6d6a | 2019-08-29 03:26:29 | [diff] [blame] | 8 | #include <map> |
| 9 | #include <set> |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 10 | #include <string> |
| 11 | #include <vector> |
| 12 | |
Arthur Sonzogni | 59ac822 | 2023-11-10 09:46:54 | [diff] [blame] | 13 | #include <optional> |
Avi Drissman | 821ca309 | 2023-01-11 22:42:15 | [diff] [blame] | 14 | #include "base/functional/callback.h" |
| 15 | #include "base/functional/callback_forward.h" |
Virender Singh | 5bee2104 | 2019-08-13 07:10:01 | [diff] [blame] | 16 | #include "base/gtest_prod_util.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 17 | #include "base/memory/raw_ptr.h" |
K Moon | 9a62bf4 | 2019-08-07 20:05:36 | [diff] [blame] | 18 | #include "pdf/page_orientation.h" |
Henrique Nakashima | fd7edf2a | 2017-11-29 22:03:49 | [diff] [blame] | 19 | #include "pdf/pdf_engine.h" |
Tom Sepez | b0048f1 | 2018-05-14 22:56:47 | [diff] [blame] | 20 | #include "third_party/pdfium/public/cpp/fpdf_scopers.h" |
tsepez | 35024356 | 2015-05-12 01:08:45 | [diff] [blame] | 21 | #include "third_party/pdfium/public/fpdf_doc.h" |
| 22 | #include "third_party/pdfium/public/fpdf_formfill.h" |
| 23 | #include "third_party/pdfium/public/fpdf_text.h" |
Nektarios Paisios | 8742971 | 2022-03-15 06:10:53 | [diff] [blame] | 24 | #include "third_party/skia/include/core/SkBitmap.h" |
Henrique Nakashima | 1a49dbc | 2018-02-08 21:00:33 | [diff] [blame] | 25 | #include "ui/gfx/geometry/point_f.h" |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 26 | #include "ui/gfx/geometry/rect.h" |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 27 | |
Ankit Kumar 🌪️ | aecc9f9 | 2020-08-18 19:11:22 | [diff] [blame] | 28 | namespace gfx { |
| 29 | class Point; |
Ankit Kumar 🌪️ | 1f081d80 | 2020-09-18 19:29:01 | [diff] [blame] | 30 | class RectF; |
Ankit Kumar 🌪️ | aecc9f9 | 2020-08-18 19:11:22 | [diff] [blame] | 31 | } // namespace gfx |
| 32 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 33 | namespace chrome_pdf { |
| 34 | |
| 35 | class PDFiumEngine; |
Daniel Hosseinian | 4222f88d | 2020-09-10 23:12:38 | [diff] [blame] | 36 | class Thumbnail; |
Ankit Kumar 🌪️ | d6469b10 | 2021-02-12 16:54:06 | [diff] [blame] | 37 | struct AccessibilityHighlightInfo; |
Ankit Kumar 🌪️ | 76b1b72 | 2021-02-12 11:32:59 | [diff] [blame] | 38 | struct AccessibilityImageInfo; |
Lei Zhang | 7399c25 | 2021-06-23 17:19:02 | [diff] [blame] | 39 | struct AccessibilityLinkInfo; |
Ankit Kumar 🌪️ | 371d77b | 2021-02-12 17:00:40 | [diff] [blame] | 40 | struct AccessibilityTextFieldInfo; |
Ankit Kumar 🌪️ | c342152 | 2020-12-03 16:27:34 | [diff] [blame] | 41 | struct AccessibilityTextRunInfo; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 42 | |
| 43 | // Wrapper around a page from the document. |
| 44 | class PDFiumPage { |
| 45 | public: |
Lei Zhang | e250e8eb | 2023-09-19 20:13:28 | [diff] [blame] | 46 | class ScopedUnloadPreventer { |
| 47 | public: |
| 48 | explicit ScopedUnloadPreventer(PDFiumPage* page); |
| 49 | ScopedUnloadPreventer(const ScopedUnloadPreventer& that); |
| 50 | ScopedUnloadPreventer& operator=(const ScopedUnloadPreventer& that); |
| 51 | ~ScopedUnloadPreventer(); |
| 52 | |
| 53 | private: |
| 54 | raw_ptr<PDFiumPage> page_; |
| 55 | }; |
| 56 | |
K Moon | b15a6a0 | 2019-08-30 01:18:40 | [diff] [blame] | 57 | PDFiumPage(PDFiumEngine* engine, int i); |
Hui Yingst | eb451e4 | 2020-08-11 18:41:24 | [diff] [blame] | 58 | PDFiumPage(const PDFiumPage&) = delete; |
| 59 | PDFiumPage& operator=(const PDFiumPage&) = delete; |
Tom Sepez | b0048f1 | 2018-05-14 22:56:47 | [diff] [blame] | 60 | PDFiumPage(PDFiumPage&& that); |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 61 | ~PDFiumPage(); |
thestig | ccb5fc8f | 2016-01-05 05:32:51 | [diff] [blame] | 62 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 63 | // Unloads the PDFium data for this page from memory. |
| 64 | void Unload(); |
| 65 | // Gets the FPDF_PAGE for this page, loading and parsing it if necessary. |
| 66 | FPDF_PAGE GetPage(); |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 67 | |
| 68 | // Returns FPDF_TEXTPAGE for the page, loading and parsing it if necessary. |
| 69 | FPDF_TEXTPAGE GetTextPage(); |
| 70 | |
Lei Zhang | f2aefe2 | 2019-08-27 22:08:02 | [diff] [blame] | 71 | // See definition of PDFEngine::GetTextRunInfo(). |
Arthur Sonzogni | 59ac822 | 2023-11-10 09:46:54 | [diff] [blame] | 72 | std::optional<AccessibilityTextRunInfo> GetTextRunInfo(int start_char_index); |
Lei Zhang | ae5fbcd | 2021-11-19 02:15:55 | [diff] [blame] | 73 | |
dmazzoni | c3547a3 | 2016-06-02 05:47:15 | [diff] [blame] | 74 | // Get a unicode character from the page. |
| 75 | uint32_t GetCharUnicode(int char_index); |
Lei Zhang | ae5fbcd | 2021-11-19 02:15:55 | [diff] [blame] | 76 | |
dmazzoni | d48d932 | 2016-06-13 19:37:42 | [diff] [blame] | 77 | // Get the bounds of a character in page pixels. |
Ankit Kumar 🌪️ | 1f081d80 | 2020-09-18 19:29:01 | [diff] [blame] | 78 | gfx::RectF GetCharBounds(int char_index); |
Lei Zhang | ae5fbcd | 2021-11-19 02:15:55 | [diff] [blame] | 79 | |
| 80 | // Get the bounds of the page with the crop box applied, in page pixels. |
| 81 | gfx::RectF GetCroppedRect(); |
| 82 | |
Andy Phan | fef93ce | 2023-04-06 00:18:25 | [diff] [blame] | 83 | // Get the bounding box of the page in page pixels. The bounding box is the |
| 84 | // largest rectangle containing all visible content in the effective crop box. |
| 85 | // If the bounding box can't be calculated, returns the effective crop box. |
| 86 | // The resulting bounding box is relative to the effective crop box. |
| 87 | gfx::RectF GetBoundingBox(); |
| 88 | |
Lei Zhang | cb83ff1 | 2022-07-11 22:12:13 | [diff] [blame] | 89 | // Returns if the character at `char_index` is within `page_bounds`. |
| 90 | bool IsCharInPageBounds(int char_index, const gfx::RectF& page_bounds); |
| 91 | |
Lei Zhang | 99ce9195 | 2019-11-18 20:57:56 | [diff] [blame] | 92 | // For all the links on the page, get their urls, underlying text ranges and |
| 93 | // bounding boxes. |
Ankit Kumar 🌪️ | ab90ffc | 2021-02-12 10:41:33 | [diff] [blame] | 94 | std::vector<AccessibilityLinkInfo> GetLinkInfo( |
| 95 | const std::vector<AccessibilityTextRunInfo>& text_runs); |
Nektarios Paisios | 8742971 | 2022-03-15 06:10:53 | [diff] [blame] | 96 | // For all the images on the page, get their alt texts and bounding boxes. If |
| 97 | // the alt text is empty or unavailable, and if the user has requested that |
| 98 | // the OCR service tag the PDF so that it is made accessible, transfer the raw |
| 99 | // image pixels in the `image_data` field. Otherwise do not populate the |
| 100 | // `image_data` field. |
Ankit Kumar 🌪️ | 76b1b72 | 2021-02-12 11:32:59 | [diff] [blame] | 101 | std::vector<AccessibilityImageInfo> GetImageInfo(uint32_t text_run_count); |
Lei Zhang | ae5fbcd | 2021-11-19 02:15:55 | [diff] [blame] | 102 | |
Kyungjun Lee | 8209e2c | 2023-08-11 23:06:47 | [diff] [blame] | 103 | // Returns the image as a 32-bit bitmap format for OCR. |
| 104 | SkBitmap GetImageForOcr(int page_object_index); |
| 105 | |
Kalpak Tapas | d99d676 | 2019-12-03 09:12:43 | [diff] [blame] | 106 | // For all the highlights on the page, get their underlying text ranges and |
| 107 | // bounding boxes. |
Ankit Kumar 🌪️ | d6469b10 | 2021-02-12 16:54:06 | [diff] [blame] | 108 | std::vector<AccessibilityHighlightInfo> GetHighlightInfo( |
| 109 | const std::vector<AccessibilityTextRunInfo>& text_runs); |
Lei Zhang | ae5fbcd | 2021-11-19 02:15:55 | [diff] [blame] | 110 | |
Mansi Awasthi | 3297cfb | 2020-03-17 19:19:32 | [diff] [blame] | 111 | // For all the text fields on the page, get their properties like name, |
| 112 | // value, bounding boxes, etc. |
Ankit Kumar 🌪️ | 371d77b | 2021-02-12 17:00:40 | [diff] [blame] | 113 | std::vector<AccessibilityTextFieldInfo> GetTextFieldInfo( |
| 114 | uint32_t text_run_count); |
dmazzoni | c3547a3 | 2016-06-02 05:47:15 | [diff] [blame] | 115 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 116 | enum Area { |
| 117 | NONSELECTABLE_AREA, |
drgage | c32fae26 | 2017-06-24 00:17:49 | [diff] [blame] | 118 | TEXT_AREA, // Area contains regular, selectable text not |
| 119 | // within form fields. |
| 120 | WEBLINK_AREA, // Area is a hyperlink. |
| 121 | DOCLINK_AREA, // Area is a link to a different part of the same |
| 122 | // document. |
| 123 | FORM_TEXT_AREA, // Area is a form text field or form combobox text |
| 124 | // field. |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 125 | }; |
| 126 | |
| 127 | struct LinkTarget { |
Henrique Nakashima | 9d9e063 | 2017-10-06 21:38:18 | [diff] [blame] | 128 | LinkTarget(); |
| 129 | LinkTarget(const LinkTarget& other); |
| 130 | ~LinkTarget(); |
| 131 | |
| 132 | // Valid for WEBLINK_AREA only. |
| 133 | std::string url; |
| 134 | |
| 135 | // Valid for DOCLINK_AREA only. |
| 136 | int page; |
Ankit Kumar 🌪️ | 51cc8c49 | 2019-09-24 19:10:39 | [diff] [blame] | 137 | // Valid for DOCLINK_AREA only. From the top-left of the page. |
Arthur Sonzogni | 59ac822 | 2023-11-10 09:46:54 | [diff] [blame] | 138 | std::optional<float> x_in_pixels; |
| 139 | std::optional<float> y_in_pixels; |
Ankit Kumar 🌪️ | 495acdd | 2019-10-04 03:44:43 | [diff] [blame] | 140 | // Valid for DOCLINK_AREA only. |
Arthur Sonzogni | 59ac822 | 2023-11-10 09:46:54 | [diff] [blame] | 141 | std::optional<float> zoom; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 142 | }; |
| 143 | |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 144 | // Given a `link_index`, returns the type of underlying area and the link |
| 145 | // target. `target` must be valid. Returns NONSELECTABLE_AREA if |
| 146 | // `link_index` is invalid. |
Mansi Awasthi | 6f4aa4a | 2019-09-07 05:34:07 | [diff] [blame] | 147 | Area GetLinkTargetAtIndex(int link_index, LinkTarget* target); |
| 148 | |
Badhri Ravikumar | 670c436 | 2020-05-14 19:29:55 | [diff] [blame] | 149 | // Returns link type and fills target associated with a link. Returns |
| 150 | // NONSELECTABLE_AREA if link detection failed. |
| 151 | Area GetLinkTarget(FPDF_LINK link, LinkTarget* target); |
| 152 | |
Hui Yingst | 5c0a0585 | 2021-01-19 04:23:39 | [diff] [blame] | 153 | // Fills the output params with the in-page coordinates and the zoom value of |
| 154 | // the destination. |
Ankit Kumar 🌪️ | 495acdd | 2019-10-04 03:44:43 | [diff] [blame] | 155 | void GetPageDestinationTarget(FPDF_DEST destination, |
Arthur Sonzogni | 59ac822 | 2023-11-10 09:46:54 | [diff] [blame] | 156 | std::optional<float>* dest_x, |
| 157 | std::optional<float>* dest_y, |
| 158 | std::optional<float>* zoom_value); |
Henrique Nakashima | 97f071c | 2018-01-11 19:56:02 | [diff] [blame] | 159 | |
Hui Yingst | a7fe60e4 | 2021-01-22 19:36:19 | [diff] [blame] | 160 | // For a named destination with "XYZ" view fit type, pre-processes the in-page |
Lei Zhang | 2065dd0 | 2021-03-24 19:59:28 | [diff] [blame] | 161 | // x/y coordinate in case it's out of the range of the page dimension. Then |
| 162 | // transform it to a screen coordinate. |
| 163 | float PreProcessAndTransformInPageCoordX(float x); |
| 164 | float PreProcessAndTransformInPageCoordY(float y); |
Hui Yingst | a7fe60e4 | 2021-01-22 19:36:19 | [diff] [blame] | 165 | |
Henrique Nakashima | 97f071c | 2018-01-11 19:56:02 | [diff] [blame] | 166 | // Transforms an (x, y) position in page coordinates to screen coordinates. |
Henrique Nakashima | 1a49dbc | 2018-02-08 21:00:33 | [diff] [blame] | 167 | gfx::PointF TransformPageToScreenXY(const gfx::PointF& xy); |
Henrique Nakashima | 9d9e063 | 2017-10-06 21:38:18 | [diff] [blame] | 168 | |
Hui Yingst | 5c0a0585 | 2021-01-19 04:23:39 | [diff] [blame] | 169 | // Transforms an in-page x coordinate to its value in screen coordinates. |
| 170 | float TransformPageToScreenX(float x); |
| 171 | |
| 172 | // Transforms an in-page y coordinate to its value in screen coordinates. |
| 173 | float TransformPageToScreenY(float y); |
| 174 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 175 | // Given a point in the document that's in this page, returns its character |
| 176 | // index if it's near a character, and also the type of text. |
| 177 | // Target is optional. It will be filled in for WEBLINK_AREA or |
| 178 | // DOCLINK_AREA only. |
Ankit Kumar 🌪️ | aecc9f9 | 2020-08-18 19:11:22 | [diff] [blame] | 179 | Area GetCharIndex(const gfx::Point& point, |
K Moon | 9a62bf4 | 2019-08-07 20:05:36 | [diff] [blame] | 180 | PageOrientation orientation, |
thestig | 98913ba | 2017-04-21 19:03:25 | [diff] [blame] | 181 | int* char_index, |
| 182 | int* form_type, |
| 183 | LinkTarget* target); |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 184 | |
drgage | c32fae26 | 2017-06-24 00:17:49 | [diff] [blame] | 185 | // Converts a form type to its corresponding Area. |
| 186 | static Area FormTypeToArea(int form_type); |
| 187 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 188 | // Gets the character at the given index. |
Jan Wilken Dörrie | a17b04b | 2021-03-09 20:51:01 | [diff] [blame] | 189 | char16_t GetCharAtIndex(int index); |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 190 | |
| 191 | // Gets the number of characters in the page. |
| 192 | int GetCharCount(); |
| 193 | |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 194 | // Returns true if the given `char_index` lies within the character range |
Mansi Awasthi | abbdd54 | 2019-12-21 11:17:54 | [diff] [blame] | 195 | // of the page. |
| 196 | bool IsCharIndexInBounds(int char_index); |
| 197 | |
Pratish Kumar | e7032973 | 2019-07-22 18:07:08 | [diff] [blame] | 198 | // Given a rectangle in page coordinates, computes the range of continuous |
| 199 | // characters which lie inside that rectangle. Returns false without |
| 200 | // modifying the out parameters if no character lies inside the rectangle. |
Ankit Kumar 🌪️ | 1f081d80 | 2020-09-18 19:29:01 | [diff] [blame] | 201 | bool GetUnderlyingTextRangeForRect(const gfx::RectF& rect, |
Pratish Kumar | e7032973 | 2019-07-22 18:07:08 | [diff] [blame] | 202 | int* start_index, |
Lei Zhang | cf99301 | 2019-08-22 18:24:23 | [diff] [blame] | 203 | int* char_len); |
Pratish Kumar | e7032973 | 2019-07-22 18:07:08 | [diff] [blame] | 204 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 205 | // Converts from page coordinates to screen coordinates. |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 206 | gfx::Rect PageToScreen(const gfx::Point& page_point, |
| 207 | double zoom, |
| 208 | double left, |
| 209 | double top, |
| 210 | double right, |
| 211 | double bottom, |
| 212 | PageOrientation orientation) const; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 213 | |
Daniel Hosseinian | 6ed110f | 2020-09-25 21:43:43 | [diff] [blame] | 214 | // Sets the callbacks for sending the thumbnail. |
| 215 | void RequestThumbnail(float device_pixel_ratio, |
| 216 | SendThumbnailCallback send_callback); |
| 217 | |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 218 | // Generates a page thumbnail accommodating a specific `device_pixel_ratio`. |
Daniel Hosseinian | 4222f88d | 2020-09-10 23:12:38 | [diff] [blame] | 219 | Thumbnail GenerateThumbnail(float device_pixel_ratio); |
| 220 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 221 | int index() const { return index_; } |
K Moon | b15a6a0 | 2019-08-30 01:18:40 | [diff] [blame] | 222 | |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 223 | const gfx::Rect& rect() const { return rect_; } |
| 224 | void set_rect(const gfx::Rect& r) { rect_ = r; } |
K Moon | b15a6a0 | 2019-08-30 01:18:40 | [diff] [blame] | 225 | |
| 226 | // Availability is a one-way transition: A page can become available, but it |
| 227 | // cannot become unavailable (unless deleted entirely). |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 228 | bool available() const { return available_; } |
Daniel Hosseinian | 6ed110f | 2020-09-25 21:43:43 | [diff] [blame] | 229 | void MarkAvailable(); |
K Moon | b15a6a0 | 2019-08-30 01:18:40 | [diff] [blame] | 230 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 231 | void set_calculated_links(bool calculated_links) { |
thestig | 98913ba | 2017-04-21 19:03:25 | [diff] [blame] | 232 | calculated_links_ = calculated_links; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 233 | } |
| 234 | |
Tom Sepez | b0048f1 | 2018-05-14 22:56:47 | [diff] [blame] | 235 | FPDF_PAGE page() const { return page_.get(); } |
| 236 | FPDF_TEXTPAGE text_page() const { return text_page_.get(); } |
| 237 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 238 | private: |
Virender Singh | 5bee2104 | 2019-08-13 07:10:01 | [diff] [blame] | 239 | friend class PDFiumPageLinkTest; |
Pratish Kumar | 7e49da29 | 2019-08-21 19:10:30 | [diff] [blame] | 240 | friend class PDFiumTestBase; |
| 241 | |
Lei Zhang | 72b2a8aa2 | 2021-11-13 00:58:37 | [diff] [blame] | 242 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageButtonTest, PopulateButtons); |
| 243 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageChoiceFieldTest, PopulateChoiceFields); |
| 244 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageHighlightTest, PopulateHighlights); |
| 245 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageImageTest, CalculateImages); |
| 246 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageImageTest, ImageAltText); |
Nektarios Paisios | 8742971 | 2022-03-15 06:10:53 | [diff] [blame] | 247 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageImageDataTest, ImageData); |
Kyungjun Lee | 8209e2c | 2023-08-11 23:06:47 | [diff] [blame] | 248 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageImageDataTest, ImageDataForNonImage); |
Ramin Halavati | 6e0174a | 2023-08-07 06:05:13 | [diff] [blame] | 249 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageImageDataTest, RotatedPageImageData); |
Lei Zhang | 72b2a8aa2 | 2021-11-13 00:58:37 | [diff] [blame] | 250 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, AnnotLinkGeneration); |
| 251 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, GetLinkTarget); |
Lei Zhang | 96e99c3 | 2022-12-09 05:29:46 | [diff] [blame] | 252 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, GetUTF8LinkTarget); |
Lei Zhang | 72b2a8aa2 | 2021-11-13 00:58:37 | [diff] [blame] | 253 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, LinkGeneration); |
Ankit Kumar | 9eafc3a | 2020-04-24 22:23:06 | [diff] [blame] | 254 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountCompleteOverlaps); |
Hui Yingst | b514706 | 2021-07-23 00:18:29 | [diff] [blame] | 255 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountPartialOverlaps); |
Lei Zhang | 72b2a8aa2 | 2021-11-13 00:58:37 | [diff] [blame] | 256 | FRIEND_TEST_ALL_PREFIXES(PDFiumPageTextFieldTest, PopulateTextFields); |
Virender Singh | 5bee2104 | 2019-08-13 07:10:01 | [diff] [blame] | 257 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 258 | struct Link { |
| 259 | Link(); |
thestig | fa6edbc7 | 2016-08-23 08:07:00 | [diff] [blame] | 260 | Link(const Link& that); |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 261 | ~Link(); |
| 262 | |
Virender Singh | 5bee2104 | 2019-08-13 07:10:01 | [diff] [blame] | 263 | // Represents start index of underlying text range. Should be -1 if the link |
| 264 | // is not over text. |
| 265 | int32_t start_char_index = -1; |
| 266 | // Represents the number of characters that the link overlaps with. |
| 267 | int32_t char_count = 0; |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 268 | std::vector<gfx::Rect> bounding_rects; |
Mansi Awasthi | 9f374ba1 | 2019-09-10 18:59:09 | [diff] [blame] | 269 | LinkTarget target; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 270 | }; |
| 271 | |
Pratish Kumar | 7e49da29 | 2019-08-21 19:10:30 | [diff] [blame] | 272 | // Represents an Image inside the page. |
| 273 | struct Image { |
| 274 | Image(); |
| 275 | Image(const Image& other); |
| 276 | ~Image(); |
| 277 | |
Kyungjun Lee | 8209e2c | 2023-08-11 23:06:47 | [diff] [blame] | 278 | // Index of the object in its page. |
Nektarios Paisios | 8742971 | 2022-03-15 06:10:53 | [diff] [blame] | 279 | int page_object_index; |
Kyungjun Lee | 8209e2c | 2023-08-11 23:06:47 | [diff] [blame] | 280 | |
Nektarios Paisios | 8742971 | 2022-03-15 06:10:53 | [diff] [blame] | 281 | // Alt text is available only for PDFs that are tagged for accessibility. |
Pratish Kumar | 8f9d6d6a | 2019-08-29 03:26:29 | [diff] [blame] | 282 | std::string alt_text; |
Nektarios Paisios | 8742971 | 2022-03-15 06:10:53 | [diff] [blame] | 283 | gfx::Rect bounding_rect; |
Pratish Kumar | 7e49da29 | 2019-08-21 19:10:30 | [diff] [blame] | 284 | }; |
| 285 | |
Kalpak Tapas | 6eb2e8d | 2019-11-22 10:49:59 | [diff] [blame] | 286 | // Represents a highlight within the page. |
| 287 | struct Highlight { |
| 288 | Highlight(); |
| 289 | Highlight(const Highlight& other); |
| 290 | ~Highlight(); |
| 291 | |
| 292 | // Start index of underlying text range. -1 indicates invalid value. |
| 293 | int32_t start_char_index = -1; |
| 294 | // Number of characters encompassed by this highlight. |
| 295 | int32_t char_count = 0; |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 296 | gfx::Rect bounding_rect; |
Ankit Kumar 🌪️ | 221befc | 2020-01-30 07:13:21 | [diff] [blame] | 297 | |
| 298 | // Color of the highlight in ARGB. Alpha is stored in the first 8 MSBs. RGB |
| 299 | // follows after it with each using 8 bytes. |
| 300 | uint32_t color; |
Ankit Kumar | e390bd6 | 2020-05-22 09:13:33 | [diff] [blame] | 301 | |
| 302 | // Text of the popup note associated with highlight. |
| 303 | std::string note_text; |
Kalpak Tapas | 6eb2e8d | 2019-11-22 10:49:59 | [diff] [blame] | 304 | }; |
| 305 | |
Mansi Awasthi | cfc83f8 | 2020-06-22 17:37:52 | [diff] [blame] | 306 | // Represents a form field within the page. |
| 307 | struct FormField { |
| 308 | FormField(); |
| 309 | FormField(const FormField& other); |
| 310 | ~FormField(); |
| 311 | |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 312 | gfx::Rect bounding_rect; |
Mansi Awasthi | cfc83f8 | 2020-06-22 17:37:52 | [diff] [blame] | 313 | // Represents the name of form field as defined in the field dictionary. |
| 314 | std::string name; |
| 315 | // Represents the flags of form field as defined in the field dictionary. |
| 316 | int flags; |
| 317 | }; |
| 318 | |
Mansi Awasthi | d6afb93 | 2020-02-27 13:02:39 | [diff] [blame] | 319 | // Represents a text field within the page. |
Mansi Awasthi | cfc83f8 | 2020-06-22 17:37:52 | [diff] [blame] | 320 | struct TextField : FormField { |
Mansi Awasthi | d6afb93 | 2020-02-27 13:02:39 | [diff] [blame] | 321 | TextField(); |
| 322 | TextField(const TextField& other); |
| 323 | ~TextField(); |
| 324 | |
Mansi Awasthi | d6afb93 | 2020-02-27 13:02:39 | [diff] [blame] | 325 | std::string value; |
Mansi Awasthi | d6afb93 | 2020-02-27 13:02:39 | [diff] [blame] | 326 | }; |
| 327 | |
Mansi Awasthi | b62d1d1 | 2020-06-23 01:34:34 | [diff] [blame] | 328 | // Represents a choice field option. |
| 329 | struct ChoiceFieldOption { |
| 330 | ChoiceFieldOption(); |
| 331 | ChoiceFieldOption(const ChoiceFieldOption& other); |
| 332 | ~ChoiceFieldOption(); |
| 333 | |
| 334 | std::string name; |
| 335 | bool is_selected; |
| 336 | }; |
| 337 | |
| 338 | // Represents a choice field within the page. |
| 339 | struct ChoiceField : FormField { |
| 340 | ChoiceField(); |
| 341 | ChoiceField(const ChoiceField& other); |
| 342 | ~ChoiceField(); |
| 343 | |
| 344 | std::vector<ChoiceFieldOption> options; |
| 345 | }; |
| 346 | |
Mansi Awasthi | 98c39cd | 2020-07-01 15:15:36 | [diff] [blame] | 347 | // Represents a button within the page. |
| 348 | struct Button : FormField { |
| 349 | Button(); |
| 350 | Button(const Button& other); |
| 351 | ~Button(); |
| 352 | |
| 353 | std::string value; |
| 354 | // A button can be of type radio, checkbox or push button. |
| 355 | int type; |
| 356 | // Represents if the radio button or checkbox is checked. |
| 357 | bool is_checked = false; |
| 358 | // Represents count of controls in the control group. A group of |
| 359 | // interactive form annotations is collectively called a form control |
| 360 | // group. Here an interactive form annotation should be either a radio |
| 361 | // button or a checkbox. |
| 362 | uint32_t control_count = 0; |
| 363 | // Represents index of the control in the control group. A group of |
| 364 | // interactive form annotations is collectively called a form control |
| 365 | // group. Here an interactive form annotation should be either a radio |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 366 | // button or a checkbox. Value of `control_index` is -1 for push button. |
Mansi Awasthi | 98c39cd | 2020-07-01 15:15:36 | [diff] [blame] | 367 | int control_index = -1; |
| 368 | }; |
| 369 | |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 370 | // Returns a link index if the given character index is over a link, or -1 |
| 371 | // otherwise. |
| 372 | int GetLink(int char_index, LinkTarget* target); |
| 373 | // Calculate the locations of any links on the page. |
| 374 | void CalculateLinks(); |
| 375 | // Populates weblinks on the page. |
| 376 | void PopulateWebLinks(); |
| 377 | // Populates annotation links on the page. |
| 378 | void PopulateAnnotationLinks(); |
| 379 | // Calculate the locations of images on the page. |
| 380 | void CalculateImages(); |
| 381 | // Populate annotations like highlight and text field on the page. |
| 382 | void PopulateAnnotations(); |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 383 | // Populate `highlights_` with `annot`. |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 384 | void PopulateHighlight(FPDF_ANNOTATION annot); |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 385 | // Populate `text_fields_` with `annot`. |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 386 | void PopulateTextField(FPDF_ANNOTATION annot); |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 387 | // Populate `choice_fields_` with `annot`. |
Mansi Awasthi | b62d1d1 | 2020-06-23 01:34:34 | [diff] [blame] | 388 | void PopulateChoiceField(FPDF_ANNOTATION annot); |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 389 | // Populate `buttons_` with `annot`. |
Mansi Awasthi | 98c39cd | 2020-07-01 15:15:36 | [diff] [blame] | 390 | void PopulateButton(FPDF_ANNOTATION annot); |
| 391 | // Populate form fields like text field, choice field and button on the page. |
Mansi Awasthi | cfc83f8 | 2020-06-22 17:37:52 | [diff] [blame] | 392 | void PopulateFormField(FPDF_ANNOTATION annot); |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 393 | // Returns link type and fills target associated with a destination. Returns |
| 394 | // NONSELECTABLE_AREA if detection failed. |
| 395 | Area GetDestinationTarget(FPDF_DEST destination, LinkTarget* target); |
| 396 | // Returns link type and fills target associated with a URI action. Returns |
| 397 | // NONSELECTABLE_AREA if detection failed. |
| 398 | Area GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const; |
| 399 | // Calculates the set of character indices on which text runs need to be |
| 400 | // broken for page objects such as links and images. |
| 401 | void CalculatePageObjectTextRunBreaks(); |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 402 | |
| 403 | // Key : Marked content id for the image element as specified in the |
| 404 | // struct tree. |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 405 | // Value : Index of image in the `images_` vector. |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 406 | using MarkedContentIdToImageMap = std::map<int, size_t>; |
| 407 | // Traverses the entire struct tree of the page recursively and extracts the |
| 408 | // alt text from struct tree elements corresponding to the marked content IDs |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 409 | // present in `marked_content_id_image_map`. |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 410 | void PopulateImageAltText( |
| 411 | const MarkedContentIdToImageMap& marked_content_id_image_map); |
| 412 | // Traverses a struct element and its sub-tree recursively and extracts the |
| 413 | // alt text from struct elements corresponding to the marked content IDs |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 414 | // present in `marked_content_id_image_map`. Uses `visited_elements` to guard |
Mansi Awasthi | 0850d41 | 2020-06-22 16:59:43 | [diff] [blame] | 415 | // against malformed struct trees. |
| 416 | void PopulateImageAltTextForStructElement( |
| 417 | const MarkedContentIdToImageMap& marked_content_id_image_map, |
| 418 | FPDF_STRUCTELEMENT current_element, |
| 419 | std::set<FPDF_STRUCTELEMENT>* visited_elements); |
Mansi Awasthi | cfc83f8 | 2020-06-22 17:37:52 | [diff] [blame] | 420 | bool PopulateFormFieldProperties(FPDF_ANNOTATION annot, |
| 421 | FormField* form_field); |
Daniel Hosseinian | e257d96 | 2021-04-23 21:18:35 | [diff] [blame] | 422 | // Generates and sends the thumbnail using `send_callback`. |
Daniel Hosseinian | 6ed110f | 2020-09-25 21:43:43 | [diff] [blame] | 423 | void GenerateAndSendThumbnail(float device_pixel_ratio, |
| 424 | SendThumbnailCallback send_callback); |
Ankit Kumar | 9eafc3a | 2020-04-24 22:23:06 | [diff] [blame] | 425 | |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 426 | raw_ptr<PDFiumEngine> engine_; |
Tom Sepez | b0048f1 | 2018-05-14 22:56:47 | [diff] [blame] | 427 | ScopedFPDFPage page_; |
| 428 | ScopedFPDFTextPage text_page_; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 429 | int index_; |
Artem Strygin | fd53f2f | 2018-07-13 13:21:05 | [diff] [blame] | 430 | int preventing_unload_count_ = 0; |
Ankit Kumar 🌪️ | 8cd111c | 2020-08-22 00:02:46 | [diff] [blame] | 431 | gfx::Rect rect_; |
Tom Sepez | b0048f1 | 2018-05-14 22:56:47 | [diff] [blame] | 432 | bool calculated_links_ = false; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 433 | std::vector<Link> links_; |
Pratish Kumar | 7e49da29 | 2019-08-21 19:10:30 | [diff] [blame] | 434 | bool calculated_images_ = false; |
| 435 | std::vector<Image> images_; |
Mansi Awasthi | d6afb93 | 2020-02-27 13:02:39 | [diff] [blame] | 436 | bool calculated_annotations_ = false; |
Kalpak Tapas | 6eb2e8d | 2019-11-22 10:49:59 | [diff] [blame] | 437 | std::vector<Highlight> highlights_; |
Mansi Awasthi | d6afb93 | 2020-02-27 13:02:39 | [diff] [blame] | 438 | std::vector<TextField> text_fields_; |
Mansi Awasthi | b62d1d1 | 2020-06-23 01:34:34 | [diff] [blame] | 439 | std::vector<ChoiceField> choice_fields_; |
Mansi Awasthi | 98c39cd | 2020-07-01 15:15:36 | [diff] [blame] | 440 | std::vector<Button> buttons_; |
Pratish Kumar | 860e883 | 2019-08-29 00:13:56 | [diff] [blame] | 441 | bool calculated_page_object_text_run_breaks_ = false; |
| 442 | // The set of character indices on which text runs need to be broken for page |
| 443 | // objects. |
| 444 | std::set<int> page_object_text_run_breaks_; |
Daniel Hosseinian | 6ed110f | 2020-09-25 21:43:43 | [diff] [blame] | 445 | base::OnceClosure thumbnail_callback_; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 446 | bool available_; |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 447 | }; |
| 448 | |
K Moon | 9a62bf4 | 2019-08-07 20:05:36 | [diff] [blame] | 449 | // Converts page orientations to the PDFium equivalents, as defined by |
| 450 | // FPDF_RenderPage(). |
Lei Zhang | 38b99bcf | 2022-02-17 20:27:55 | [diff] [blame] | 451 | constexpr int ToPDFiumRotation(PageOrientation orientation) { |
| 452 | // Could use static_cast<int>(orientation), but using an exhaustive switch |
| 453 | // will trigger an error if we ever change the definition of |
| 454 | // `PageOrientation`. |
| 455 | switch (orientation) { |
| 456 | case PageOrientation::kOriginal: |
| 457 | return 0; |
| 458 | case PageOrientation::kClockwise90: |
| 459 | return 1; |
| 460 | case PageOrientation::kClockwise180: |
| 461 | return 2; |
| 462 | case PageOrientation::kClockwise270: |
| 463 | return 3; |
| 464 | } |
| 465 | } |
K Moon | 9a62bf4 | 2019-08-07 20:05:36 | [diff] [blame] | 466 | |
Ankit Kumar 🌪️ | 221befc | 2020-01-30 07:13:21 | [diff] [blame] | 467 | constexpr uint32_t MakeARGB(unsigned int a, |
| 468 | unsigned int r, |
| 469 | unsigned int g, |
| 470 | unsigned int b) { |
| 471 | return (a << 24) | (r << 16) | (g << 8) | b; |
| 472 | } |
| 473 | |
jam@chromium.org | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 474 | } // namespace chrome_pdf |
| 475 | |
| 476 | #endif // PDF_PDFIUM_PDFIUM_PAGE_H_ |