// Copyright 2014 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 "core/fpdfapi/fpdf_page/pageint.h" #include #include "core/fpdfapi/fpdf_font/cpdf_type3char.h" #include "core/fpdfapi/fpdf_page/cpdf_allstates.h" #include "core/fpdfapi/fpdf_page/include/cpdf_form.h" #include "core/fpdfapi/fpdf_page/include/cpdf_page.h" #include "core/fpdfapi/fpdf_page/include/cpdf_pageobject.h" #include "core/fpdfapi/fpdf_page/include/cpdf_path.h" #include "core/fpdfapi/fpdf_parser/cpdf_boolean.h" #include "core/fpdfapi/fpdf_parser/cpdf_null.h" #include "core/fpdfapi/fpdf_parser/fpdf_parser_utility.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_array.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_dictionary.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_document.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_name.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_number.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_stream.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_stream_acc.h" #include "core/fpdfapi/fpdf_parser/include/cpdf_string.h" #include "core/fpdfapi/fpdf_parser/include/fpdf_parser_decode.h" #include "core/fpdfapi/include/cpdf_modulemgr.h" #include "core/fxcodec/include/fx_codec.h" #include "core/fxcrt/include/fx_ext.h" #include "core/fxcrt/include/fx_safe_types.h" #include "core/fxge/include/cfx_fxgedevice.h" #include "core/fxge/include/cfx_renderdevice.h" namespace { const uint32_t kMaxNestedArrayLevel = 512; const uint32_t kMaxWordBuffer = 256; const FX_STRSIZE kMaxStringLength = 32767; } // namespace CPDF_StreamParser::CPDF_StreamParser(const uint8_t* pData, uint32_t dwSize) { m_pBuf = pData; m_Size = dwSize; m_Pos = 0; m_pLastObj = nullptr; } CPDF_StreamParser::~CPDF_StreamParser() { if (m_pLastObj) { m_pLastObj->Release(); } } uint32_t DecodeAllScanlines(CCodec_ScanlineDecoder* pDecoder, uint8_t*& dest_buf, uint32_t& dest_size) { if (!pDecoder) { return FX_INVALID_OFFSET; } int ncomps = pDecoder->CountComps(); int bpc = pDecoder->GetBPC(); int width = pDecoder->GetWidth(); int height = pDecoder->GetHeight(); int pitch = (width * ncomps * bpc + 7) / 8; if (height == 0 || pitch > (1 << 30) / height) { delete pDecoder; return FX_INVALID_OFFSET; } dest_buf = FX_Alloc2D(uint8_t, pitch, height); dest_size = pitch * height; // Safe since checked alloc returned. for (int row = 0; row < height; row++) { const uint8_t* pLine = pDecoder->GetScanline(row); if (!pLine) break; FXSYS_memcpy(dest_buf + row * pitch, pLine, pitch); } uint32_t srcoff = pDecoder->GetSrcOffset(); delete pDecoder; return srcoff; } CCodec_ScanlineDecoder* FPDFAPI_CreateFaxDecoder( const uint8_t* src_buf, uint32_t src_size, int width, int height, const CPDF_Dictionary* pParams); uint32_t PDF_DecodeInlineStream(const uint8_t* src_buf, uint32_t limit, int width, int height, CFX_ByteString& decoder, CPDF_Dictionary* pParam, uint8_t*& dest_buf, uint32_t& dest_size) { if (decoder == "CCITTFaxDecode" || decoder == "CCF") { CCodec_ScanlineDecoder* pDecoder = FPDFAPI_CreateFaxDecoder(src_buf, limit, width, height, pParam); return DecodeAllScanlines(pDecoder, dest_buf, dest_size); } if (decoder == "ASCII85Decode" || decoder == "A85") { return A85Decode(src_buf, limit, dest_buf, dest_size); } if (decoder == "ASCIIHexDecode" || decoder == "AHx") { return HexDecode(src_buf, limit, dest_buf, dest_size); } if (decoder == "FlateDecode" || decoder == "Fl") { return FPDFAPI_FlateOrLZWDecode(FALSE, src_buf, limit, pParam, dest_size, dest_buf, dest_size); } if (decoder == "LZWDecode" || decoder == "LZW") { return FPDFAPI_FlateOrLZWDecode(TRUE, src_buf, limit, pParam, 0, dest_buf, dest_size); } if (decoder == "DCTDecode" || decoder == "DCT") { CCodec_ScanlineDecoder* pDecoder = CPDF_ModuleMgr::Get()->GetJpegModule()->CreateDecoder( src_buf, limit, width, height, 0, pParam ? pParam->GetIntegerBy("ColorTransform", 1) : 1); return DecodeAllScanlines(pDecoder, dest_buf, dest_size); } if (decoder == "RunLengthDecode" || decoder == "RL") { return RunLengthDecode(src_buf, limit, dest_buf, dest_size); } dest_size = 0; dest_buf = 0; return (uint32_t)-1; } CPDF_Stream* CPDF_StreamParser::ReadInlineStream(CPDF_Document* pDoc, CPDF_Dictionary* pDict, CPDF_Object* pCSObj) { if (m_Pos == m_Size) return nullptr; if (PDFCharIsWhitespace(m_pBuf[m_Pos])) m_Pos++; CFX_ByteString Decoder; CPDF_Dictionary* pParam = nullptr; CPDF_Object* pFilter = pDict->GetDirectObjectBy("Filter"); if (pFilter) { if (CPDF_Array* pArray = pFilter->AsArray()) { Decoder = pArray->GetStringAt(0); CPDF_Array* pParams = pDict->GetArrayBy("DecodeParms"); if (pParams) pParam = pParams->GetDictAt(0); } else { Decoder = pFilter->GetString(); pParam = pDict->GetDictBy("DecodeParms"); } } uint32_t width = pDict->GetIntegerBy("Width"); uint32_t height = pDict->GetIntegerBy("Height"); uint32_t OrigSize = 0; if (pCSObj) { uint32_t bpc = pDict->GetIntegerBy("BitsPerComponent"); uint32_t nComponents = 1; CPDF_ColorSpace* pCS = pDoc->LoadColorSpace(pCSObj); if (pCS) { nComponents = pCS->CountComponents(); pDoc->GetPageData()->ReleaseColorSpace(pCSObj); } else { nComponents = 3; } uint32_t pitch = width; if (bpc && pitch > INT_MAX / bpc) return nullptr; pitch *= bpc; if (nComponents && pitch > INT_MAX / nComponents) return nullptr; pitch *= nComponents; if (pitch > INT_MAX - 7) return nullptr; pitch += 7; pitch /= 8; OrigSize = pitch; } else { if (width > INT_MAX - 7) return nullptr; OrigSize = ((width + 7) / 8); } if (height && OrigSize > INT_MAX / height) return nullptr; OrigSize *= height; uint8_t* pData = nullptr; uint32_t dwStreamSize; if (Decoder.IsEmpty()) { if (OrigSize > m_Size - m_Pos) { OrigSize = m_Size - m_Pos; } pData = FX_Alloc(uint8_t, OrigSize); FXSYS_memcpy(pData, m_pBuf + m_Pos, OrigSize); dwStreamSize = OrigSize; m_Pos += OrigSize; } else { uint32_t dwDestSize = OrigSize; dwStreamSize = PDF_DecodeInlineStream(m_pBuf + m_Pos, m_Size - m_Pos, width, height, Decoder, pParam, pData, dwDestSize); FX_Free(pData); if ((int)dwStreamSize < 0) return nullptr; uint32_t dwSavePos = m_Pos; m_Pos += dwStreamSize; while (1) { uint32_t dwPrevPos = m_Pos; CPDF_StreamParser::SyntaxType type = ParseNextElement(); if (type == CPDF_StreamParser::EndOfData) break; if (type != CPDF_StreamParser::Keyword) { dwStreamSize += m_Pos - dwPrevPos; continue; } if (GetWordSize() == 2 && GetWordBuf()[0] == 'E' && GetWordBuf()[1] == 'I') { m_Pos = dwPrevPos; break; } dwStreamSize += m_Pos - dwPrevPos; } m_Pos = dwSavePos; pData = FX_Alloc(uint8_t, dwStreamSize); FXSYS_memcpy(pData, m_pBuf + m_Pos, dwStreamSize); m_Pos += dwStreamSize; } pDict->SetAtInteger("Length", (int)dwStreamSize); return new CPDF_Stream(pData, dwStreamSize, pDict); } CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() { if (m_pLastObj) { m_pLastObj->Release(); m_pLastObj = nullptr; } m_WordSize = 0; FX_BOOL bIsNumber = TRUE; if (!PositionIsInBounds()) return EndOfData; int ch = m_pBuf[m_Pos++]; while (1) { while (PDFCharIsWhitespace(ch)) { if (!PositionIsInBounds()) return EndOfData; ch = m_pBuf[m_Pos++]; } if (ch != '%') break; while (1) { if (!PositionIsInBounds()) return EndOfData; ch = m_pBuf[m_Pos++]; if (PDFCharIsLineEnding(ch)) break; } } if (PDFCharIsDelimiter(ch) && ch != '/') { m_Pos--; m_pLastObj = ReadNextObject(false, 0); return Others; } while (1) { if (m_WordSize < kMaxWordBuffer) m_WordBuffer[m_WordSize++] = ch; if (!PDFCharIsNumeric(ch)) bIsNumber = FALSE; if (!PositionIsInBounds()) break; ch = m_pBuf[m_Pos++]; if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) { m_Pos--; break; } } m_WordBuffer[m_WordSize] = 0; if (bIsNumber) return Number; if (m_WordBuffer[0] == '/') return Name; if (m_WordSize == 4) { if (memcmp(m_WordBuffer, "true", 4) == 0) { m_pLastObj = new CPDF_Boolean(TRUE); return Others; } if (memcmp(m_WordBuffer, "null", 4) == 0) { m_pLastObj = new CPDF_Null; return Others; } } else if (m_WordSize == 5) { if (memcmp(m_WordBuffer, "false", 5) == 0) { m_pLastObj = new CPDF_Boolean(FALSE); return Others; } } return Keyword; } CPDF_Object* CPDF_StreamParser::ReadNextObject(bool bAllowNestedArray, uint32_t dwInArrayLevel) { FX_BOOL bIsNumber; GetNextWord(bIsNumber); if (!m_WordSize) return nullptr; if (bIsNumber) { m_WordBuffer[m_WordSize] = 0; return new CPDF_Number(CFX_ByteStringC(m_WordBuffer, m_WordSize)); } int first_char = m_WordBuffer[0]; if (first_char == '/') { return new CPDF_Name( PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1))); } if (first_char == '(') return new CPDF_String(ReadString(), FALSE); if (first_char == '<') { if (m_WordSize == 1) return new CPDF_String(ReadHexString(), TRUE); CPDF_Dictionary* pDict = new CPDF_Dictionary; while (1) { GetNextWord(bIsNumber); if (m_WordSize == 2 && m_WordBuffer[0] == '>') break; if (!m_WordSize || m_WordBuffer[0] != '/') { pDict->Release(); return nullptr; } CFX_ByteString key = PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1)); CPDF_Object* pObj = ReadNextObject(true, 0); if (!pObj) { pDict->Release(); return nullptr; } if (key.IsEmpty()) pObj->Release(); else pDict->SetAt(key, pObj); } return pDict; } if (first_char == '[') { if ((!bAllowNestedArray && dwInArrayLevel) || dwInArrayLevel > kMaxNestedArrayLevel) { return nullptr; } CPDF_Array* pArray = new CPDF_Array; while (1) { CPDF_Object* pObj = ReadNextObject(bAllowNestedArray, dwInArrayLevel + 1); if (pObj) { pArray->Add(pObj); continue; } if (!m_WordSize || m_WordBuffer[0] == ']') break; } return pArray; } if (m_WordSize == 5 && !memcmp(m_WordBuffer, "false", 5)) return new CPDF_Boolean(FALSE); if (m_WordSize == 4) { if (memcmp(m_WordBuffer, "true", 4) == 0) return new CPDF_Boolean(TRUE); if (memcmp(m_WordBuffer, "null", 4) == 0) return new CPDF_Null; } return nullptr; } void CPDF_StreamParser::GetNextWord(FX_BOOL& bIsNumber) { m_WordSize = 0; bIsNumber = TRUE; if (!PositionIsInBounds()) return; int ch = m_pBuf[m_Pos++]; while (1) { while (PDFCharIsWhitespace(ch)) { if (!PositionIsInBounds()) { return; } ch = m_pBuf[m_Pos++]; } if (ch != '%') break; while (1) { if (!PositionIsInBounds()) return; ch = m_pBuf[m_Pos++]; if (PDFCharIsLineEnding(ch)) break; } } if (PDFCharIsDelimiter(ch)) { bIsNumber = FALSE; m_WordBuffer[m_WordSize++] = ch; if (ch == '/') { while (1) { if (!PositionIsInBounds()) return; ch = m_pBuf[m_Pos++]; if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) { m_Pos--; return; } if (m_WordSize < kMaxWordBuffer) m_WordBuffer[m_WordSize++] = ch; } } else if (ch == '<') { if (!PositionIsInBounds()) return; ch = m_pBuf[m_Pos++]; if (ch == '<') m_WordBuffer[m_WordSize++] = ch; else m_Pos--; } else if (ch == '>') { if (!PositionIsInBounds()) return; ch = m_pBuf[m_Pos++]; if (ch == '>') m_WordBuffer[m_WordSize++] = ch; else m_Pos--; } return; } while (1) { if (m_WordSize < kMaxWordBuffer) m_WordBuffer[m_WordSize++] = ch; if (!PDFCharIsNumeric(ch)) bIsNumber = FALSE; if (!PositionIsInBounds()) return; ch = m_pBuf[m_Pos++]; if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) { m_Pos--; break; } } } CFX_ByteString CPDF_StreamParser::ReadString() { if (!PositionIsInBounds()) return CFX_ByteString(); uint8_t ch = m_pBuf[m_Pos++]; CFX_ByteTextBuf buf; int parlevel = 0; int status = 0; int iEscCode = 0; while (1) { switch (status) { case 0: if (ch == ')') { if (parlevel == 0) { if (buf.GetLength() > kMaxStringLength) { return CFX_ByteString(buf.GetBuffer(), kMaxStringLength); } return buf.MakeString(); } parlevel--; buf.AppendChar(')'); } else if (ch == '(') { parlevel++; buf.AppendChar('('); } else if (ch == '\\') { status = 1; } else { buf.AppendChar((char)ch); } break; case 1: if (ch >= '0' && ch <= '7') { iEscCode = FXSYS_toDecimalDigit(static_cast(ch)); status = 2; break; } if (ch == 'n') { buf.AppendChar('\n'); } else if (ch == 'r') { buf.AppendChar('\r'); } else if (ch == 't') { buf.AppendChar('\t'); } else if (ch == 'b') { buf.AppendChar('\b'); } else if (ch == 'f') { buf.AppendChar('\f'); } else if (ch == '\r') { status = 4; break; } else if (ch == '\n') { } else { buf.AppendChar(ch); } status = 0; break; case 2: if (ch >= '0' && ch <= '7') { iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(static_cast(ch)); status = 3; } else { buf.AppendChar(iEscCode); status = 0; continue; } break; case 3: if (ch >= '0' && ch <= '7') { iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(static_cast(ch)); buf.AppendChar(iEscCode); status = 0; } else { buf.AppendChar(iEscCode); status = 0; continue; } break; case 4: status = 0; if (ch != '\n') { continue; } break; } if (!PositionIsInBounds()) break; ch = m_pBuf[m_Pos++]; } if (PositionIsInBounds()) ++m_Pos; if (buf.GetLength() > kMaxStringLength) { return CFX_ByteString(buf.GetBuffer(), kMaxStringLength); } return buf.MakeString(); } CFX_ByteString CPDF_StreamParser::ReadHexString() { if (!PositionIsInBounds()) return CFX_ByteString(); CFX_ByteTextBuf buf; bool bFirst = true; int code = 0; while (PositionIsInBounds()) { int ch = m_pBuf[m_Pos++]; if (ch == '>') break; if (!std::isxdigit(ch)) continue; int val = FXSYS_toHexDigit(ch); if (bFirst) { code = val * 16; } else { code += val; buf.AppendByte((uint8_t)code); } bFirst = !bFirst; } if (!bFirst) buf.AppendChar((char)code); if (buf.GetLength() > kMaxStringLength) return CFX_ByteString(buf.GetBuffer(), kMaxStringLength); return buf.MakeString(); } bool CPDF_StreamParser::PositionIsInBounds() const { return m_Pos < m_Size; } CPDF_ContentParser::CPDF_ContentParser() : m_Status(Ready), m_InternalStage(STAGE_GETCONTENT), m_pObjectHolder(nullptr), m_bForm(false), m_pType3Char(nullptr), m_pData(nullptr), m_Size(0), m_CurrentOffset(0) {} CPDF_ContentParser::~CPDF_ContentParser() { if (!m_pSingleStream) FX_Free(m_pData); } void CPDF_ContentParser::Start(CPDF_Page* pPage) { if (m_Status != Ready || !pPage || !pPage->m_pDocument || !pPage->m_pFormDict) { m_Status = Done; return; } m_pObjectHolder = pPage; m_bForm = FALSE; m_Status = ToBeContinued; m_InternalStage = STAGE_GETCONTENT; m_CurrentOffset = 0; CPDF_Object* pContent = pPage->m_pFormDict->GetDirectObjectBy("Contents"); if (!pContent) { m_Status = Done; return; } if (CPDF_Stream* pStream = pContent->AsStream()) { m_nStreams = 0; m_pSingleStream.reset(new CPDF_StreamAcc); m_pSingleStream->LoadAllData(pStream, FALSE); } else if (CPDF_Array* pArray = pContent->AsArray()) { m_nStreams = pArray->GetCount(); if (m_nStreams) m_StreamArray.resize(m_nStreams); else m_Status = Done; } else { m_Status = Done; } } void CPDF_ContentParser::Start(CPDF_Form* pForm, CPDF_AllStates* pGraphicStates, const CFX_Matrix* pParentMatrix, CPDF_Type3Char* pType3Char, int level) { m_pType3Char = pType3Char; m_pObjectHolder = pForm; m_bForm = TRUE; CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrixBy("Matrix"); if (pGraphicStates) { form_matrix.Concat(pGraphicStates->m_CTM); } CPDF_Array* pBBox = pForm->m_pFormDict->GetArrayBy("BBox"); CFX_FloatRect form_bbox; CPDF_Path ClipPath; if (pBBox) { form_bbox = pBBox->GetRect(); ClipPath.New(); ClipPath.AppendRect(form_bbox.left, form_bbox.bottom, form_bbox.right, form_bbox.top); ClipPath.Transform(&form_matrix); if (pParentMatrix) { ClipPath.Transform(pParentMatrix); } form_bbox.Transform(&form_matrix); if (pParentMatrix) { form_bbox.Transform(pParentMatrix); } } CPDF_Dictionary* pResources = pForm->m_pFormDict->GetDictBy("Resources"); m_pParser.reset(new CPDF_StreamContentParser( pForm->m_pDocument, pForm->m_pPageResources, pForm->m_pResources, pParentMatrix, pForm, pResources, &form_bbox, pGraphicStates, level)); m_pParser->GetCurStates()->m_CTM = form_matrix; m_pParser->GetCurStates()->m_ParentMatrix = form_matrix; if (ClipPath) { m_pParser->GetCurStates()->m_ClipPath.AppendPath(ClipPath, FXFILL_WINDING, TRUE); } if (pForm->m_Transparency & PDFTRANS_GROUP) { m_pParser->GetCurStates()->m_GeneralState.MakePrivateCopy(); CPDF_GeneralStateData* pData = m_pParser->GetCurStates()->m_GeneralState.GetObject(); pData->m_BlendType = FXDIB_BLEND_NORMAL; pData->m_StrokeAlpha = 1.0f; pData->m_FillAlpha = 1.0f; pData->m_pSoftMask = nullptr; } m_nStreams = 0; m_pSingleStream.reset(new CPDF_StreamAcc); m_pSingleStream->LoadAllData(pForm->m_pFormStream, FALSE); m_pData = (uint8_t*)m_pSingleStream->GetData(); m_Size = m_pSingleStream->GetSize(); m_Status = ToBeContinued; m_InternalStage = STAGE_PARSE; m_CurrentOffset = 0; } void CPDF_ContentParser::Continue(IFX_Pause* pPause) { int steps = 0; while (m_Status == ToBeContinued) { if (m_InternalStage == STAGE_GETCONTENT) { if (m_CurrentOffset == m_nStreams) { if (!m_StreamArray.empty()) { FX_SAFE_UINT32 safeSize = 0; for (const auto& stream : m_StreamArray) { safeSize += stream->GetSize(); safeSize += 1; } if (!safeSize.IsValid()) { m_Status = Done; return; } m_Size = safeSize.ValueOrDie(); m_pData = FX_Alloc(uint8_t, m_Size); uint32_t pos = 0; for (const auto& stream : m_StreamArray) { FXSYS_memcpy(m_pData + pos, stream->GetData(), stream->GetSize()); pos += stream->GetSize(); m_pData[pos++] = ' '; } m_StreamArray.clear(); } else { m_pData = (uint8_t*)m_pSingleStream->GetData(); m_Size = m_pSingleStream->GetSize(); } m_InternalStage = STAGE_PARSE; m_CurrentOffset = 0; } else { CPDF_Array* pContent = m_pObjectHolder->m_pFormDict->GetArrayBy("Contents"); m_StreamArray[m_CurrentOffset].reset(new CPDF_StreamAcc); CPDF_Stream* pStreamObj = ToStream( pContent ? pContent->GetDirectObjectAt(m_CurrentOffset) : nullptr); m_StreamArray[m_CurrentOffset]->LoadAllData(pStreamObj, FALSE); m_CurrentOffset++; } } if (m_InternalStage == STAGE_PARSE) { if (!m_pParser) { m_pParser.reset(new CPDF_StreamContentParser( m_pObjectHolder->m_pDocument, m_pObjectHolder->m_pPageResources, nullptr, nullptr, m_pObjectHolder, m_pObjectHolder->m_pResources, &m_pObjectHolder->m_BBox, nullptr, 0)); m_pParser->GetCurStates()->m_ColorState.MakePrivateCopy(); m_pParser->GetCurStates()->m_ColorState->Default(); } if (m_CurrentOffset >= m_Size) { m_InternalStage = STAGE_CHECKCLIP; } else { m_CurrentOffset += m_pParser->Parse(m_pData + m_CurrentOffset, m_Size - m_CurrentOffset, PARSE_STEP_LIMIT); } } if (m_InternalStage == STAGE_CHECKCLIP) { if (m_pType3Char) { m_pType3Char->m_bColored = m_pParser->IsColored(); m_pType3Char->m_Width = FXSYS_round(m_pParser->GetType3Data()[0] * 1000); m_pType3Char->m_BBox.left = FXSYS_round(m_pParser->GetType3Data()[2] * 1000); m_pType3Char->m_BBox.bottom = FXSYS_round(m_pParser->GetType3Data()[3] * 1000); m_pType3Char->m_BBox.right = FXSYS_round(m_pParser->GetType3Data()[4] * 1000); m_pType3Char->m_BBox.top = FXSYS_round(m_pParser->GetType3Data()[5] * 1000); } for (auto& pObj : *m_pObjectHolder->GetPageObjectList()) { if (!pObj->m_ClipPath) continue; if (pObj->m_ClipPath.GetPathCount() != 1) continue; if (pObj->m_ClipPath.GetTextCount()) continue; CPDF_Path ClipPath = pObj->m_ClipPath.GetPath(0); if (!ClipPath.IsRect() || pObj->IsShading()) continue; CFX_FloatRect old_rect(ClipPath.GetPointX(0), ClipPath.GetPointY(0), ClipPath.GetPointX(2), ClipPath.GetPointY(2)); CFX_FloatRect obj_rect(pObj->m_Left, pObj->m_Bottom, pObj->m_Right, pObj->m_Top); if (old_rect.Contains(obj_rect)) pObj->m_ClipPath.Clear(); } m_Status = Done; return; } steps++; if (pPause && pPause->NeedToPauseNow()) { break; } } }