• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "public/fpdf_text.h"
8 
9 #include <algorithm>
10 #include <limits>
11 #include <memory>
12 #include <vector>
13 
14 #include "build/build_config.h"
15 #include "core/fpdfapi/font/cpdf_font.h"
16 #include "core/fpdfapi/page/cpdf_page.h"
17 #include "core/fpdfapi/page/cpdf_textobject.h"
18 #include "core/fpdfdoc/cpdf_viewerpreferences.h"
19 #include "core/fpdftext/cpdf_linkextract.h"
20 #include "core/fpdftext/cpdf_textpage.h"
21 #include "core/fpdftext/cpdf_textpagefind.h"
22 #include "core/fxcrt/check_op.h"
23 #include "core/fxcrt/compiler_specific.h"
24 #include "core/fxcrt/fx_memcpy_wrappers.h"
25 #include "core/fxcrt/numerics/safe_conversions.h"
26 #include "core/fxcrt/span.h"
27 #include "core/fxcrt/span_util.h"
28 #include "core/fxcrt/stl_util.h"
29 #include "fpdfsdk/cpdfsdk_helpers.h"
30 
31 namespace {
32 
GetTextPageForValidIndex(FPDF_TEXTPAGE text_page,int index)33 CPDF_TextPage* GetTextPageForValidIndex(FPDF_TEXTPAGE text_page, int index) {
34   if (!text_page || index < 0)
35     return nullptr;
36 
37   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
38   return static_cast<size_t>(index) < textpage->size() ? textpage : nullptr;
39 }
40 
41 }  // namespace
42 
FPDFText_LoadPage(FPDF_PAGE page)43 FPDF_EXPORT FPDF_TEXTPAGE FPDF_CALLCONV FPDFText_LoadPage(FPDF_PAGE page) {
44   CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page);
45   if (!pPDFPage)
46     return nullptr;
47 
48   CPDF_ViewerPreferences viewRef(pPDFPage->GetDocument());
49   auto textpage =
50       std::make_unique<CPDF_TextPage>(pPDFPage, viewRef.IsDirectionR2L());
51 
52   // Caller takes ownership.
53   return FPDFTextPageFromCPDFTextPage(textpage.release());
54 }
55 
FPDFText_ClosePage(FPDF_TEXTPAGE text_page)56 FPDF_EXPORT void FPDF_CALLCONV FPDFText_ClosePage(FPDF_TEXTPAGE text_page) {
57   // PDFium takes ownership.
58   std::unique_ptr<CPDF_TextPage> textpage_deleter(
59       CPDFTextPageFromFPDFTextPage(text_page));
60 }
61 
FPDFText_CountChars(FPDF_TEXTPAGE text_page)62 FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page) {
63   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
64   return textpage ? textpage->CountChars() : -1;
65 }
66 
67 FPDF_EXPORT unsigned int FPDF_CALLCONV
FPDFText_GetUnicode(FPDF_TEXTPAGE text_page,int index)68 FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index) {
69   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
70   if (!textpage)
71     return 0;
72 
73   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
74   return charinfo.unicode();
75 }
76 
77 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFText_GetTextObject(FPDF_TEXTPAGE text_page,int index)78 FPDFText_GetTextObject(FPDF_TEXTPAGE text_page, int index) {
79   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
80   if (!textpage) {
81     return nullptr;
82   }
83 
84   return FPDFPageObjectFromCPDFPageObject(
85       textpage->GetCharInfo(index).text_object());
86 }
87 
FPDFText_IsGenerated(FPDF_TEXTPAGE text_page,int index)88 FPDF_EXPORT int FPDF_CALLCONV FPDFText_IsGenerated(FPDF_TEXTPAGE text_page,
89                                                    int index) {
90   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
91   if (!textpage)
92     return -1;
93 
94   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
95   return charinfo.char_type() == CPDF_TextPage::CharType::kGenerated ? 1 : 0;
96 }
97 
FPDFText_IsHyphen(FPDF_TEXTPAGE text_page,int index)98 FPDF_EXPORT int FPDF_CALLCONV FPDFText_IsHyphen(FPDF_TEXTPAGE text_page,
99                                                 int index) {
100   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
101   if (!textpage) {
102     return -1;
103   }
104 
105   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
106   return charinfo.char_type() == CPDF_TextPage::CharType::kHyphen;
107 }
108 
109 FPDF_EXPORT int FPDF_CALLCONV
FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page,int index)110 FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page, int index) {
111   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
112   if (!textpage)
113     return -1;
114 
115   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
116   return charinfo.char_type() == CPDF_TextPage::CharType::kNotUnicode;
117 }
118 
FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,int index)119 FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,
120                                                       int index) {
121   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
122   if (!textpage)
123     return 0;
124 
125   return textpage->GetCharFontSize(index);
126 }
127 
128 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFText_GetFontInfo(FPDF_TEXTPAGE text_page,int index,void * buffer,unsigned long buflen,int * flags)129 FPDFText_GetFontInfo(FPDF_TEXTPAGE text_page,
130                      int index,
131                      void* buffer,
132                      unsigned long buflen,
133                      int* flags) {
134   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
135   if (!textpage)
136     return 0;
137 
138   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
139   if (!charinfo.text_object()) {
140     return 0;
141   }
142 
143   RetainPtr<CPDF_Font> font = charinfo.text_object()->GetFont();
144   if (flags)
145     *flags = font->GetFontFlags();
146 
147   // SAFETY: required from caller.
148   auto result_span = UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen));
149   ByteString basefont = font->GetBaseFontName();
150   auto basefont_span = basefont.span_with_terminator();
151   fxcrt::try_spancpy(result_span, basefont_span);
152   return pdfium::checked_cast<unsigned long>(basefont_span.size());
153 }
154 
FPDFText_GetFontWeight(FPDF_TEXTPAGE text_page,int index)155 FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetFontWeight(FPDF_TEXTPAGE text_page,
156                                                      int index) {
157   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
158   if (!textpage)
159     return -1;
160 
161   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
162   if (!charinfo.text_object()) {
163     return -1;
164   }
165 
166   return charinfo.text_object()->GetFont()->GetFontWeight();
167 }
168 
169 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,int index,unsigned int * R,unsigned int * G,unsigned int * B,unsigned int * A)170 FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,
171                       int index,
172                       unsigned int* R,
173                       unsigned int* G,
174                       unsigned int* B,
175                       unsigned int* A) {
176   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
177   if (!textpage || !R || !G || !B || !A)
178     return false;
179 
180   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
181   if (!charinfo.text_object()) {
182     return false;
183   }
184 
185   FX_COLORREF fill_color =
186       charinfo.text_object()->color_state().GetFillColorRef();
187   *R = FXSYS_GetRValue(fill_color);
188   *G = FXSYS_GetGValue(fill_color);
189   *B = FXSYS_GetBValue(fill_color);
190   *A = FXSYS_GetUnsignedAlpha(
191       charinfo.text_object()->general_state().GetFillAlpha());
192   return true;
193 }
194 
195 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,int index,unsigned int * R,unsigned int * G,unsigned int * B,unsigned int * A)196 FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,
197                         int index,
198                         unsigned int* R,
199                         unsigned int* G,
200                         unsigned int* B,
201                         unsigned int* A) {
202   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
203   if (!textpage || !R || !G || !B || !A)
204     return false;
205 
206   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
207   if (!charinfo.text_object()) {
208     return false;
209   }
210 
211   FX_COLORREF stroke_color =
212       charinfo.text_object()->color_state().GetStrokeColorRef();
213   *R = FXSYS_GetRValue(stroke_color);
214   *G = FXSYS_GetGValue(stroke_color);
215   *B = FXSYS_GetBValue(stroke_color);
216   *A = FXSYS_GetUnsignedAlpha(
217       charinfo.text_object()->general_state().GetStrokeAlpha());
218   return true;
219 }
220 
FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,int index)221 FPDF_EXPORT float FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
222                                                       int index) {
223   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
224   if (!textpage)
225     return -1.0f;
226 
227   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
228   // On the left is our current Matrix and on the right a generic rotation
229   // matrix for our coordinate space.
230   // | a  b  0 |    | cos(t)  -sin(t)  0 |
231   // | c  d  0 |    | sin(t)   cos(t)  0 |
232   // | e  f  1 |    |   0        0     1 |
233   // Calculate the angle of the vector
234   float angle = atan2f(charinfo.matrix().c, charinfo.matrix().a);
235   if (angle < 0)
236     angle = 2 * FXSYS_PI + angle;
237 
238   return angle;
239 }
240 
FPDFText_GetCharBox(FPDF_TEXTPAGE text_page,int index,double * left,double * right,double * bottom,double * top)241 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetCharBox(FPDF_TEXTPAGE text_page,
242                                                         int index,
243                                                         double* left,
244                                                         double* right,
245                                                         double* bottom,
246                                                         double* top) {
247   if (!left || !right || !bottom || !top)
248     return false;
249 
250   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
251   if (!textpage)
252     return false;
253 
254   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
255   *left = charinfo.char_box().left;
256   *right = charinfo.char_box().right;
257   *bottom = charinfo.char_box().bottom;
258   *top = charinfo.char_box().top;
259   return true;
260 }
261 
262 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetLooseCharBox(FPDF_TEXTPAGE text_page,int index,FS_RECTF * rect)263 FPDFText_GetLooseCharBox(FPDF_TEXTPAGE text_page, int index, FS_RECTF* rect) {
264   if (!rect)
265     return false;
266 
267   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
268   if (!textpage)
269     return false;
270 
271   *rect = FSRectFFromCFXFloatRect(textpage->GetCharLooseBounds(index));
272   return true;
273 }
274 
FPDFText_GetMatrix(FPDF_TEXTPAGE text_page,int index,FS_MATRIX * matrix)275 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetMatrix(FPDF_TEXTPAGE text_page,
276                                                        int index,
277                                                        FS_MATRIX* matrix) {
278   if (!matrix)
279     return false;
280 
281   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
282   if (!textpage)
283     return false;
284 
285   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
286   *matrix = FSMatrixFromCFXMatrix(charinfo.matrix());
287   return true;
288 }
289 
290 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFText_GetCharOrigin(FPDF_TEXTPAGE text_page,int index,double * x,double * y)291 FPDFText_GetCharOrigin(FPDF_TEXTPAGE text_page,
292                        int index,
293                        double* x,
294                        double* y) {
295   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
296   if (!textpage)
297     return false;
298 
299   const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
300   *x = charinfo.origin().x;
301   *y = charinfo.origin().y;
302   return true;
303 }
304 
305 FPDF_EXPORT int FPDF_CALLCONV
FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page,double x,double y,double xTolerance,double yTolerance)306 FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page,
307                            double x,
308                            double y,
309                            double xTolerance,
310                            double yTolerance) {
311   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
312   if (!textpage)
313     return -3;
314 
315   return textpage->GetIndexAtPos(
316       CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
317       CFX_SizeF(static_cast<float>(xTolerance),
318                 static_cast<float>(yTolerance)));
319 }
320 
FPDFText_GetText(FPDF_TEXTPAGE page,int start_index,int char_count,unsigned short * result)321 FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE page,
322                                                int start_index,
323                                                int char_count,
324                                                unsigned short* result) {
325   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page);
326   if (!textpage || start_index < 0 || char_count < 0 || !result) {
327     return 0;
328   }
329   int char_available = textpage->CountChars() - start_index;
330   if (char_available <= 0) {
331     return 0;
332   }
333   char_count = std::min(char_count, char_available);
334   if (char_count == 0) {
335     // Writing out "", which has a character count of 1 due to the NUL.
336     *result = '\0';
337     return 1;
338   }
339   // SAFETY: Required from caller. Public API description states that
340   // `result` must be able to hold `char_count` characters plus a
341   // terminator.
342   CHECK_LT(char_count, std::numeric_limits<int>::max());
343   pdfium::span<unsigned short> result_span =
344       UNSAFE_BUFFERS(pdfium::make_span(result, char_count + 1));
345 
346   // Includes two-byte terminator in string data itself.
347   ByteString str = textpage->GetPageText(start_index, char_count).ToUCS2LE();
348   auto str_span = fxcrt::reinterpret_span<const unsigned short>(str.span());
349 
350   // Hard CHECK() in Copy() if retrieved text is too long.
351   fxcrt::Copy(str_span, result_span);
352   return pdfium::checked_cast<int>(str_span.size());
353 }
354 
FPDFText_CountRects(FPDF_TEXTPAGE text_page,int start,int count)355 FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
356                                                   int start,
357                                                   int count) {
358   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
359   return textpage ? textpage->CountRects(start, count) : 0;
360 }
361 
FPDFText_GetRect(FPDF_TEXTPAGE text_page,int rect_index,double * left,double * top,double * right,double * bottom)362 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page,
363                                                      int rect_index,
364                                                      double* left,
365                                                      double* top,
366                                                      double* right,
367                                                      double* bottom) {
368   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
369   if (!textpage)
370     return false;
371 
372   CFX_FloatRect rect;
373   bool result = textpage->GetRect(rect_index, &rect);
374 
375   *left = rect.left;
376   *top = rect.top;
377   *right = rect.right;
378   *bottom = rect.bottom;
379   return result;
380 }
381 
FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page,double left,double top,double right,double bottom,unsigned short * buffer,int buflen)382 FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page,
383                                                       double left,
384                                                       double top,
385                                                       double right,
386                                                       double bottom,
387                                                       unsigned short* buffer,
388                                                       int buflen) {
389   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
390   if (!textpage) {
391     return 0;
392   }
393   CFX_FloatRect rect((float)left, (float)bottom, (float)right, (float)top);
394   WideString wstr = textpage->GetTextByRect(rect);
395   if (buflen <= 0 || !buffer) {
396     return pdfium::checked_cast<int>(wstr.GetLength());
397   }
398 
399   // SAFETY: Required from caller. Public API states that buflen
400   // describes the number of values buffer can hold.
401   const auto buffer_span = UNSAFE_BUFFERS(pdfium::make_span(buffer, buflen));
402 
403   ByteString str = wstr.ToUTF16LE();
404   pdfium::span<const char> str_span = str.span();
405   auto copy_span = fxcrt::reinterpret_span<const unsigned short>(str_span);
406   if (copy_span.size() > buffer_span.size()) {
407     copy_span = copy_span.first(buffer_span.size());
408   }
409   fxcrt::Copy(copy_span, buffer_span);
410   return pdfium::checked_cast<int>(copy_span.size());
411 }
412 
413 FPDF_EXPORT FPDF_SCHHANDLE FPDF_CALLCONV
FPDFText_FindStart(FPDF_TEXTPAGE text_page,FPDF_WIDESTRING findwhat,unsigned long flags,int start_index)414 FPDFText_FindStart(FPDF_TEXTPAGE text_page,
415                    FPDF_WIDESTRING findwhat,
416                    unsigned long flags,
417                    int start_index) {
418   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
419   if (!textpage)
420     return nullptr;
421 
422   CPDF_TextPageFind::Options options;
423   options.bMatchCase = !!(flags & FPDF_MATCHCASE);
424   options.bMatchWholeWord = !!(flags & FPDF_MATCHWHOLEWORD);
425   options.bConsecutive = !!(flags & FPDF_CONSECUTIVE);
426 
427   // SAFETY: required from caller.
428   auto find = CPDF_TextPageFind::Create(
429       textpage, UNSAFE_BUFFERS(WideStringFromFPDFWideString(findwhat)), options,
430       start_index >= 0 ? std::optional<size_t>(start_index) : std::nullopt);
431 
432   // Caller takes ownership.
433   return FPDFSchHandleFromCPDFTextPageFind(find.release());
434 }
435 
FPDFText_FindNext(FPDF_SCHHANDLE handle)436 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindNext(FPDF_SCHHANDLE handle) {
437   if (!handle)
438     return false;
439 
440   CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
441   return textpageFind->FindNext();
442 }
443 
FPDFText_FindPrev(FPDF_SCHHANDLE handle)444 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindPrev(FPDF_SCHHANDLE handle) {
445   if (!handle)
446     return false;
447 
448   CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
449   return textpageFind->FindPrev();
450 }
451 
452 FPDF_EXPORT int FPDF_CALLCONV
FPDFText_GetSchResultIndex(FPDF_SCHHANDLE handle)453 FPDFText_GetSchResultIndex(FPDF_SCHHANDLE handle) {
454   if (!handle)
455     return 0;
456 
457   CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
458   return textpageFind->GetCurOrder();
459 }
460 
FPDFText_GetSchCount(FPDF_SCHHANDLE handle)461 FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetSchCount(FPDF_SCHHANDLE handle) {
462   if (!handle)
463     return 0;
464 
465   CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
466   return textpageFind->GetMatchedCount();
467 }
468 
FPDFText_FindClose(FPDF_SCHHANDLE handle)469 FPDF_EXPORT void FPDF_CALLCONV FPDFText_FindClose(FPDF_SCHHANDLE handle) {
470   if (!handle)
471     return;
472 
473   // Take ownership back from caller and destroy.
474   std::unique_ptr<CPDF_TextPageFind> textpageFind(
475       CPDFTextPageFindFromFPDFSchHandle(handle));
476 }
477 
478 // web link
479 FPDF_EXPORT FPDF_PAGELINK FPDF_CALLCONV
FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page)480 FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page) {
481   CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
482   if (!textpage)
483     return nullptr;
484 
485   auto pagelink = std::make_unique<CPDF_LinkExtract>(textpage);
486   pagelink->ExtractLinks();
487 
488   // Caller takes ownership.
489   return FPDFPageLinkFromCPDFLinkExtract(pagelink.release());
490 }
491 
FPDFLink_CountWebLinks(FPDF_PAGELINK link_page)492 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountWebLinks(FPDF_PAGELINK link_page) {
493   if (!link_page)
494     return 0;
495 
496   CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
497   return pdfium::checked_cast<int>(pageLink->CountLinks());
498 }
499 
FPDFLink_GetURL(FPDF_PAGELINK link_page,int link_index,unsigned short * buffer,int buflen)500 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetURL(FPDF_PAGELINK link_page,
501                                               int link_index,
502                                               unsigned short* buffer,
503                                               int buflen) {
504   WideString wsUrl(L"");
505   if (link_page && link_index >= 0) {
506     CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
507     wsUrl = pageLink->GetURL(link_index);
508   }
509   ByteString cbUTF16URL = wsUrl.ToUTF16LE();
510   auto url_span =
511       fxcrt::reinterpret_span<const unsigned short>(cbUTF16URL.span());
512   if (!buffer || buflen <= 0) {
513     return pdfium::checked_cast<int>(url_span.size());
514   }
515 
516   // SAFETY: required from caller.
517   pdfium::span<unsigned short> result_span =
518       UNSAFE_BUFFERS(pdfium::make_span(buffer, buflen));
519 
520   size_t size = std::min(url_span.size(), result_span.size());
521   fxcrt::Copy(url_span.first(size), result_span);
522   return pdfium::checked_cast<int>(size);
523 }
524 
FPDFLink_CountRects(FPDF_PAGELINK link_page,int link_index)525 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountRects(FPDF_PAGELINK link_page,
526                                                   int link_index) {
527   if (!link_page || link_index < 0)
528     return 0;
529 
530   CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
531   return fxcrt::CollectionSize<int>(pageLink->GetRects(link_index));
532 }
533 
FPDFLink_GetRect(FPDF_PAGELINK link_page,int link_index,int rect_index,double * left,double * top,double * right,double * bottom)534 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page,
535                                                      int link_index,
536                                                      int rect_index,
537                                                      double* left,
538                                                      double* top,
539                                                      double* right,
540                                                      double* bottom) {
541   if (!link_page || link_index < 0 || rect_index < 0)
542     return false;
543 
544   CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
545   std::vector<CFX_FloatRect> rectArray = pageLink->GetRects(link_index);
546   if (rect_index >= fxcrt::CollectionSize<int>(rectArray))
547     return false;
548 
549   *left = rectArray[rect_index].left;
550   *right = rectArray[rect_index].right;
551   *top = rectArray[rect_index].top;
552   *bottom = rectArray[rect_index].bottom;
553   return true;
554 }
555 
556 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFLink_GetTextRange(FPDF_PAGELINK link_page,int link_index,int * start_char_index,int * char_count)557 FPDFLink_GetTextRange(FPDF_PAGELINK link_page,
558                       int link_index,
559                       int* start_char_index,
560                       int* char_count) {
561   if (!link_page || link_index < 0)
562     return false;
563 
564   CPDF_LinkExtract* page_link = CPDFLinkExtractFromFPDFPageLink(link_page);
565   auto maybe_range = page_link->GetTextRange(link_index);
566   if (!maybe_range.has_value())
567     return false;
568 
569   *start_char_index = pdfium::checked_cast<int>(maybe_range.value().m_Start);
570   *char_count = pdfium::checked_cast<int>(maybe_range.value().m_Count);
571   return true;
572 }
573 
FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page)574 FPDF_EXPORT void FPDF_CALLCONV FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page) {
575   delete CPDFLinkExtractFromFPDFPageLink(link_page);
576 }
577