1 // Copyright 2018 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 "fpdfsdk/cpdfsdk_helpers.h"
8
9 #include <utility>
10
11 #include "build/build_config.h"
12 #include "constants/form_fields.h"
13 #include "constants/stream_dict_common.h"
14 #include "core/fpdfapi/page/cpdf_page.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_document.h"
18 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
19 #include "core/fpdfapi/render/cpdf_renderoptions.h"
20 #include "core/fpdfdoc/cpdf_annot.h"
21 #include "core/fpdfdoc/cpdf_interactiveform.h"
22 #include "core/fpdfdoc/cpdf_metadata.h"
23 #include "core/fxcrt/check.h"
24 #include "core/fxcrt/compiler_specific.h"
25 #include "core/fxcrt/fx_memcpy_wrappers.h"
26 #include "core/fxcrt/fx_safe_types.h"
27 #include "core/fxcrt/numerics/safe_conversions.h"
28 #include "core/fxcrt/span_util.h"
29 #include "core/fxcrt/stl_util.h"
30 #include "core/fxcrt/unowned_ptr.h"
31 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
32
33 namespace {
34
35 constexpr char kQuadPoints[] = "QuadPoints";
36
37 // 0 bit: FPDF_POLICY_MACHINETIME_ACCESS
38 uint32_t g_sandbox_policy = 0xFFFFFFFF;
39
40 UNSUPPORT_INFO* g_unsupport_info = nullptr;
41
RaiseUnsupportedError(int nError)42 bool RaiseUnsupportedError(int nError) {
43 if (!g_unsupport_info)
44 return false;
45
46 if (g_unsupport_info->FSDK_UnSupport_Handler)
47 g_unsupport_info->FSDK_UnSupport_Handler(g_unsupport_info, nError);
48 return true;
49 }
50
51 // Use the existence of the XFA array as a signal for XFA forms.
DocHasXFA(const CPDF_Document * doc)52 bool DocHasXFA(const CPDF_Document* doc) {
53 const CPDF_Dictionary* root = doc->GetRoot();
54 if (!root)
55 return false;
56
57 RetainPtr<const CPDF_Dictionary> form = root->GetDictFor("AcroForm");
58 return form && form->GetArrayFor("XFA");
59 }
60
GetStreamMaybeCopyAndReturnLengthImpl(RetainPtr<const CPDF_Stream> stream,pdfium::span<uint8_t> buffer,bool decode)61 unsigned long GetStreamMaybeCopyAndReturnLengthImpl(
62 RetainPtr<const CPDF_Stream> stream,
63 pdfium::span<uint8_t> buffer,
64 bool decode) {
65 DCHECK(stream);
66 auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(stream));
67 if (decode)
68 stream_acc->LoadAllDataFiltered();
69 else
70 stream_acc->LoadAllDataRaw();
71
72 pdfium::span<const uint8_t> stream_data_span = stream_acc->GetSpan();
73 if (!buffer.empty() && buffer.size() <= stream_data_span.size()) {
74 fxcrt::Copy(stream_data_span, buffer);
75 }
76 return pdfium::checked_cast<unsigned long>(stream_data_span.size());
77 }
78
79 // TODO(tsepez): should be UNSAFE_BUFFER_USAGE.
FPDFWideStringLength(const unsigned short * str)80 size_t FPDFWideStringLength(const unsigned short* str) {
81 if (!str) {
82 return 0;
83 }
84 size_t len = 0;
85 // SAFETY: NUL-termination required from caller.
86 UNSAFE_BUFFERS({
87 while (str[len]) {
88 len++;
89 }
90 });
91 return len;
92 }
93
94 #ifdef PDF_ENABLE_XFA
95 class FPDF_FileHandlerContext final : public IFX_SeekableStream {
96 public:
97 CONSTRUCT_VIA_MAKE_RETAIN;
98
99 // IFX_SeekableStream:
100 FX_FILESIZE GetSize() override;
101 FX_FILESIZE GetPosition() override;
102 bool IsEOF() override;
103 bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
104 FX_FILESIZE offset) override;
105 bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
106 bool Flush() override;
107
108 private:
109 explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS);
110 ~FPDF_FileHandlerContext() override;
111
112 UnownedPtr<FPDF_FILEHANDLER> const m_pFS;
113 FX_FILESIZE m_nCurPos = 0;
114 };
115
FPDF_FileHandlerContext(FPDF_FILEHANDLER * pFS)116 FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS)
117 : m_pFS(pFS) {
118 CHECK(m_pFS);
119 }
120
~FPDF_FileHandlerContext()121 FPDF_FileHandlerContext::~FPDF_FileHandlerContext() {
122 if (m_pFS->Release) {
123 m_pFS->Release(m_pFS->clientData);
124 }
125 }
126
GetSize()127 FX_FILESIZE FPDF_FileHandlerContext::GetSize() {
128 if (m_pFS->GetSize) {
129 return static_cast<FX_FILESIZE>(m_pFS->GetSize(m_pFS->clientData));
130 }
131 return 0;
132 }
133
IsEOF()134 bool FPDF_FileHandlerContext::IsEOF() {
135 return m_nCurPos >= GetSize();
136 }
137
GetPosition()138 FX_FILESIZE FPDF_FileHandlerContext::GetPosition() {
139 return m_nCurPos;
140 }
141
ReadBlockAtOffset(pdfium::span<uint8_t> buffer,FX_FILESIZE offset)142 bool FPDF_FileHandlerContext::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
143 FX_FILESIZE offset) {
144 if (buffer.empty() || !m_pFS->ReadBlock) {
145 return false;
146 }
147
148 FX_SAFE_FILESIZE new_position = offset;
149 new_position += buffer.size();
150 if (!new_position.IsValid()) {
151 return false;
152 }
153
154 if (m_pFS->ReadBlock(m_pFS->clientData, static_cast<FPDF_DWORD>(offset),
155 buffer.data(),
156 static_cast<FPDF_DWORD>(buffer.size())) != 0) {
157 return false;
158 }
159
160 m_nCurPos = new_position.ValueOrDie();
161 return true;
162 }
163
WriteBlock(pdfium::span<const uint8_t> buffer)164 bool FPDF_FileHandlerContext::WriteBlock(pdfium::span<const uint8_t> buffer) {
165 if (!m_pFS->WriteBlock) {
166 return false;
167 }
168
169 const FX_FILESIZE size = GetSize();
170 FX_SAFE_FILESIZE new_position = size;
171 new_position += buffer.size();
172 if (!new_position.IsValid()) {
173 return false;
174 }
175
176 if (m_pFS->WriteBlock(m_pFS->clientData, static_cast<FPDF_DWORD>(size),
177 buffer.data(),
178 static_cast<FPDF_DWORD>(buffer.size())) != 0) {
179 return false;
180 }
181
182 m_nCurPos = new_position.ValueOrDie();
183 return true;
184 }
185
Flush()186 bool FPDF_FileHandlerContext::Flush() {
187 if (!m_pFS->Flush) {
188 return true;
189 }
190
191 return m_pFS->Flush(m_pFS->clientData) == 0;
192 }
193 #endif // PDF_ENABLE_XFA
194
195 } // namespace
196
IPDFPageFromFPDFPage(FPDF_PAGE page)197 IPDF_Page* IPDFPageFromFPDFPage(FPDF_PAGE page) {
198 return reinterpret_cast<IPDF_Page*>(page);
199 }
200
FPDFPageFromIPDFPage(IPDF_Page * page)201 FPDF_PAGE FPDFPageFromIPDFPage(IPDF_Page* page) {
202 return reinterpret_cast<FPDF_PAGE>(page);
203 }
204
CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc)205 CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc) {
206 return reinterpret_cast<CPDF_Document*>(doc);
207 }
208
FPDFDocumentFromCPDFDocument(CPDF_Document * doc)209 FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc) {
210 return reinterpret_cast<FPDF_DOCUMENT>(doc);
211 }
212
CPDFPageFromFPDFPage(FPDF_PAGE page)213 CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page) {
214 return page ? IPDFPageFromFPDFPage(page)->AsPDFPage() : nullptr;
215 }
216
FXDIBFormatFromFPDFFormat(int format)217 FXDIB_Format FXDIBFormatFromFPDFFormat(int format) {
218 switch (format) {
219 case FPDFBitmap_Gray:
220 return FXDIB_Format::k8bppRgb;
221 case FPDFBitmap_BGR:
222 return FXDIB_Format::kBgr;
223 case FPDFBitmap_BGRx:
224 return FXDIB_Format::kBgrx;
225 case FPDFBitmap_BGRA:
226 return FXDIB_Format::kBgra;
227 default:
228 return FXDIB_Format::kInvalid;
229 }
230 }
231
FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle)232 CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle) {
233 CPDFSDK_FormFillEnvironment* pFormFillEnv =
234 CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
235 return pFormFillEnv ? pFormFillEnv->GetInteractiveForm() : nullptr;
236 }
237
ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string)238 ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string) {
239 // SAFETY: caller ensures `wide_string` is NUL-terminated and enforced
240 // by UNSAFE_BUFFER_USAGE in header file.
241 return UNSAFE_BUFFERS(WideStringFromFPDFWideString(wide_string).ToUTF8());
242 }
243
WideStringFromFPDFWideString(FPDF_WIDESTRING wide_string)244 WideString WideStringFromFPDFWideString(FPDF_WIDESTRING wide_string) {
245 // SAFETY: caller ensures `wide_string` is NUL-terminated and enforced
246 // by UNSAFE_BUFFER_USAGE in header file.
247 return WideString::FromUTF16LE(UNSAFE_BUFFERS(
248 pdfium::make_span(reinterpret_cast<const uint8_t*>(wide_string),
249 FPDFWideStringLength(wide_string) * 2)));
250 }
251
SpanFromFPDFApiArgs(void * buffer,pdfium::StrictNumeric<size_t> buflen)252 UNSAFE_BUFFER_USAGE pdfium::span<char> SpanFromFPDFApiArgs(
253 void* buffer,
254 pdfium::StrictNumeric<size_t> buflen) {
255 if (!buffer) {
256 // API convention is to ignore `buflen` arg when `buffer` is NULL.
257 return pdfium::span<char>();
258 }
259 // SAFETY: required from caller, enforced by UNSAFE_BUFFER_USAGE in header.
260 return UNSAFE_BUFFERS(pdfium::make_span(static_cast<char*>(buffer), buflen));
261 }
262
263 #ifdef PDF_ENABLE_XFA
MakeSeekableStream(FPDF_FILEHANDLER * pFilehandler)264 RetainPtr<IFX_SeekableStream> MakeSeekableStream(
265 FPDF_FILEHANDLER* pFilehandler) {
266 return pdfium::MakeRetain<FPDF_FileHandlerContext>(pFilehandler);
267 }
268 #endif // PDF_ENABLE_XFA
269
GetQuadPointsArrayFromDictionary(const CPDF_Dictionary * dict)270 RetainPtr<const CPDF_Array> GetQuadPointsArrayFromDictionary(
271 const CPDF_Dictionary* dict) {
272 return dict->GetArrayFor("QuadPoints");
273 }
274
GetMutableQuadPointsArrayFromDictionary(CPDF_Dictionary * dict)275 RetainPtr<CPDF_Array> GetMutableQuadPointsArrayFromDictionary(
276 CPDF_Dictionary* dict) {
277 return pdfium::WrapRetain(
278 const_cast<CPDF_Array*>(GetQuadPointsArrayFromDictionary(dict).Get()));
279 }
280
AddQuadPointsArrayToDictionary(CPDF_Dictionary * dict)281 RetainPtr<CPDF_Array> AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) {
282 return dict->SetNewFor<CPDF_Array>(kQuadPoints);
283 }
284
IsValidQuadPointsIndex(const CPDF_Array * array,size_t index)285 bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index) {
286 return array && index < array->size() / 8;
287 }
288
GetQuadPointsAtIndex(RetainPtr<const CPDF_Array> array,size_t quad_index,FS_QUADPOINTSF * quad_points)289 bool GetQuadPointsAtIndex(RetainPtr<const CPDF_Array> array,
290 size_t quad_index,
291 FS_QUADPOINTSF* quad_points) {
292 DCHECK(quad_points);
293 DCHECK(array);
294
295 if (!IsValidQuadPointsIndex(array, quad_index))
296 return false;
297
298 quad_index *= 8;
299 quad_points->x1 = array->GetFloatAt(quad_index);
300 quad_points->y1 = array->GetFloatAt(quad_index + 1);
301 quad_points->x2 = array->GetFloatAt(quad_index + 2);
302 quad_points->y2 = array->GetFloatAt(quad_index + 3);
303 quad_points->x3 = array->GetFloatAt(quad_index + 4);
304 quad_points->y3 = array->GetFloatAt(quad_index + 5);
305 quad_points->x4 = array->GetFloatAt(quad_index + 6);
306 quad_points->y4 = array->GetFloatAt(quad_index + 7);
307 return true;
308 }
309
CFXPointFFromFSPointF(const FS_POINTF & point)310 CFX_PointF CFXPointFFromFSPointF(const FS_POINTF& point) {
311 return CFX_PointF(point.x, point.y);
312 }
313
CFXFloatRectFromFSRectF(const FS_RECTF & rect)314 CFX_FloatRect CFXFloatRectFromFSRectF(const FS_RECTF& rect) {
315 return CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top);
316 }
317
FSRectFFromCFXFloatRect(const CFX_FloatRect & rect)318 FS_RECTF FSRectFFromCFXFloatRect(const CFX_FloatRect& rect) {
319 return {rect.left, rect.top, rect.right, rect.bottom};
320 }
321
CFXMatrixFromFSMatrix(const FS_MATRIX & matrix)322 CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix) {
323 return CFX_Matrix(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f);
324 }
325
FSMatrixFromCFXMatrix(const CFX_Matrix & matrix)326 FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix) {
327 return {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f};
328 }
329
NulTerminateMaybeCopyAndReturnLength(const ByteString & text,pdfium::span<char> result_span)330 unsigned long NulTerminateMaybeCopyAndReturnLength(
331 const ByteString& text,
332 pdfium::span<char> result_span) {
333 pdfium::span<const char> text_span = text.span_with_terminator();
334 fxcrt::try_spancpy(result_span, text_span);
335 return pdfium::checked_cast<unsigned long>(text_span.size());
336 }
337
Utf16EncodeMaybeCopyAndReturnLength(const WideString & text,pdfium::span<char> result_span)338 unsigned long Utf16EncodeMaybeCopyAndReturnLength(
339 const WideString& text,
340 pdfium::span<char> result_span) {
341 ByteString encoded_text = text.ToUTF16LE();
342 pdfium::span<const char> encoded_text_span = encoded_text.span();
343 fxcrt::try_spancpy(result_span, encoded_text_span);
344 return pdfium::checked_cast<unsigned long>(encoded_text_span.size());
345 }
346
GetRawStreamMaybeCopyAndReturnLength(RetainPtr<const CPDF_Stream> stream,pdfium::span<uint8_t> buffer)347 unsigned long GetRawStreamMaybeCopyAndReturnLength(
348 RetainPtr<const CPDF_Stream> stream,
349 pdfium::span<uint8_t> buffer) {
350 return GetStreamMaybeCopyAndReturnLengthImpl(std::move(stream), buffer,
351 /*decode=*/false);
352 }
353
DecodeStreamMaybeCopyAndReturnLength(RetainPtr<const CPDF_Stream> stream,pdfium::span<uint8_t> buffer)354 unsigned long DecodeStreamMaybeCopyAndReturnLength(
355 RetainPtr<const CPDF_Stream> stream,
356 pdfium::span<uint8_t> buffer) {
357 return GetStreamMaybeCopyAndReturnLengthImpl(std::move(stream), buffer,
358 /*decode=*/true);
359 }
360
SetPDFSandboxPolicy(FPDF_DWORD policy,FPDF_BOOL enable)361 void SetPDFSandboxPolicy(FPDF_DWORD policy, FPDF_BOOL enable) {
362 switch (policy) {
363 case FPDF_POLICY_MACHINETIME_ACCESS: {
364 uint32_t mask = 1 << policy;
365 if (enable)
366 g_sandbox_policy |= mask;
367 else
368 g_sandbox_policy &= ~mask;
369 } break;
370 default:
371 break;
372 }
373 }
374
IsPDFSandboxPolicyEnabled(FPDF_DWORD policy)375 FPDF_BOOL IsPDFSandboxPolicyEnabled(FPDF_DWORD policy) {
376 switch (policy) {
377 case FPDF_POLICY_MACHINETIME_ACCESS: {
378 uint32_t mask = 1 << policy;
379 return !!(g_sandbox_policy & mask);
380 }
381 default:
382 return false;
383 }
384 }
385
SetPDFUnsupportInfo(UNSUPPORT_INFO * unsp_info)386 void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info) {
387 g_unsupport_info = unsp_info;
388 }
389
ReportUnsupportedFeatures(const CPDF_Document * pDoc)390 void ReportUnsupportedFeatures(const CPDF_Document* pDoc) {
391 const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
392 if (!pRootDict)
393 return;
394
395 // Portfolios and Packages
396 if (pRootDict->KeyExist("Collection"))
397 RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION);
398
399 RetainPtr<const CPDF_Dictionary> pNameDict = pRootDict->GetDictFor("Names");
400 if (pNameDict) {
401 if (pNameDict->KeyExist("EmbeddedFiles"))
402 RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT);
403
404 RetainPtr<const CPDF_Dictionary> pJSDict =
405 pNameDict->GetDictFor("JavaScript");
406 if (pJSDict) {
407 RetainPtr<const CPDF_Array> pArray = pJSDict->GetArrayFor("Names");
408 if (pArray) {
409 for (size_t i = 0; i < pArray->size(); i++) {
410 ByteString cbStr = pArray->GetByteStringAt(i);
411 if (cbStr == "com.adobe.acrobat.SharedReview.Register") {
412 RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW);
413 break;
414 }
415 }
416 }
417 }
418 }
419
420 // SharedForm
421 RetainPtr<const CPDF_Stream> pStream = pRootDict->GetStreamFor("Metadata");
422 if (pStream) {
423 CPDF_Metadata metadata(std::move(pStream));
424 for (const UnsupportedFeature& feature : metadata.CheckForSharedForm())
425 RaiseUnsupportedError(static_cast<int>(feature));
426 }
427 }
428
ReportUnsupportedXFA(const CPDF_Document * pDoc)429 void ReportUnsupportedXFA(const CPDF_Document* pDoc) {
430 if (!pDoc->GetExtension() && DocHasXFA(pDoc))
431 RaiseUnsupportedError(FPDF_UNSP_DOC_XFAFORM);
432 }
433
CheckForUnsupportedAnnot(const CPDF_Annot * pAnnot)434 void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot) {
435 switch (pAnnot->GetSubtype()) {
436 case CPDF_Annot::Subtype::FILEATTACHMENT:
437 RaiseUnsupportedError(FPDF_UNSP_ANNOT_ATTACHMENT);
438 break;
439 case CPDF_Annot::Subtype::MOVIE:
440 RaiseUnsupportedError(FPDF_UNSP_ANNOT_MOVIE);
441 break;
442 case CPDF_Annot::Subtype::RICHMEDIA:
443 RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA);
444 break;
445 case CPDF_Annot::Subtype::SCREEN: {
446 const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
447 ByteString cbString = pAnnotDict->GetByteStringFor("IT");
448 if (cbString != "Img")
449 RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_MEDIA);
450 break;
451 }
452 case CPDF_Annot::Subtype::SOUND:
453 RaiseUnsupportedError(FPDF_UNSP_ANNOT_SOUND);
454 break;
455 case CPDF_Annot::Subtype::THREED:
456 RaiseUnsupportedError(FPDF_UNSP_ANNOT_3DANNOT);
457 break;
458 case CPDF_Annot::Subtype::WIDGET: {
459 const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
460 ByteString cbString =
461 pAnnotDict->GetByteStringFor(pdfium::form_fields::kFT);
462 if (cbString == pdfium::form_fields::kSig)
463 RaiseUnsupportedError(FPDF_UNSP_ANNOT_SIG);
464 break;
465 }
466 default:
467 break;
468 }
469 }
470
ProcessParseError(CPDF_Parser::Error err)471 void ProcessParseError(CPDF_Parser::Error err) {
472 uint32_t err_code = FPDF_ERR_SUCCESS;
473 // Translate FPDFAPI error code to FPDFVIEW error code
474 switch (err) {
475 case CPDF_Parser::SUCCESS:
476 err_code = FPDF_ERR_SUCCESS;
477 break;
478 case CPDF_Parser::FILE_ERROR:
479 err_code = FPDF_ERR_FILE;
480 break;
481 case CPDF_Parser::FORMAT_ERROR:
482 err_code = FPDF_ERR_FORMAT;
483 break;
484 case CPDF_Parser::PASSWORD_ERROR:
485 err_code = FPDF_ERR_PASSWORD;
486 break;
487 case CPDF_Parser::HANDLER_ERROR:
488 err_code = FPDF_ERR_SECURITY;
489 break;
490 }
491 FXSYS_SetLastError(err_code);
492 }
493
SetColorFromScheme(const FPDF_COLORSCHEME * pColorScheme,CPDF_RenderOptions * pRenderOptions)494 void SetColorFromScheme(const FPDF_COLORSCHEME* pColorScheme,
495 CPDF_RenderOptions* pRenderOptions) {
496 CPDF_RenderOptions::ColorScheme color_scheme;
497 color_scheme.path_fill_color =
498 static_cast<FX_ARGB>(pColorScheme->path_fill_color);
499 color_scheme.path_stroke_color =
500 static_cast<FX_ARGB>(pColorScheme->path_stroke_color);
501 color_scheme.text_fill_color =
502 static_cast<FX_ARGB>(pColorScheme->text_fill_color);
503 color_scheme.text_stroke_color =
504 static_cast<FX_ARGB>(pColorScheme->text_stroke_color);
505 pRenderOptions->SetColorScheme(color_scheme);
506 }
507
ParsePageRangeString(const ByteString & bsPageRange,uint32_t nCount)508 std::vector<uint32_t> ParsePageRangeString(const ByteString& bsPageRange,
509 uint32_t nCount) {
510 ByteStringView alphabet(" 0123456789-,");
511 for (const auto& ch : bsPageRange) {
512 if (!alphabet.Contains(ch))
513 return std::vector<uint32_t>();
514 }
515
516 ByteString bsStrippedPageRange = bsPageRange;
517 bsStrippedPageRange.Remove(' ');
518
519 std::vector<uint32_t> results;
520 for (const auto& entry : fxcrt::Split(bsStrippedPageRange, ',')) {
521 std::vector<ByteString> args = fxcrt::Split(entry, '-');
522 if (args.size() == 1) {
523 uint32_t page_num = pdfium::checked_cast<uint32_t>(atoi(args[0].c_str()));
524 if (page_num == 0 || page_num > nCount)
525 return std::vector<uint32_t>();
526 results.push_back(page_num - 1);
527 } else if (args.size() == 2) {
528 uint32_t first_num =
529 pdfium::checked_cast<uint32_t>(atoi(args[0].c_str()));
530 if (first_num == 0)
531 return std::vector<uint32_t>();
532 uint32_t last_num = pdfium::checked_cast<uint32_t>(atoi(args[1].c_str()));
533 if (last_num == 0 || first_num > last_num || last_num > nCount)
534 return std::vector<uint32_t>();
535 for (uint32_t i = first_num; i <= last_num; ++i)
536 results.push_back(i - 1);
537 } else {
538 return std::vector<uint32_t>();
539 }
540 }
541 return results;
542 }
543