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