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