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