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