void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get(); if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon) return; CFX_Matrix formMatrix = Obj.m_formMatrix; CPDF_Font* pFont = pTextObj->GetFont(); CFX_Matrix matrix = pTextObj->GetTextMatrix() * formMatrix; FPDFText_MarkedContent ePreMKC = PreMarkedContent(Obj); if (ePreMKC == FPDFText_MarkedContent::Done) { m_pPreTextObj = pTextObj; m_perMatrix = formMatrix; return; } GenerateCharacter result = GenerateCharacter::None; if (m_pPreTextObj) { result = ProcessInsertObject(pTextObj, formMatrix); if (result == GenerateCharacter::LineBreak) m_CurlineRect = Obj.m_pTextObj->GetRect(); else m_CurlineRect.Union(Obj.m_pTextObj->GetRect()); switch (result) { case GenerateCharacter::None: break; case GenerateCharacter::Space: { Optional<PAGECHAR_INFO> pGenerateChar = GenerateCharInfo(TEXT_SPACE_CHAR); if (pGenerateChar) { if (!formMatrix.IsIdentity()) pGenerateChar->m_Matrix = formMatrix; m_TempTextBuf.AppendChar(TEXT_SPACE_CHAR); m_TempCharList.push_back(*pGenerateChar); } break; } case GenerateCharacter::LineBreak: CloseTempLine(); if (m_TextBuf.GetSize()) { AppendGeneratedCharacter(TEXT_RETURN_CHAR, formMatrix); AppendGeneratedCharacter(TEXT_LINEFEED_CHAR, formMatrix); } break; case GenerateCharacter::Hyphen: if (pTextObj->CountChars() == 1) { CPDF_TextObjectItem item; pTextObj->GetCharInfo(0, &item); WideString wstrItem = pTextObj->GetFont()->UnicodeFromCharCode(item.m_CharCode); if (wstrItem.IsEmpty()) wstrItem += (wchar_t)item.m_CharCode; wchar_t curChar = wstrItem[0]; if (IsHyphenCode(curChar)) return; } while (m_TempTextBuf.GetSize() > 0 && m_TempTextBuf.AsStringView()[m_TempTextBuf.GetLength() - 1] == 0x20) { m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1); m_TempCharList.pop_back(); } PAGECHAR_INFO* charinfo = &m_TempCharList.back(); m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1); charinfo->m_Unicode = 0x2; charinfo->m_Flag = FPDFTEXT_CHAR_HYPHEN; m_TempTextBuf.AppendChar(0xfffe); break; } } else { m_CurlineRect = Obj.m_pTextObj->GetRect(); } if (ePreMKC == FPDFText_MarkedContent::Delay) { ProcessMarkedContent(Obj); m_pPreTextObj = pTextObj; m_perMatrix = formMatrix; return; } m_pPreTextObj = pTextObj; m_perMatrix = formMatrix; float baseSpace = CalculateBaseSpace(pTextObj, matrix); const bool bR2L = IsRightToLeft(*pTextObj, *pFont); const bool bIsBidiAndMirrorInverse = bR2L && (matrix.a * matrix.d - matrix.b * matrix.c) < 0; int32_t iBufStartAppend = m_TempTextBuf.GetLength(); int32_t iCharListStartAppend = pdfium::CollectionSize<int32_t>(m_TempCharList); float spacing = 0; const size_t nItems = pTextObj->CountItems(); for (size_t i = 0; i < nItems; ++i) { CPDF_TextObjectItem item; PAGECHAR_INFO charinfo; pTextObj->GetItemInfo(i, &item); if (item.m_CharCode == static_cast<uint32_t>(-1)) { WideString str = m_TempTextBuf.MakeString(); if (str.IsEmpty()) str = m_TextBuf.AsStringView(); if (str.IsEmpty() || str[str.GetLength() - 1] == TEXT_SPACE_CHAR) continue; float fontsize_h = pTextObj->m_TextState.GetFontSizeH(); spacing = -fontsize_h * item.m_Origin.x / 1000; continue; } float charSpace = pTextObj->m_TextState.GetCharSpace(); if (charSpace > 0.001) spacing += matrix.TransformDistance(charSpace); else if (charSpace < -0.001) spacing -= matrix.TransformDistance(fabs(charSpace)); spacing -= baseSpace; if (spacing && i > 0) { float fontsize_h = pTextObj->m_TextState.GetFontSizeH(); uint32_t space_charcode = pFont->CharCodeFromUnicode(' '); float threshold = 0; if (space_charcode != CPDF_Font::kInvalidCharCode) threshold = fontsize_h * pFont->GetCharWidthF(space_charcode) / 1000; if (threshold > fontsize_h / 3) threshold = 0; else threshold /= 2; if (threshold == 0) { threshold = static_cast<float>(GetCharWidth(item.m_CharCode, pFont)); threshold = NormalizeThreshold(threshold, 300, 500, 700); threshold = fontsize_h * threshold / 1000; } if (threshold && (spacing && spacing >= threshold)) { charinfo.m_Unicode = TEXT_SPACE_CHAR; charinfo.m_Flag = FPDFTEXT_CHAR_GENERATED; charinfo.m_pTextObj = pTextObj; charinfo.m_Index = m_TextBuf.GetLength(); m_TempTextBuf.AppendChar(TEXT_SPACE_CHAR); charinfo.m_CharCode = CPDF_Font::kInvalidCharCode; charinfo.m_Matrix = formMatrix; charinfo.m_Origin = matrix.Transform(item.m_Origin); charinfo.m_CharBox = CFX_FloatRect(charinfo.m_Origin.x, charinfo.m_Origin.y, charinfo.m_Origin.x, charinfo.m_Origin.y); m_TempCharList.push_back(charinfo); } if (item.m_CharCode == CPDF_Font::kInvalidCharCode) continue; } spacing = 0; WideString wstrItem = pFont->UnicodeFromCharCode(item.m_CharCode); bool bNoUnicode = false; if (wstrItem.IsEmpty() && item.m_CharCode) { wstrItem += static_cast<wchar_t>(item.m_CharCode); bNoUnicode = true; } charinfo.m_Index = -1; charinfo.m_CharCode = item.m_CharCode; charinfo.m_Flag = bNoUnicode ? FPDFTEXT_CHAR_UNUNICODE : FPDFTEXT_CHAR_NORMAL; charinfo.m_pTextObj = pTextObj; charinfo.m_Origin = matrix.Transform(item.m_Origin); const FX_RECT rect = charinfo.m_pTextObj->GetFont()->GetCharBBox(charinfo.m_CharCode); const float fFontSize = pTextObj->GetFontSize() / 1000; charinfo.m_CharBox.top = rect.top * fFontSize + item.m_Origin.y; charinfo.m_CharBox.left = rect.left * fFontSize + item.m_Origin.x; charinfo.m_CharBox.right = rect.right * fFontSize + item.m_Origin.x; charinfo.m_CharBox.bottom = rect.bottom * fFontSize + item.m_Origin.y; if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) < kSizeEpsilon) { charinfo.m_CharBox.top = charinfo.m_CharBox.bottom + pTextObj->GetFontSize(); } if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) < kSizeEpsilon) { charinfo.m_CharBox.right = charinfo.m_CharBox.left + pTextObj->GetCharWidth(charinfo.m_CharCode); } charinfo.m_CharBox = matrix.TransformRect(charinfo.m_CharBox); charinfo.m_Matrix = matrix; if (wstrItem.IsEmpty()) { charinfo.m_Unicode = 0; m_TempCharList.push_back(charinfo); m_TempTextBuf.AppendChar(0xfffe); continue; } int nTotal = wstrItem.GetLength(); bool bDel = false; const int count = std::min(pdfium::CollectionSize<int>(m_TempCharList), 7); float threshold = charinfo.m_Matrix.TransformXDistance( static_cast<float>(TEXT_CHARRATIO_GAPDELTA) * pTextObj->GetFontSize()); for (int n = pdfium::CollectionSize<int>(m_TempCharList); n > pdfium::CollectionSize<int>(m_TempCharList) - count; --n) { const PAGECHAR_INFO& charinfo1 = m_TempCharList[n - 1]; CFX_PointF diff = charinfo1.m_Origin - charinfo.m_Origin; if (charinfo1.m_CharCode == charinfo.m_CharCode && charinfo1.m_pTextObj->GetFont() == charinfo.m_pTextObj->GetFont() && fabs(diff.x) < threshold && fabs(diff.y) < threshold) { bDel = true; break; } } if (!bDel) { for (int nIndex = 0; nIndex < nTotal; ++nIndex) { charinfo.m_Unicode = wstrItem[nIndex]; if (charinfo.m_Unicode) { charinfo.m_Index = m_TextBuf.GetLength(); m_TempTextBuf.AppendChar(charinfo.m_Unicode); } else { m_TempTextBuf.AppendChar(0xfffe); } m_TempCharList.push_back(charinfo); } } else if (i == 0) { WideString str = m_TempTextBuf.MakeString(); if (!str.IsEmpty() && str[str.GetLength() - 1] == TEXT_SPACE_CHAR) { m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1); m_TempCharList.pop_back(); } } } if (bIsBidiAndMirrorInverse) SwapTempTextBuf(iCharListStartAppend, iBufStartAppend); }
//+--------------------------------------------------------------------------- // // Member: CDispScrollerPlus::GetNodeTransform // // Synopsis: Return a context that can be used to transform values // in the source coordinate system, producing values for the // destination coordinate system. // // Arguments: pContext returns context // source source coordinate system // destination destination coordinate system // // Notes: // //---------------------------------------------------------------------------- void CDispScrollerPlus::GetNodeTransform(CDispContext* pContext, COORDINATE_SYSTEM source, COORDINATE_SYSTEM destination) const { Assert(destination < source); BOOL fRightToLeft = IsRightToLeft(); CSize& offset = pContext->_offset; CRect& rcClip = pContext->_rcClip; switch(source) { case COORDSYS_NONFLOWCONTENT: case COORDSYS_CONTENT: // COORDSYS_CONTENT --> COORDSYS_CONTAINER { CDispInfo di((CDispExtras*) _extra); const CSize& topLeftBorder = di._prcBorderWidths->TopLeft().AsSize(); if(!fRightToLeft) { offset = topLeftBorder + _sizeScrollOffset; // add inset for flow child if(source == COORDSYS_CONTENT) { offset += *di._pInsetOffset; } } else { offset.cx = _rcContainer.Width() - di._prcBorderWidths->right + _sizeScrollOffset.cx; offset.cy = di._prcBorderWidths->top + _sizeScrollOffset.cy; } // all layers clip inside border regardless of layer type CSize sizeContained = _rcContainer.Size() - topLeftBorder - di._prcBorderWidths->BottomRight().AsSize(); // clip to scroll bars if(_fHasVScrollbar) { sizeContained.cx -= _sizeScrollbars.cx; } if(_fHasHScrollbar) { sizeContained.cy -= _sizeScrollbars.cy; } if(!fRightToLeft) { rcClip.SetRect(-_sizeScrollOffset.AsPoint(), sizeContained); } else { CPoint pt(_sizeScrollOffset.cx, -_sizeScrollOffset.cy); rcClip.SetRect(pt, sizeContained); rcClip.MirrorX(); } } if(destination == COORDSYS_CONTAINER) { break; } // fall thru to continue transformation case COORDSYS_CONTAINER: // COORDSYS_CONTAINER --> COORDSYS_PARENT if(source == COORDSYS_CONTAINER) { if(HasUserClip()) { rcClip = GetUserClip((CDispExtras*)_extra); } else { pContext->SetNoClip(); } offset = _rcContainer.TopLeft().AsSize(); } else { if(HasUserClip()) { CRect rcUserClip(GetUserClip((CDispExtras*)_extra)); rcUserClip.OffsetRect(-offset); // to source coords. rcClip.IntersectRect(rcUserClip); } offset += _rcContainer.TopLeft().AsSize(); } if(destination == COORDSYS_PARENT) { break; } // fall thru to continue transformation default: Assert(source != COORDSYS_GLOBAL); // COORDSYS_PARENT --> COORDSYS_GLOBAL if(source == COORDSYS_PARENT) { if(_pParentNode != NULL) { _pParentNode->GetNodeTransform( pContext, GetContentCoordinateSystem(), COORDSYS_GLOBAL); } else { pContext->SetToIdentity(); } } else { if(_pParentNode != NULL) { CDispContext globalContext; _pParentNode->GetNodeTransform( &globalContext, GetContentCoordinateSystem(), COORDSYS_GLOBAL); globalContext._rcClip.OffsetRect(-offset); // to source coords. rcClip.IntersectRect(globalContext._rcClip); offset += globalContext._offset; } } break; } }
//+--------------------------------------------------------------------------- // // Member: CDispScrollerPlus::CalcDispInfo // // Synopsis: Calculate clipping and positioning info for this node. // // Arguments: rcClip current clip rect // pdi display info structure // // Notes: // //---------------------------------------------------------------------------- void CDispScrollerPlus::CalcDispInfo(const CRect& rcClip, CDispInfo* pdi) const { // user clip = current clip INTERSECT optional user clip INTERSECT // container bounds, in content coordinates with scrolling // // border clip = current clip INTERSECT optional user clip INTERSECT // container bounds, in container coordinates (no scrolling) // // flow clip = current clip INTERSECT optional user clip INTERSECT // container bounds, in content coordinates with scrolling CDispInfo& di = *pdi; // notational convenience // set scrolling offset di._scrollOffset = _sizeScrollOffset; // content size di._sizeContent = _sizeContent; // offset to local coordinates _rcContainer.GetTopLeft(&(di._borderOffset.AsPoint())); // calc container clip intersect with container bounds di._rcContainerClip = rcClip; di._rcContainerClip.IntersectRect(_rcContainer); di._rcContainerClip.OffsetRect(-di._borderOffset); // calc rect inside border and scroll bars di._sizeBackground = _rcContainer.Size() - di._prcBorderWidths->TopLeft().AsSize() - di._prcBorderWidths->BottomRight().AsSize(); if(_fHasVScrollbar) { di._sizeBackground.cx -= _sizeScrollbars.cx; } if(_fHasHScrollbar) { di._sizeBackground.cy -= _sizeScrollbars.cy; } di._rcPositionedClip.SetRect( di._prcBorderWidths->TopLeft(), di._sizeBackground); di._contentOffset = di._prcBorderWidths->TopLeft().AsSize(); di._rcPositionedClip.IntersectRect(di._rcContainerClip); di._rcPositionedClip.OffsetRect(-di._contentOffset); // contents scroll if(!IsSet(CDispFlags::s_fixedBackground)) { di._rcPositionedClip.OffsetRect(-_sizeScrollOffset); di._rcBackgroundClip = di._rcPositionedClip; } else { di._rcBackgroundClip = di._rcPositionedClip; di._rcPositionedClip.OffsetRect(-_sizeScrollOffset); } // adjust for right to left coordinate system if(!IsRightToLeft()) { di._rcFlowClip.left = max(di._rcPositionedClip.left, di._pInsetOffset->cx); di._rcFlowClip.right = di._rcPositionedClip.right; } else { di._contentOffset.cx = di._sizeBackground.cx + di._prcBorderWidths->left; if(_fHasVScrollbar) { di._contentOffset.cx += _sizeScrollbars.cx; } di._rcPositionedClip.OffsetX(-di._sizeBackground.cx); di._rcBackgroundClip.OffsetX(-di._sizeBackground.cx); di._rcFlowClip.left = di._rcPositionedClip.left; di._rcFlowClip.right = min(di._rcPositionedClip.right, di._pInsetOffset->cx); } di._rcFlowClip.top = max(di._rcPositionedClip.top, di._pInsetOffset->cy); di._rcFlowClip.bottom = di._rcPositionedClip.bottom; di._rcFlowClip.OffsetRect(-*di._pInsetOffset); // size of background is big enough to fill background and content di._sizeBackground.Max(di._sizeContent); }
//+--------------------------------------------------------------------------- // // Member: CDispScrollerPlus::GetNodeTransform // // Synopsis: Return an offset that can be added to points in the source // coordinate system, producing values for the destination // coordinate system. // // Arguments: pOffset returns offset value // source source coordinate system // destination destination coordinate system // // Notes: // //---------------------------------------------------------------------------- void CDispScrollerPlus::GetNodeTransform(CSize* pOffset, COORDINATE_SYSTEM source, COORDINATE_SYSTEM destination) const { Assert(destination < source); BOOL fRightToLeft = IsRightToLeft(); *pOffset = _afxGlobalData._Zero.size; switch(source) { case COORDSYS_NONFLOWCONTENT: case COORDSYS_CONTENT: // COORDSYS_CONTENT --> COORDSYS_CONTAINER { CDispInfo di((CDispExtras*)_extra); if(!fRightToLeft) { *pOffset = di._prcBorderWidths->TopLeft().AsSize() + _sizeScrollOffset; // add inset for flow child if(source == COORDSYS_CONTENT) { *pOffset += *di._pInsetOffset; } } else { pOffset->cx = _rcContainer.Width() - di._prcBorderWidths->right + _sizeScrollOffset.cx; pOffset->cy = di._prcBorderWidths->top + _sizeScrollOffset.cy; // add inset for flow child if(source == COORDSYS_CONTENT) { pOffset->cx -= di._pInsetOffset->cx; pOffset->cy += di._pInsetOffset->cy; } } } if(destination == COORDSYS_CONTAINER) { break; } // fall thru to continue transformation case COORDSYS_CONTAINER: // COORDSYS_CONTAINER --> COORDSYS_PARENT *pOffset += _rcContainer.TopLeft().AsSize(); if(destination == COORDSYS_PARENT) { break; } // fall thru to continue transformation default: Assert(source != COORDSYS_GLOBAL); // COORDSYS_PARENT --> COORDSYS_GLOBAL if(_pParentNode != NULL) { CSize globalOffset; _pParentNode->GetNodeTransform( &globalOffset, GetContentCoordinateSystem(), COORDSYS_GLOBAL); *pOffset += globalOffset; } break; } }