| // Copyright 2018 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "fpdfsdk/cpdfsdk_helpers.h" | |
| #include "build/build_config.h" | |
| #include "constants/form_fields.h" | |
| #include "constants/stream_dict_common.h" | |
| #include "core/fpdfapi/page/cpdf_page.h" | |
| #include "core/fpdfapi/parser/cpdf_array.h" | |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" | |
| #include "core/fpdfapi/parser/cpdf_document.h" | |
| #include "core/fpdfapi/parser/cpdf_stream_acc.h" | |
| #include "core/fpdfapi/render/cpdf_renderoptions.h" | |
| #include "core/fpdfdoc/cpdf_annot.h" | |
| #include "core/fpdfdoc/cpdf_interactiveform.h" | |
| #include "core/fpdfdoc/cpdf_metadata.h" | |
| #include "fpdfsdk/cpdfsdk_formfillenvironment.h" | |
| namespace { | |
| constexpr char kQuadPoints[] = "QuadPoints"; | |
| // 0 bit: FPDF_POLICY_MACHINETIME_ACCESS | |
| uint32_t g_sandbox_policy = 0xFFFFFFFF; | |
| UNSUPPORT_INFO* g_unsupport_info = nullptr; | |
| bool RaiseUnsupportedError(int nError) { | |
| if (!g_unsupport_info) | |
| return false; | |
| if (g_unsupport_info->FSDK_UnSupport_Handler) | |
| g_unsupport_info->FSDK_UnSupport_Handler(g_unsupport_info, nError); | |
| return true; | |
| } | |
| // Use the existence of the XFA array as a signal for XFA forms. | |
| bool DocHasXFA(const CPDF_Document* doc) { | |
| const CPDF_Dictionary* root = doc->GetRoot(); | |
| if (!root) | |
| return false; | |
| const CPDF_Dictionary* form = root->GetDictFor("AcroForm"); | |
| return form && form->GetArrayFor("XFA"); | |
| } | |
| unsigned long GetStreamMaybeCopyAndReturnLengthImpl(const CPDF_Stream* stream, | |
| void* buffer, | |
| unsigned long buflen, | |
| bool decode) { | |
| ASSERT(stream); | |
| auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream); | |
| if (decode) | |
| stream_acc->LoadAllDataFiltered(); | |
| else | |
| stream_acc->LoadAllDataRaw(); | |
| const auto stream_data_size = stream_acc->GetSize(); | |
| if (!buffer || buflen < stream_data_size) | |
| return stream_data_size; | |
| memcpy(buffer, stream_acc->GetData(), stream_data_size); | |
| return stream_data_size; | |
| } | |
| #ifdef PDF_ENABLE_XFA | |
| class FPDF_FileHandlerContext final : public IFX_SeekableStream { | |
| public: | |
| CONSTRUCT_VIA_MAKE_RETAIN; | |
| // IFX_SeekableStream: | |
| FX_FILESIZE GetSize() override; | |
| bool IsEOF() override; | |
| FX_FILESIZE GetPosition() override; | |
| bool ReadBlockAtOffset(void* buffer, | |
| FX_FILESIZE offset, | |
| size_t size) override; | |
| size_t ReadBlock(void* buffer, size_t size) override; | |
| bool WriteBlockAtOffset(const void* buffer, | |
| FX_FILESIZE offset, | |
| size_t size) override; | |
| bool Flush() override; | |
| void SetPosition(FX_FILESIZE pos) { m_nCurPos = pos; } | |
| private: | |
| explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS); | |
| ~FPDF_FileHandlerContext() override; | |
| FPDF_FILEHANDLER* m_pFS; | |
| FX_FILESIZE m_nCurPos; | |
| }; | |
| FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) { | |
| m_pFS = pFS; | |
| m_nCurPos = 0; | |
| } | |
| FPDF_FileHandlerContext::~FPDF_FileHandlerContext() { | |
| if (m_pFS && m_pFS->Release) | |
| m_pFS->Release(m_pFS->clientData); | |
| } | |
| FX_FILESIZE FPDF_FileHandlerContext::GetSize() { | |
| if (m_pFS && m_pFS->GetSize) | |
| return (FX_FILESIZE)m_pFS->GetSize(m_pFS->clientData); | |
| return 0; | |
| } | |
| bool FPDF_FileHandlerContext::IsEOF() { | |
| return m_nCurPos >= GetSize(); | |
| } | |
| FX_FILESIZE FPDF_FileHandlerContext::GetPosition() { | |
| return m_nCurPos; | |
| } | |
| bool FPDF_FileHandlerContext::ReadBlockAtOffset(void* buffer, | |
| FX_FILESIZE offset, | |
| size_t size) { | |
| if (!buffer || !size || !m_pFS->ReadBlock) | |
| return false; | |
| if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer, | |
| (FPDF_DWORD)size) == 0) { | |
| m_nCurPos = offset + size; | |
| return true; | |
| } | |
| return false; | |
| } | |
| size_t FPDF_FileHandlerContext::ReadBlock(void* buffer, size_t size) { | |
| if (!buffer || !size || !m_pFS->ReadBlock) | |
| return 0; | |
| FX_FILESIZE nSize = GetSize(); | |
| if (m_nCurPos >= nSize) | |
| return 0; | |
| FX_FILESIZE dwAvail = nSize - m_nCurPos; | |
| if (dwAvail < (FX_FILESIZE)size) | |
| size = static_cast<size_t>(dwAvail); | |
| if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)m_nCurPos, buffer, | |
| (FPDF_DWORD)size) == 0) { | |
| m_nCurPos += size; | |
| return size; | |
| } | |
| return 0; | |
| } | |
| bool FPDF_FileHandlerContext::WriteBlockAtOffset(const void* buffer, | |
| FX_FILESIZE offset, | |
| size_t size) { | |
| if (!m_pFS || !m_pFS->WriteBlock) | |
| return false; | |
| if (m_pFS->WriteBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer, | |
| (FPDF_DWORD)size) == 0) { | |
| m_nCurPos = offset + size; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool FPDF_FileHandlerContext::Flush() { | |
| if (!m_pFS || !m_pFS->Flush) | |
| return true; | |
| return m_pFS->Flush(m_pFS->clientData) == 0; | |
| } | |
| #endif // PDF_ENABLE_XFA | |
| } // namespace | |
| IPDF_Page* IPDFPageFromFPDFPage(FPDF_PAGE page) { | |
| return reinterpret_cast<IPDF_Page*>(page); | |
| } | |
| FPDF_PAGE FPDFPageFromIPDFPage(IPDF_Page* page) { | |
| return reinterpret_cast<FPDF_PAGE>(page); | |
| } | |
| CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc) { | |
| return reinterpret_cast<CPDF_Document*>(doc); | |
| } | |
| FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc) { | |
| return reinterpret_cast<FPDF_DOCUMENT>(doc); | |
| } | |
| CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page) { | |
| return page ? IPDFPageFromFPDFPage(page)->AsPDFPage() : nullptr; | |
| } | |
| CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle) { | |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = | |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); | |
| return pFormFillEnv ? pFormFillEnv->GetInteractiveForm() : nullptr; | |
| } | |
| ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string) { | |
| return WideStringFromFPDFWideString(wide_string).ToUTF8(); | |
| } | |
| WideString WideStringFromFPDFWideString(FPDF_WIDESTRING wide_string) { | |
| return WideString::FromUTF16LE(wide_string, | |
| WideString::WStringLength(wide_string)); | |
| } | |
| #ifdef PDF_ENABLE_XFA | |
| RetainPtr<IFX_SeekableStream> MakeSeekableStream( | |
| FPDF_FILEHANDLER* pFilehandler) { | |
| return pdfium::MakeRetain<FPDF_FileHandlerContext>(pFilehandler); | |
| } | |
| #endif // PDF_ENABLE_XFA | |
| const CPDF_Array* GetQuadPointsArrayFromDictionary( | |
| const CPDF_Dictionary* dict) { | |
| return dict->GetArrayFor("QuadPoints"); | |
| } | |
| CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict) { | |
| return dict->GetArrayFor("QuadPoints"); | |
| } | |
| CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) { | |
| return dict->SetNewFor<CPDF_Array>(kQuadPoints); | |
| } | |
| bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index) { | |
| return array && index < array->size() / 8; | |
| } | |
| bool GetQuadPointsAtIndex(const CPDF_Array* array, | |
| size_t quad_index, | |
| FS_QUADPOINTSF* quad_points) { | |
| ASSERT(quad_points); | |
| ASSERT(array); | |
| if (!IsValidQuadPointsIndex(array, quad_index)) | |
| return false; | |
| quad_index *= 8; | |
| quad_points->x1 = array->GetNumberAt(quad_index); | |
| quad_points->y1 = array->GetNumberAt(quad_index + 1); | |
| quad_points->x2 = array->GetNumberAt(quad_index + 2); | |
| quad_points->y2 = array->GetNumberAt(quad_index + 3); | |
| quad_points->x3 = array->GetNumberAt(quad_index + 4); | |
| quad_points->y3 = array->GetNumberAt(quad_index + 5); | |
| quad_points->x4 = array->GetNumberAt(quad_index + 6); | |
| quad_points->y4 = array->GetNumberAt(quad_index + 7); | |
| return true; | |
| } | |
| CFX_PointF CFXPointFFromFSPointF(const FS_POINTF& point) { | |
| return CFX_PointF(point.x, point.y); | |
| } | |
| CFX_FloatRect CFXFloatRectFromFSRectF(const FS_RECTF& rect) { | |
| return CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top); | |
| } | |
| FS_RECTF FSRectFFromCFXFloatRect(const CFX_FloatRect& rect) { | |
| return {rect.left, rect.top, rect.right, rect.bottom}; | |
| } | |
| CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix) { | |
| return CFX_Matrix(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f); | |
| } | |
| FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix) { | |
| return {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f}; | |
| } | |
| unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text, | |
| void* buffer, | |
| unsigned long buflen) { | |
| unsigned long len = text.GetLength() + 1; | |
| if (buffer && len <= buflen) | |
| memcpy(buffer, text.c_str(), len); | |
| return len; | |
| } | |
| unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text, | |
| void* buffer, | |
| unsigned long buflen) { | |
| ByteString encoded_text = text.ToUTF16LE(); | |
| unsigned long len = encoded_text.GetLength(); | |
| if (buffer && len <= buflen) | |
| memcpy(buffer, encoded_text.c_str(), len); | |
| return len; | |
| } | |
| unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream, | |
| void* buffer, | |
| unsigned long buflen) { | |
| return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen, | |
| /*decode=*/false); | |
| } | |
| unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream, | |
| void* buffer, | |
| unsigned long buflen) { | |
| return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen, | |
| /*decode=*/true); | |
| } | |
| void SetPDFSandboxPolicy(FPDF_DWORD policy, FPDF_BOOL enable) { | |
| switch (policy) { | |
| case FPDF_POLICY_MACHINETIME_ACCESS: { | |
| uint32_t mask = 1 << policy; | |
| if (enable) | |
| g_sandbox_policy |= mask; | |
| else | |
| g_sandbox_policy &= ~mask; | |
| } break; | |
| default: | |
| break; | |
| } | |
| } | |
| FPDF_BOOL IsPDFSandboxPolicyEnabled(FPDF_DWORD policy) { | |
| switch (policy) { | |
| case FPDF_POLICY_MACHINETIME_ACCESS: { | |
| uint32_t mask = 1 << policy; | |
| return !!(g_sandbox_policy & mask); | |
| } | |
| default: | |
| return false; | |
| } | |
| } | |
| void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info) { | |
| g_unsupport_info = unsp_info; | |
| } | |
| void ReportUnsupportedFeatures(const CPDF_Document* pDoc) { | |
| const CPDF_Dictionary* pRootDict = pDoc->GetRoot(); | |
| if (!pRootDict) | |
| return; | |
| // Portfolios and Packages | |
| if (pRootDict->KeyExist("Collection")) | |
| RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION); | |
| const CPDF_Dictionary* pNameDict = pRootDict->GetDictFor("Names"); | |
| if (pNameDict) { | |
| if (pNameDict->KeyExist("EmbeddedFiles")) | |
| RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT); | |
| const CPDF_Dictionary* pJSDict = pNameDict->GetDictFor("JavaScript"); | |
| if (pJSDict) { | |
| const CPDF_Array* pArray = pJSDict->GetArrayFor("Names"); | |
| if (pArray) { | |
| for (size_t i = 0; i < pArray->size(); i++) { | |
| ByteString cbStr = pArray->GetStringAt(i); | |
| if (cbStr.Compare("com.adobe.acrobat.SharedReview.Register") == 0) { | |
| RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // SharedForm | |
| const CPDF_Stream* pStream = pRootDict->GetStreamFor("Metadata"); | |
| if (pStream) { | |
| CPDF_Metadata metadata(pStream); | |
| for (const UnsupportedFeature& feature : metadata.CheckForSharedForm()) | |
| RaiseUnsupportedError(static_cast<int>(feature)); | |
| } | |
| } | |
| void ReportUnsupportedXFA(const CPDF_Document* pDoc) { | |
| if (!pDoc->GetExtension() && DocHasXFA(pDoc)) | |
| RaiseUnsupportedError(FPDF_UNSP_DOC_XFAFORM); | |
| } | |
| void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot) { | |
| switch (pAnnot->GetSubtype()) { | |
| case CPDF_Annot::Subtype::FILEATTACHMENT: | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_ATTACHMENT); | |
| break; | |
| case CPDF_Annot::Subtype::MOVIE: | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_MOVIE); | |
| break; | |
| case CPDF_Annot::Subtype::RICHMEDIA: | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA); | |
| break; | |
| case CPDF_Annot::Subtype::SCREEN: { | |
| const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); | |
| ByteString cbString = pAnnotDict->GetStringFor("IT"); | |
| if (cbString != "Img") | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_MEDIA); | |
| break; | |
| } | |
| case CPDF_Annot::Subtype::SOUND: | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_SOUND); | |
| break; | |
| case CPDF_Annot::Subtype::THREED: | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_3DANNOT); | |
| break; | |
| case CPDF_Annot::Subtype::WIDGET: { | |
| const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); | |
| ByteString cbString = pAnnotDict->GetStringFor(pdfium::form_fields::kFT); | |
| if (cbString == pdfium::form_fields::kSig) | |
| RaiseUnsupportedError(FPDF_UNSP_ANNOT_SIG); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| } | |
| void ProcessParseError(CPDF_Parser::Error err) { | |
| uint32_t err_code = FPDF_ERR_SUCCESS; | |
| // Translate FPDFAPI error code to FPDFVIEW error code | |
| switch (err) { | |
| case CPDF_Parser::SUCCESS: | |
| err_code = FPDF_ERR_SUCCESS; | |
| break; | |
| case CPDF_Parser::FILE_ERROR: | |
| err_code = FPDF_ERR_FILE; | |
| break; | |
| case CPDF_Parser::FORMAT_ERROR: | |
| err_code = FPDF_ERR_FORMAT; | |
| break; | |
| case CPDF_Parser::PASSWORD_ERROR: | |
| err_code = FPDF_ERR_PASSWORD; | |
| break; | |
| case CPDF_Parser::HANDLER_ERROR: | |
| err_code = FPDF_ERR_SECURITY; | |
| break; | |
| } | |
| FXSYS_SetLastError(err_code); | |
| } | |
| void SetColorFromScheme(const FPDF_COLORSCHEME* pColorScheme, | |
| CPDF_RenderOptions* pRenderOptions) { | |
| CPDF_RenderOptions::ColorScheme color_scheme; | |
| color_scheme.path_fill_color = pColorScheme->path_fill_color; | |
| color_scheme.path_stroke_color = pColorScheme->path_stroke_color; | |
| color_scheme.text_fill_color = pColorScheme->text_fill_color; | |
| color_scheme.text_stroke_color = pColorScheme->text_stroke_color; | |
| pRenderOptions->SetColorScheme(color_scheme); | |
| } |