1 // Copyright 2014 PDFium Authors. All rights reserved. 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 "xfa/fde/cfde_txtedtpage.h" 8 9 #include <algorithm> 10 11 #include "third_party/base/ptr_util.h" 12 #include "third_party/base/stl_util.h" 13 #include "xfa/fde/cfde_txtedtbuf.h" 14 #include "xfa/fde/cfde_txtedtengine.h" 15 #include "xfa/fde/cfde_txtedtparag.h" 16 #include "xfa/fde/cfde_txtedttextset.h" 17 #include "xfa/fde/cfx_wordbreak.h" 18 #include "xfa/fde/ifde_txtedtengine.h" 19 #include "xfa/fde/ifde_txtedtpage.h" 20 21 namespace { 22 23 const double kTolerance = 0.1f; 24 25 } // namespace 26 27 IFDE_TxtEdtPage* IFDE_TxtEdtPage::Create(CFDE_TxtEdtEngine* pEngine, 28 int32_t nIndex) { 29 return new CFDE_TxtEdtPage(pEngine, nIndex); 30 } 31 32 CFDE_TxtEdtPage::CFDE_TxtEdtPage(CFDE_TxtEdtEngine* pEngine, int32_t nPageIndex) 33 : m_pEditEngine(pEngine), 34 m_pBgnParag(nullptr), 35 m_pEndParag(nullptr), 36 m_nRefCount(0), 37 m_nPageStart(-1), 38 m_nCharCount(0), 39 m_nPageIndex(nPageIndex), 40 m_bLoaded(false) { 41 } 42 43 CFDE_TxtEdtPage::~CFDE_TxtEdtPage() {} 44 45 CFDE_TxtEdtEngine* CFDE_TxtEdtPage::GetEngine() const { 46 return m_pEditEngine; 47 } 48 49 FDE_VISUALOBJTYPE CFDE_TxtEdtPage::GetType() { 50 return FDE_VISUALOBJ_Text; 51 } 52 53 CFX_RectF CFDE_TxtEdtPage::GetRect(const FDE_TEXTEDITPIECE& hVisualObj) { 54 return CFX_RectF(); 55 } 56 57 int32_t CFDE_TxtEdtPage::GetCharRect(int32_t nIndex, 58 CFX_RectF& rect, 59 bool bBBox) const { 60 ASSERT(m_nRefCount > 0); 61 ASSERT(nIndex >= 0 && nIndex < m_nCharCount); 62 if (m_nRefCount < 1) 63 return 0; 64 65 for (const auto& piece : m_Pieces) { 66 if (nIndex >= piece.nStart && nIndex < piece.nStart + piece.nCount) { 67 std::vector<CFX_RectF> rectArr = m_pTextSet->GetCharRects(&piece, bBBox); 68 rect = rectArr[nIndex - piece.nStart]; 69 return piece.nBidiLevel; 70 } 71 } 72 ASSERT(0); 73 return 0; 74 } 75 76 int32_t CFDE_TxtEdtPage::GetCharIndex(const CFX_PointF& fPoint, bool& bBefore) { 77 CFX_PointF ptF = fPoint; 78 NormalizePt2Rect(ptF, m_rtPageContents, kTolerance); 79 int32_t nCount = pdfium::CollectionSize<int32_t>(m_Pieces); 80 CFX_RectF rtLine; 81 int32_t nBgn = 0; 82 int32_t nEnd = 0; 83 bool bInLine = false; 84 int32_t i = 0; 85 for (i = 0; i < nCount; i++) { 86 const FDE_TEXTEDITPIECE* pPiece = &m_Pieces[i]; 87 if (!bInLine && 88 (pPiece->rtPiece.top <= ptF.y && pPiece->rtPiece.bottom() > ptF.y)) { 89 nBgn = nEnd = i; 90 rtLine = pPiece->rtPiece; 91 bInLine = true; 92 } else if (bInLine) { 93 if (pPiece->rtPiece.bottom() <= ptF.y || pPiece->rtPiece.top > ptF.y) { 94 nEnd = i - 1; 95 break; 96 } else { 97 rtLine.Union(pPiece->rtPiece); 98 } 99 } 100 } 101 NormalizePt2Rect(ptF, rtLine, kTolerance); 102 int32_t nCaret = 0; 103 FDE_TEXTEDITPIECE* pPiece = nullptr; 104 for (i = nBgn; i <= nEnd; i++) { 105 pPiece = &m_Pieces[i]; 106 nCaret = m_nPageStart + pPiece->nStart; 107 if (pPiece->rtPiece.Contains(ptF)) { 108 std::vector<CFX_RectF> rectArr = m_pTextSet->GetCharRects(pPiece, false); 109 int32_t nRtCount = pdfium::CollectionSize<int32_t>(rectArr); 110 for (int32_t j = 0; j < nRtCount; j++) { 111 if (rectArr[j].Contains(ptF)) { 112 nCaret = m_nPageStart + pPiece->nStart + j; 113 if (nCaret >= m_pEditEngine->GetTextBufLength()) { 114 bBefore = true; 115 return m_pEditEngine->GetTextBufLength(); 116 } 117 FX_WCHAR wChar = m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret); 118 if (wChar == L'\n' || wChar == L'\r') { 119 if (wChar == L'\n') { 120 if (m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret - 1) == 121 L'\r') { 122 nCaret--; 123 } 124 } 125 bBefore = true; 126 return nCaret; 127 } 128 if (ptF.x > ((rectArr[j].left + rectArr[j].right()) / 2)) { 129 bBefore = FX_IsOdd(pPiece->nBidiLevel); 130 } else { 131 bBefore = !FX_IsOdd(pPiece->nBidiLevel); 132 } 133 return nCaret; 134 } 135 } 136 } 137 } 138 bBefore = true; 139 return nCaret; 140 } 141 142 int32_t CFDE_TxtEdtPage::GetCharStart() const { 143 return m_nPageStart; 144 } 145 146 int32_t CFDE_TxtEdtPage::GetCharCount() const { 147 return m_nCharCount; 148 } 149 150 int32_t CFDE_TxtEdtPage::GetDisplayPos(const CFX_RectF& rtClip, 151 FXTEXT_CHARPOS*& pCharPos, 152 CFX_RectF* pBBox) const { 153 pCharPos = FX_Alloc(FXTEXT_CHARPOS, m_nCharCount); 154 int32_t nCharPosCount = 0; 155 FXTEXT_CHARPOS* pos = pCharPos; 156 for (const auto& piece : m_Pieces) { 157 if (!rtClip.IntersectWith(m_pTextSet->GetRect(piece))) 158 continue; 159 160 int32_t nCount = m_pTextSet->GetDisplayPos(piece, pos, false); 161 nCharPosCount += nCount; 162 pos += nCount; 163 } 164 if ((nCharPosCount * 5) < (m_nCharCount << 2)) { 165 FXTEXT_CHARPOS* pTemp = FX_Alloc(FXTEXT_CHARPOS, nCharPosCount); 166 FXSYS_memcpy(pTemp, pCharPos, sizeof(FXTEXT_CHARPOS) * nCharPosCount); 167 FX_Free(pCharPos); 168 pCharPos = pTemp; 169 } 170 return nCharPosCount; 171 } 172 173 void CFDE_TxtEdtPage::CalcRangeRectArray( 174 int32_t nStart, 175 int32_t nCount, 176 std::vector<CFX_RectF>* pRectFArr) const { 177 int32_t nEnd = nStart + nCount - 1; 178 bool bInRange = false; 179 for (const auto& piece : m_Pieces) { 180 if (!bInRange) { 181 if (nStart >= piece.nStart && nStart < piece.nStart + piece.nCount) { 182 int32_t nRangeEnd = piece.nCount - 1; 183 bool bEnd = false; 184 if (nEnd >= piece.nStart && nEnd < piece.nStart + piece.nCount) { 185 nRangeEnd = nEnd - piece.nStart; 186 bEnd = true; 187 } 188 std::vector<CFX_RectF> rcArr = m_pTextSet->GetCharRects(&piece, false); 189 CFX_RectF rectPiece = rcArr[nStart - piece.nStart]; 190 rectPiece.Union(rcArr[nRangeEnd]); 191 pRectFArr->push_back(rectPiece); 192 if (bEnd) 193 return; 194 195 bInRange = true; 196 } 197 } else { 198 if (nEnd >= piece.nStart && nEnd < piece.nStart + piece.nCount) { 199 std::vector<CFX_RectF> rcArr = m_pTextSet->GetCharRects(&piece, false); 200 CFX_RectF rectPiece = rcArr[0]; 201 rectPiece.Union(rcArr[nEnd - piece.nStart]); 202 pRectFArr->push_back(rectPiece); 203 return; 204 } 205 pRectFArr->push_back(piece.rtPiece); 206 } 207 } 208 } 209 210 int32_t CFDE_TxtEdtPage::SelectWord(const CFX_PointF& fPoint, int32_t& nCount) { 211 if (m_nRefCount < 0) { 212 return -1; 213 } 214 CFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf(); 215 bool bBefore; 216 int32_t nIndex = GetCharIndex(fPoint, bBefore); 217 if (nIndex == m_pEditEngine->GetTextBufLength()) { 218 nIndex = m_pEditEngine->GetTextBufLength() - 1; 219 } 220 if (nIndex < 0) { 221 return -1; 222 } 223 std::unique_ptr<CFX_WordBreak> pIter(new CFX_WordBreak); 224 pIter->Attach(new CFDE_TxtEdtBuf::Iterator(pBuf)); 225 pIter->SetAt(nIndex); 226 nCount = pIter->GetWordLength(); 227 return pIter->GetWordPos(); 228 } 229 230 bool CFDE_TxtEdtPage::IsLoaded(const CFX_RectF* pClipBox) { 231 return m_bLoaded; 232 } 233 234 int32_t CFDE_TxtEdtPage::LoadPage(const CFX_RectF* pClipBox, 235 IFX_Pause* pPause) { 236 if (m_nRefCount > 0) { 237 m_nRefCount++; 238 return m_nRefCount; 239 } 240 CFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf(); 241 const FDE_TXTEDTPARAMS* pParams = m_pEditEngine->GetEditParams(); 242 FX_WCHAR wcAlias = 0; 243 if (pParams->dwMode & FDE_TEXTEDITMODE_Password) { 244 wcAlias = m_pEditEngine->GetAliasChar(); 245 } 246 m_pIter.reset(new CFDE_TxtEdtBuf::Iterator(static_cast<CFDE_TxtEdtBuf*>(pBuf), 247 wcAlias)); 248 CFX_TxtBreak* pBreak = m_pEditEngine->GetTextBreak(); 249 pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 250 pBreak->ClearBreakPieces(); 251 int32_t nPageLineCount = m_pEditEngine->GetPageLineCount(); 252 int32_t nStartLine = nPageLineCount * m_nPageIndex; 253 int32_t nEndLine = std::min((nStartLine + nPageLineCount - 1), 254 (m_pEditEngine->GetLineCount() - 1)); 255 int32_t nPageStart, nPageEnd, nTemp, nBgnParag, nStartLineInParag, nEndParag, 256 nEndLineInParag; 257 nBgnParag = m_pEditEngine->Line2Parag(0, 0, nStartLine, nStartLineInParag); 258 m_pBgnParag = 259 static_cast<CFDE_TxtEdtParag*>(m_pEditEngine->GetParag(nBgnParag)); 260 m_pBgnParag->LoadParag(); 261 m_pBgnParag->GetLineRange(nStartLine - nStartLineInParag, nPageStart, nTemp); 262 nEndParag = m_pEditEngine->Line2Parag(nBgnParag, nStartLineInParag, nEndLine, 263 nEndLineInParag); 264 m_pEndParag = 265 static_cast<CFDE_TxtEdtParag*>(m_pEditEngine->GetParag(nEndParag)); 266 m_pEndParag->LoadParag(); 267 m_pEndParag->GetLineRange(nEndLine - nEndLineInParag, nPageEnd, nTemp); 268 nPageEnd += (nTemp - 1); 269 270 FX_FLOAT fLineStart = 0.0f; 271 FX_FLOAT fLineStep = pParams->fLineSpace; 272 FX_FLOAT fLinePos = fLineStart; 273 if (!m_pTextSet) 274 m_pTextSet = pdfium::MakeUnique<CFDE_TxtEdtTextSet>(this); 275 276 m_Pieces.clear(); 277 uint32_t dwBreakStatus = FX_TXTBREAK_None; 278 int32_t nPieceStart = 0; 279 280 m_CharWidths.resize(nPageEnd - nPageStart + 1, 0); 281 pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 282 pBreak->ClearBreakPieces(); 283 m_nPageStart = nPageStart; 284 m_nCharCount = nPageEnd - nPageStart + 1; 285 bool bReload = false; 286 FX_FLOAT fDefCharWidth = 0; 287 std::unique_ptr<IFX_CharIter> pIter(m_pIter->Clone()); 288 pIter->SetAt(nPageStart); 289 m_pIter->SetAt(nPageStart); 290 bool bFirstPiece = true; 291 do { 292 if (bReload) { 293 dwBreakStatus = pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 294 } else { 295 FX_WCHAR wAppend = pIter->GetChar(); 296 dwBreakStatus = pBreak->AppendChar(wAppend); 297 } 298 if (pIter->GetAt() == nPageEnd && dwBreakStatus < FX_TXTBREAK_LineBreak) { 299 dwBreakStatus = pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 300 } 301 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 302 int32_t nPieceCount = pBreak->CountBreakPieces(); 303 for (int32_t j = 0; j < nPieceCount; j++) { 304 const CFX_TxtPiece* pPiece = pBreak->GetBreakPiece(j); 305 FDE_TEXTEDITPIECE TxtEdtPiece; 306 FXSYS_memset(&TxtEdtPiece, 0, sizeof(FDE_TEXTEDITPIECE)); 307 TxtEdtPiece.nBidiLevel = pPiece->m_iBidiLevel; 308 TxtEdtPiece.nCount = pPiece->GetLength(); 309 TxtEdtPiece.nStart = nPieceStart; 310 TxtEdtPiece.dwCharStyles = pPiece->m_dwCharStyles; 311 if (FX_IsOdd(pPiece->m_iBidiLevel)) { 312 TxtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel; 313 } 314 FX_FLOAT fParaBreakWidth = 0.0f; 315 if (pPiece->m_dwStatus > FX_TXTBREAK_PieceBreak) { 316 FX_WCHAR wRtChar = pParams->wLineBreakChar; 317 if (TxtEdtPiece.nCount >= 2) { 318 FX_WCHAR wChar = pBuf->GetCharByIndex( 319 m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1); 320 FX_WCHAR wCharPre = pBuf->GetCharByIndex( 321 m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 2); 322 if (wChar == wRtChar) { 323 fParaBreakWidth += fDefCharWidth; 324 } 325 if (wCharPre == wRtChar) { 326 fParaBreakWidth += fDefCharWidth; 327 } 328 } else if (TxtEdtPiece.nCount >= 1) { 329 FX_WCHAR wChar = pBuf->GetCharByIndex( 330 m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1); 331 if (wChar == wRtChar) { 332 fParaBreakWidth += fDefCharWidth; 333 } 334 } 335 } 336 337 TxtEdtPiece.rtPiece.left = (FX_FLOAT)pPiece->m_iStartPos / 20000.0f; 338 TxtEdtPiece.rtPiece.top = fLinePos; 339 TxtEdtPiece.rtPiece.width = 340 (FX_FLOAT)pPiece->m_iWidth / 20000.0f + fParaBreakWidth; 341 TxtEdtPiece.rtPiece.height = pParams->fLineSpace; 342 343 if (bFirstPiece) { 344 m_rtPageContents = TxtEdtPiece.rtPiece; 345 bFirstPiece = false; 346 } else { 347 m_rtPageContents.Union(TxtEdtPiece.rtPiece); 348 } 349 nPieceStart += TxtEdtPiece.nCount; 350 m_Pieces.push_back(TxtEdtPiece); 351 for (int32_t k = 0; k < TxtEdtPiece.nCount; k++) { 352 CFX_Char* ptc = pPiece->GetCharPtr(k); 353 m_CharWidths[TxtEdtPiece.nStart + k] = ptc->m_iCharWidth; 354 } 355 } 356 fLinePos += fLineStep; 357 pBreak->ClearBreakPieces(); 358 } 359 if (pIter->GetAt() == nPageEnd && dwBreakStatus == FX_TXTBREAK_LineBreak) { 360 bReload = true; 361 pIter->Next(true); 362 } 363 } while (pIter->Next(false) && (pIter->GetAt() <= nPageEnd)); 364 if (m_rtPageContents.left != 0) { 365 FX_FLOAT fDelta = 0.0f; 366 if (m_rtPageContents.width < pParams->fPlateWidth) { 367 if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Right) { 368 fDelta = pParams->fPlateWidth - m_rtPageContents.width; 369 } else if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Center) { 370 if ((pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) && 371 m_nCharCount > 1) { 372 int32_t nCount = m_nCharCount - 1; 373 int32_t n = (m_pEditEngine->m_nLimit - nCount) / 2; 374 fDelta = (m_rtPageContents.width / nCount) * n; 375 } else { 376 fDelta = (pParams->fPlateWidth - m_rtPageContents.width) / 2; 377 } 378 } 379 } 380 FX_FLOAT fOffset = m_rtPageContents.left - fDelta; 381 for (auto& piece : m_Pieces) 382 piece.rtPiece.Offset(-fOffset, 0.0f); 383 384 m_rtPageContents.Offset(-fOffset, 0.0f); 385 } 386 if (m_pEditEngine->GetEditParams()->dwLayoutStyles & 387 FDE_TEXTEDITLAYOUT_LastLineHeight) { 388 m_rtPageContents.height -= pParams->fLineSpace - pParams->fFontSize; 389 m_Pieces.back().rtPiece.height = pParams->fFontSize; 390 } 391 m_nRefCount = 1; 392 m_bLoaded = true; 393 return 0; 394 } 395 396 void CFDE_TxtEdtPage::UnloadPage(const CFX_RectF* pClipBox) { 397 ASSERT(m_nRefCount > 0); 398 m_nRefCount--; 399 if (m_nRefCount != 0) 400 return; 401 402 m_Pieces.clear(); 403 m_pTextSet.reset(); 404 m_CharWidths.clear(); 405 if (m_pBgnParag) { 406 m_pBgnParag->UnloadParag(); 407 m_pBgnParag = nullptr; 408 } 409 if (m_pEndParag) { 410 m_pEndParag->UnloadParag(); 411 m_pEndParag = nullptr; 412 } 413 m_pIter.reset(); 414 } 415 416 const CFX_RectF& CFDE_TxtEdtPage::GetContentsBox() { 417 return m_rtPageContents; 418 } 419 420 FX_POSITION CFDE_TxtEdtPage::GetFirstPosition() { 421 if (m_Pieces.empty()) 422 return nullptr; 423 return (FX_POSITION)1; 424 } 425 426 FDE_TEXTEDITPIECE* CFDE_TxtEdtPage::GetNext(FX_POSITION& pos, 427 IFDE_VisualSet*& pVisualSet) { 428 if (!m_pTextSet) { 429 pos = nullptr; 430 return nullptr; 431 } 432 int32_t nPos = (int32_t)(uintptr_t)pos; 433 pVisualSet = m_pTextSet.get(); 434 if (nPos + 1 > pdfium::CollectionSize<int32_t>(m_Pieces)) 435 pos = nullptr; 436 else 437 pos = (FX_POSITION)(uintptr_t)(nPos + 1); 438 439 return &m_Pieces[nPos - 1]; 440 } 441 442 FX_WCHAR CFDE_TxtEdtPage::GetChar(const FDE_TEXTEDITPIECE* pIdentity, 443 int32_t index) const { 444 int32_t nIndex = m_nPageStart + pIdentity->nStart + index; 445 if (nIndex != m_pIter->GetAt()) 446 m_pIter->SetAt(nIndex); 447 448 FX_WCHAR wChar = m_pIter->GetChar(); 449 m_pIter->Next(); 450 return wChar; 451 } 452 453 int32_t CFDE_TxtEdtPage::GetWidth(const FDE_TEXTEDITPIECE* pIdentity, 454 int32_t index) const { 455 int32_t nWidth = m_CharWidths[pIdentity->nStart + index]; 456 return nWidth; 457 } 458 459 void CFDE_TxtEdtPage::NormalizePt2Rect(CFX_PointF& ptF, 460 const CFX_RectF& rtF, 461 FX_FLOAT fTolerance) const { 462 if (rtF.Contains(ptF)) 463 return; 464 if (ptF.x < rtF.left) 465 ptF.x = rtF.left; 466 else if (ptF.x >= rtF.right()) 467 ptF.x = rtF.right() - fTolerance; 468 469 if (ptF.y < rtF.top) 470 ptF.y = rtF.top; 471 else if (ptF.y >= rtF.bottom()) 472 ptF.y = rtF.bottom() - fTolerance; 473 } 474