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 <algorithm> 8 9 #include "xfa/src/foxitlib.h" 10 #include "fde_textout.h" 11 IFDE_TextOut* IFDE_TextOut::Create() { 12 return new CFDE_TextOut; 13 } 14 CFDE_TextOut::CFDE_TextOut() 15 : m_pFont(NULL), 16 m_fFontSize(12.0f), 17 m_fLineSpace(m_fFontSize), 18 m_fLinePos(0.0f), 19 m_fTolerance(0.0f), 20 m_iAlignment(0), 21 m_iTxtBkAlignment(0), 22 m_pCharWidths(NULL), 23 m_iChars(0), 24 m_pEllCharWidths(NULL), 25 m_iEllChars(0), 26 m_wParagraphBkChar(L'\n'), 27 m_TxtColor(0xFF000000), 28 m_dwStyles(0), 29 m_dwTxtBkStyles(0), 30 m_bElliChanged(FALSE), 31 m_iEllipsisWidth(0), 32 m_ttoLines(5), 33 m_iCurLine(0), 34 m_iCurPiece(0), 35 m_iTotalLines(0), 36 m_pCharPos(NULL), 37 m_iCharPosSize(0), 38 m_pRenderDevice(NULL) { 39 m_pTxtBreak = IFX_TxtBreak::Create(FX_TXTBREAKPOLICY_None); 40 FXSYS_assert(m_pTxtBreak != NULL); 41 m_Matrix.SetIdentity(); 42 m_rtClip.Reset(); 43 m_rtLogicClip.Reset(); 44 } 45 CFDE_TextOut::~CFDE_TextOut() { 46 if (m_pTxtBreak) { 47 m_pTxtBreak->Release(); 48 } 49 FX_Free(m_pCharWidths); 50 FX_Free(m_pEllCharWidths); 51 if (m_pRenderDevice) { 52 m_pRenderDevice->Release(); 53 } 54 FX_Free(m_pCharPos); 55 m_ttoLines.RemoveAll(); 56 } 57 void CFDE_TextOut::SetFont(IFX_Font* pFont) { 58 FXSYS_assert(pFont); 59 m_pFont = pFont; 60 m_pTxtBreak->SetFont(pFont); 61 } 62 void CFDE_TextOut::SetFontSize(FX_FLOAT fFontSize) { 63 FXSYS_assert(fFontSize > 0); 64 m_fFontSize = fFontSize; 65 m_pTxtBreak->SetFontSize(fFontSize); 66 } 67 void CFDE_TextOut::SetTextColor(FX_ARGB color) { 68 m_TxtColor = color; 69 } 70 void CFDE_TextOut::SetStyles(FX_DWORD dwStyles) { 71 m_dwStyles = dwStyles; 72 m_dwTxtBkStyles = 0; 73 if (dwStyles & FDE_TTOSTYLE_SingleLine) { 74 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_SingleLine; 75 } 76 if (dwStyles & FDE_TTOSTYLE_ExpandTab) { 77 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ExpandTab; 78 } 79 if (dwStyles & FDE_TTOSTYLE_ArabicShapes) { 80 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicShapes; 81 } 82 if (dwStyles & FDE_TTOSTYLE_RTL) { 83 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_RTLReadingOrder; 84 } 85 if (dwStyles & FDE_TTOSTYLE_ArabicContext) { 86 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicContext; 87 } 88 if (dwStyles & FDE_TTOSTYLE_VerticalLayout) { 89 m_dwTxtBkStyles |= 90 (FX_TXTLAYOUTSTYLE_VerticalChars | FX_TXTLAYOUTSTYLE_VerticalLayout); 91 } 92 m_pTxtBreak->SetLayoutStyles(m_dwTxtBkStyles); 93 } 94 void CFDE_TextOut::SetTabWidth(FX_FLOAT fTabWidth) { 95 FXSYS_assert(fTabWidth > 1.0f); 96 m_pTxtBreak->SetTabWidth(fTabWidth, FALSE); 97 } 98 void CFDE_TextOut::SetEllipsisString(const CFX_WideString& wsEllipsis) { 99 m_bElliChanged = TRUE; 100 m_wsEllipsis = wsEllipsis; 101 } 102 void CFDE_TextOut::SetParagraphBreakChar(FX_WCHAR wch) { 103 m_wParagraphBkChar = wch; 104 m_pTxtBreak->SetParagraphBreakChar(wch); 105 } 106 void CFDE_TextOut::SetAlignment(int32_t iAlignment) { 107 m_iAlignment = iAlignment; 108 switch (m_iAlignment) { 109 case FDE_TTOALIGNMENT_TopCenter: 110 case FDE_TTOALIGNMENT_Center: 111 case FDE_TTOALIGNMENT_BottomCenter: 112 m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Center; 113 break; 114 case FDE_TTOALIGNMENT_TopRight: 115 case FDE_TTOALIGNMENT_CenterRight: 116 case FDE_TTOALIGNMENT_BottomRight: 117 m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Right; 118 break; 119 default: 120 m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Left; 121 break; 122 } 123 m_pTxtBreak->SetAlignment(m_iTxtBkAlignment); 124 } 125 void CFDE_TextOut::SetLineSpace(FX_FLOAT fLineSpace) { 126 FXSYS_assert(fLineSpace > 1.0f); 127 m_fLineSpace = fLineSpace; 128 } 129 void CFDE_TextOut::SetDIBitmap(CFX_DIBitmap* pDIB) { 130 FXSYS_assert(pDIB != NULL); 131 if (m_pRenderDevice != NULL) { 132 m_pRenderDevice->Release(); 133 } 134 m_pRenderDevice = IFDE_RenderDevice::Create(pDIB); 135 } 136 void CFDE_TextOut::SetRenderDevice(CFX_RenderDevice* pDevice) { 137 FXSYS_assert(pDevice != NULL); 138 if (m_pRenderDevice != NULL) { 139 m_pRenderDevice->Release(); 140 } 141 m_pRenderDevice = IFDE_RenderDevice::Create(pDevice); 142 } 143 void CFDE_TextOut::SetClipRect(const CFX_Rect& rtClip) { 144 m_rtClip.Set((FX_FLOAT)rtClip.left, (FX_FLOAT)rtClip.top, 145 (FX_FLOAT)rtClip.Width(), (FX_FLOAT)rtClip.Height()); 146 } 147 void CFDE_TextOut::SetClipRect(const CFX_RectF& rtClip) { 148 m_rtClip = rtClip; 149 } 150 void CFDE_TextOut::SetLogicClipRect(const CFX_RectF& rtClip) { 151 m_rtLogicClip = rtClip; 152 } 153 void CFDE_TextOut::SetMatrix(const CFX_Matrix& matrix) { 154 m_Matrix = matrix; 155 } 156 void CFDE_TextOut::SetLineBreakTolerance(FX_FLOAT fTolerance) { 157 m_fTolerance = fTolerance; 158 m_pTxtBreak->SetLineBreakTolerance(m_fTolerance); 159 } 160 int32_t CFDE_TextOut::GetTotalLines() { 161 return m_iTotalLines; 162 } 163 void CFDE_TextOut::CalcSize(const FX_WCHAR* pwsStr, 164 int32_t iLength, 165 CFX_Size& size) { 166 CFX_RectF rtText; 167 rtText.Set(0.0f, 0.0f, (FX_FLOAT)size.x, (FX_FLOAT)size.y); 168 CalcSize(pwsStr, iLength, rtText); 169 size.x = (int32_t)rtText.Width(); 170 size.y = (int32_t)rtText.Height(); 171 } 172 void CFDE_TextOut::CalcSize(const FX_WCHAR* pwsStr, 173 int32_t iLength, 174 CFX_SizeF& size) { 175 CFX_RectF rtText; 176 rtText.Set(0.0f, 0.0f, size.x, size.y); 177 CalcSize(pwsStr, iLength, rtText); 178 size.x = rtText.Width(); 179 size.y = rtText.Height(); 180 } 181 void CFDE_TextOut::CalcSize(const FX_WCHAR* pwsStr, 182 int32_t iLength, 183 CFX_Rect& rect) { 184 CFX_RectF rtText; 185 rtText.Set((FX_FLOAT)rect.left, (FX_FLOAT)rect.top, (FX_FLOAT)rect.Width(), 186 (FX_FLOAT)rect.Height()); 187 CalcSize(pwsStr, iLength, rtText); 188 rect.Set((int32_t)rtText.left, (int32_t)rtText.top, (int32_t)rtText.Width(), 189 (int32_t)rtText.Height()); 190 } 191 void CFDE_TextOut::CalcSize(const FX_WCHAR* pwsStr, 192 int32_t iLength, 193 CFX_RectF& rect) { 194 if (pwsStr == NULL || iLength < 1) { 195 rect.width = 0.0f; 196 rect.height = 0.0f; 197 } else { 198 CFX_Matrix rm; 199 rm.SetReverse(m_Matrix); 200 rm.TransformRect(rect); 201 CalcTextSize(pwsStr, iLength, rect); 202 m_Matrix.TransformRect(rect); 203 } 204 } 205 void CFDE_TextOut::CalcLogicSize(const FX_WCHAR* pwsStr, 206 int32_t iLength, 207 CFX_SizeF& size) { 208 CFX_RectF rtText; 209 rtText.Set(0.0f, 0.0f, size.x, size.y); 210 CalcLogicSize(pwsStr, iLength, rtText); 211 size.x = rtText.Width(); 212 size.y = rtText.Height(); 213 } 214 void CFDE_TextOut::CalcLogicSize(const FX_WCHAR* pwsStr, 215 int32_t iLength, 216 CFX_RectF& rect) { 217 if (pwsStr == NULL || iLength < 1) { 218 rect.width = 0.0f; 219 rect.height = 0.0f; 220 } else { 221 CalcTextSize(pwsStr, iLength, rect); 222 } 223 } 224 void CFDE_TextOut::CalcTextSize(const FX_WCHAR* pwsStr, 225 int32_t iLength, 226 CFX_RectF& rect) { 227 FXSYS_assert(m_pFont != NULL && m_fFontSize >= 1.0f); 228 SetLineWidth(rect); 229 m_iTotalLines = 0; 230 const FX_WCHAR* pStr = pwsStr; 231 FX_BOOL bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey); 232 FX_BOOL bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 233 FX_FLOAT fWidth = 0.0f; 234 FX_FLOAT fHeight = 0.0f; 235 FX_FLOAT fStartPos = bVertical ? rect.bottom() : rect.right(); 236 FX_DWORD dwBreakStatus = 0; 237 FX_WCHAR wPreChar = 0; 238 FX_WCHAR wch; 239 FX_WCHAR wBreak = 0; 240 while (iLength-- > 0) { 241 wch = *pStr++; 242 if (wBreak == 0 && (wch == L'\n' || wch == L'\r')) { 243 wBreak = wch; 244 m_pTxtBreak->SetParagraphBreakChar(wch); 245 } 246 if (bHotKey && wch == L'&' && wPreChar != L'&') { 247 wPreChar = wch; 248 continue; 249 } 250 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 251 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 252 RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight); 253 } 254 wPreChar = 0; 255 } 256 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 257 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 258 RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight); 259 } 260 m_pTxtBreak->Reset(); 261 FX_FLOAT fInc = rect.Height() - fHeight; 262 if (bVertical) { 263 fInc = rect.Width() - fHeight; 264 } 265 if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft && 266 m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) { 267 fInc /= 2.0f; 268 } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) { 269 fInc = 0.0f; 270 } 271 if (bVertical) { 272 rect.top += fStartPos; 273 rect.left += fInc; 274 rect.width = fHeight; 275 rect.height = std::min(fWidth, rect.Height()); 276 } else { 277 rect.left += fStartPos; 278 rect.top += fInc; 279 rect.width = std::min(fWidth, rect.Width()); 280 rect.height = fHeight; 281 if (m_dwStyles & FDE_TTOSTYLE_LastLineHeight) { 282 rect.height -= m_fLineSpace - m_fFontSize; 283 } 284 } 285 } 286 void CFDE_TextOut::SetLineWidth(CFX_RectF& rect) { 287 if ((m_dwStyles & FDE_TTOSTYLE_SingleLine) == 0) { 288 FX_FLOAT fLineWidth = 0.0f; 289 if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) { 290 if (rect.Height() < 1.0f) { 291 rect.height = m_fFontSize * 1000.0f; 292 } 293 fLineWidth = rect.Height(); 294 } else { 295 if (rect.Width() < 1.0f) { 296 rect.width = m_fFontSize * 1000.0f; 297 } 298 fLineWidth = rect.Width(); 299 } 300 m_pTxtBreak->SetLineWidth(fLineWidth); 301 } 302 } 303 FX_BOOL CFDE_TextOut::RetrieveLineWidth(FX_DWORD dwBreakStatus, 304 FX_FLOAT& fStartPos, 305 FX_FLOAT& fWidth, 306 FX_FLOAT& fHeight) { 307 if (dwBreakStatus <= FX_TXTBREAK_PieceBreak) { 308 return FALSE; 309 } 310 FX_FLOAT fLineStep = 311 (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 312 FX_BOOL bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap); 313 FX_FLOAT fLineWidth = 0.0f; 314 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 315 for (int32_t i = 0; i < iCount; i++) { 316 const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i); 317 fLineWidth += (FX_FLOAT)pPiece->m_iWidth / 20000.0f; 318 fStartPos = std::min(fStartPos, (FX_FLOAT)pPiece->m_iStartPos / 20000.0f); 319 } 320 m_pTxtBreak->ClearBreakPieces(); 321 if (dwBreakStatus == FX_TXTBREAK_ParagraphBreak) { 322 m_pTxtBreak->Reset(); 323 } 324 if (!bLineWrap && dwBreakStatus == FX_TXTBREAK_LineBreak) { 325 fWidth += fLineWidth; 326 } else { 327 fWidth = std::max(fWidth, fLineWidth); 328 fHeight += fLineStep; 329 } 330 m_iTotalLines++; 331 return TRUE; 332 } 333 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 334 int32_t iLength, 335 int32_t x, 336 int32_t y) { 337 CFX_RectF rtText; 338 rtText.Set((FX_FLOAT)x, (FX_FLOAT)y, m_fFontSize * 1000.0f, 339 m_fFontSize * 1000.0f); 340 DrawText(pwsStr, iLength, rtText); 341 } 342 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 343 int32_t iLength, 344 FX_FLOAT x, 345 FX_FLOAT y) { 346 CFX_RectF rtText; 347 rtText.Set(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f); 348 DrawText(pwsStr, iLength, rtText); 349 } 350 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 351 int32_t iLength, 352 const CFX_Rect& rect) { 353 CFX_RectF rtText; 354 rtText.Set((FX_FLOAT)rect.left, (FX_FLOAT)rect.top, (FX_FLOAT)rect.width, 355 (FX_FLOAT)rect.height); 356 DrawText(pwsStr, iLength, rtText); 357 } 358 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 359 int32_t iLength, 360 const CFX_RectF& rect) { 361 CFX_RectF rtText; 362 rtText.Set(rect.left, rect.top, rect.width, rect.height); 363 CFX_Matrix rm; 364 rm.SetReverse(m_Matrix); 365 rm.TransformRect(rtText); 366 DrawText(pwsStr, iLength, rtText, m_rtClip); 367 } 368 void CFDE_TextOut::DrawLogicText(const FX_WCHAR* pwsStr, 369 int32_t iLength, 370 FX_FLOAT x, 371 FX_FLOAT y) { 372 CFX_RectF rtText; 373 rtText.Set(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f); 374 DrawLogicText(pwsStr, iLength, rtText); 375 } 376 void CFDE_TextOut::DrawLogicText(const FX_WCHAR* pwsStr, 377 int32_t iLength, 378 const CFX_RectF& rect) { 379 CFX_RectF rtClip; 380 rtClip.Set(m_rtLogicClip.left, m_rtLogicClip.top, m_rtLogicClip.width, 381 m_rtLogicClip.height); 382 m_Matrix.TransformRect(rtClip); 383 DrawText(pwsStr, iLength, rect, rtClip); 384 } 385 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 386 int32_t iLength, 387 const CFX_RectF& rect, 388 const CFX_RectF& rtClip) { 389 FXSYS_assert(m_pFont != NULL && m_fFontSize >= 1.0f); 390 if (pwsStr == NULL || iLength < 1) { 391 return; 392 } 393 if (rect.width < m_fFontSize || rect.height < m_fFontSize) { 394 return; 395 } 396 FX_FLOAT fLineWidth = rect.width; 397 if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) { 398 fLineWidth = rect.height; 399 } 400 m_pTxtBreak->SetLineWidth(fLineWidth); 401 m_ttoLines.RemoveAll(TRUE); 402 m_wsText.Empty(); 403 LoadText(pwsStr, iLength, rect); 404 if (m_dwStyles & FDE_TTOSTYLE_Ellipsis) { 405 ReplaceWidthEllipsis(); 406 } 407 Reload(rect); 408 DoAlignment(rect); 409 OnDraw(rtClip); 410 } 411 void CFDE_TextOut::ExpandBuffer(int32_t iSize, int32_t iType) { 412 switch (iType) { 413 case 0: 414 if (!m_pCharWidths) { 415 m_pCharWidths = FX_Alloc(int32_t, iSize); 416 m_iChars = iSize; 417 } else if (m_iChars < iSize) { 418 m_pCharWidths = FX_Realloc(int32_t, m_pCharWidths, iSize); 419 m_iChars = iSize; 420 } 421 FXSYS_memset(m_pCharWidths, 0, iSize * sizeof(int32_t)); 422 break; 423 case 1: 424 if (!m_pEllCharWidths) { 425 m_pEllCharWidths = FX_Alloc(int32_t, iSize); 426 m_iEllChars = iSize; 427 } else if (m_iEllChars < iSize) { 428 m_pEllCharWidths = FX_Realloc(int32_t, m_pEllCharWidths, iSize); 429 m_iEllChars = iSize; 430 } 431 FXSYS_memset(m_pEllCharWidths, 0, iSize * sizeof(int32_t)); 432 break; 433 case 2: 434 if (m_pCharPos == NULL) { 435 m_pCharPos = FX_Alloc(FXTEXT_CHARPOS, iSize); 436 m_iCharPosSize = iSize; 437 } else if (m_iCharPosSize < iSize) { 438 m_pCharPos = FX_Realloc(FXTEXT_CHARPOS, m_pCharPos, iSize); 439 m_iCharPosSize = iSize; 440 } 441 break; 442 } 443 } 444 void CFDE_TextOut::LoadEllipsis() { 445 if (!m_bElliChanged) { 446 return; 447 } 448 m_bElliChanged = FALSE; 449 m_iEllipsisWidth = 0; 450 int32_t iLength = m_wsEllipsis.GetLength(); 451 if (iLength < 1) { 452 return; 453 } 454 ExpandBuffer(iLength, 1); 455 const FX_WCHAR* pStr = (const FX_WCHAR*)m_wsEllipsis; 456 int32_t* pCharWidths = m_pEllCharWidths; 457 FX_DWORD dwBreakStatus; 458 FX_WCHAR wch; 459 while (iLength-- > 0) { 460 wch = *pStr++; 461 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 462 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 463 RetrieveEllPieces(pCharWidths); 464 } 465 } 466 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 467 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 468 RetrieveEllPieces(pCharWidths); 469 } 470 m_pTxtBreak->Reset(); 471 } 472 void CFDE_TextOut::RetrieveEllPieces(int32_t*& pCharWidths) { 473 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 474 CFX_Char* pTC; 475 for (int32_t i = 0; i < iCount; i++) { 476 const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i); 477 int32_t iPieceChars = pPiece->GetLength(); 478 for (int32_t j = 0; j < iPieceChars; j++) { 479 pTC = pPiece->GetCharPtr(j); 480 if (pTC->m_iCharWidth <= 0) { 481 *pCharWidths = 0; 482 } else { 483 *pCharWidths = pTC->m_iCharWidth; 484 } 485 m_iEllipsisWidth += *pCharWidths; 486 pCharWidths++; 487 } 488 } 489 m_pTxtBreak->ClearBreakPieces(); 490 } 491 void CFDE_TextOut::LoadText(const FX_WCHAR* pwsStr, 492 int32_t iLength, 493 const CFX_RectF& rect) { 494 FX_WCHAR* pStr = m_wsText.GetBuffer(iLength); 495 int32_t iTxtLength = iLength; 496 ExpandBuffer(iTxtLength, 0); 497 FX_BOOL bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey); 498 FX_BOOL bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 499 FX_BOOL bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap); 500 FX_FLOAT fLineStep = 501 (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 502 FX_FLOAT fLineStop = bVertical ? rect.left : rect.bottom(); 503 m_fLinePos = bVertical ? rect.right() : rect.top; 504 if (bVertical) { 505 fLineStep = -fLineStep; 506 } 507 m_hotKeys.RemoveAll(); 508 int32_t iStartChar = 0; 509 int32_t iChars = 0; 510 int32_t iPieceWidths = 0; 511 FX_DWORD dwBreakStatus; 512 FX_WCHAR wch; 513 FX_BOOL bRet = FALSE; 514 while (iTxtLength-- > 0) { 515 wch = *pwsStr++; 516 if (wch == L'&' && bHotKey && (pStr - 1) != NULL && *(pStr - 1) != L'&') { 517 if (iTxtLength > 0) { 518 m_hotKeys.Add(iChars); 519 } 520 continue; 521 } 522 *pStr++ = wch; 523 iChars++; 524 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 525 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 526 FX_BOOL bEndofLine = 527 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, FALSE, rect); 528 if (bEndofLine && (bLineWrap || (dwBreakStatus > FX_TXTBREAK_LineBreak && 529 !bLineWrap))) { 530 iPieceWidths = 0; 531 m_iCurLine++; 532 m_fLinePos += fLineStep; 533 } 534 if ((bVertical && m_fLinePos + fLineStep < fLineStop) || 535 (!bVertical && m_fLinePos + fLineStep > fLineStop)) { 536 int32_t iCurLine = m_iCurLine; 537 if (bEndofLine) { 538 iCurLine--; 539 } 540 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(iCurLine); 541 pLine->m_bNewReload = TRUE; 542 bRet = TRUE; 543 break; 544 } 545 } 546 } 547 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 548 if (dwBreakStatus > FX_TXTBREAK_PieceBreak && !bRet) { 549 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, FALSE, rect); 550 } 551 m_pTxtBreak->ClearBreakPieces(); 552 m_pTxtBreak->Reset(); 553 m_wsText.ReleaseBuffer(iLength); 554 } 555 FX_BOOL CFDE_TextOut::RetriecePieces(FX_DWORD dwBreakStatus, 556 int32_t& iStartChar, 557 int32_t& iPieceWidths, 558 FX_BOOL bReload, 559 const CFX_RectF& rect) { 560 FX_BOOL bSingleLine = !!(m_dwStyles & FDE_TTOSTYLE_SingleLine); 561 FX_BOOL bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap); 562 FX_BOOL bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 563 FX_FLOAT fLineStep = 564 (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 565 if (bVertical) { 566 fLineStep = -fLineStep; 567 } 568 CFX_Char* pTC = NULL; 569 FX_BOOL bNeedReload = FALSE; 570 FX_FLOAT fLineWidth = bVertical ? rect.Height() : rect.Width(); 571 int32_t iLineWidth = FXSYS_round(fLineWidth * 20000.0f); 572 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 573 for (int32_t i = 0; i < iCount; i++) { 574 const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i); 575 int32_t iPieceChars = pPiece->GetLength(); 576 int32_t iChar = iStartChar; 577 int32_t iWidth = 0; 578 int32_t j = 0; 579 for (; j < iPieceChars; j++) { 580 pTC = pPiece->GetCharPtr(j); 581 int32_t iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0; 582 if (bSingleLine || !bLineWrap) { 583 if (iLineWidth - iPieceWidths - iWidth < iCurCharWidth) { 584 bNeedReload = TRUE; 585 break; 586 } 587 } 588 iWidth += iCurCharWidth; 589 m_pCharWidths[iChar++] = iCurCharWidth; 590 } 591 if (j == 0 && !bReload) { 592 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(m_iCurLine); 593 pLine->m_bNewReload = TRUE; 594 } else if (j > 0) { 595 CFX_RectF rtPiece; 596 if (bVertical) { 597 rtPiece.left = m_fLinePos; 598 rtPiece.top = rect.top + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f; 599 rtPiece.width = fLineStep; 600 rtPiece.height = iWidth / 20000.0f; 601 } else { 602 rtPiece.left = rect.left + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f; 603 rtPiece.top = m_fLinePos; 604 rtPiece.width = iWidth / 20000.0f; 605 rtPiece.height = fLineStep; 606 } 607 FDE_TTOPIECE ttoPiece; 608 ttoPiece.iStartChar = iStartChar; 609 ttoPiece.iChars = j; 610 ttoPiece.rtPiece = rtPiece; 611 ttoPiece.dwCharStyles = pPiece->m_dwCharStyles; 612 if (FX_IsOdd(pPiece->m_iBidiLevel)) { 613 ttoPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel; 614 } 615 AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1)); 616 } 617 iStartChar += iPieceChars; 618 iPieceWidths += iWidth; 619 } 620 m_pTxtBreak->ClearBreakPieces(); 621 FX_BOOL bRet = bSingleLine || bLineWrap || (!bLineWrap && bNeedReload) || 622 dwBreakStatus == FX_TXTBREAK_ParagraphBreak; 623 return bRet; 624 } 625 void CFDE_TextOut::AppendPiece(const FDE_TTOPIECE& ttoPiece, 626 FX_BOOL bNeedReload, 627 FX_BOOL bEnd) { 628 if (m_iCurLine >= m_ttoLines.GetSize()) { 629 CFDE_TTOLine ttoLine; 630 ttoLine.m_bNewReload = bNeedReload; 631 m_iCurPiece = ttoLine.AddPiece(m_iCurPiece, ttoPiece); 632 m_iCurLine = m_ttoLines.Add(ttoLine); 633 } else { 634 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(m_iCurLine); 635 pLine->m_bNewReload = bNeedReload; 636 m_iCurPiece = pLine->AddPiece(m_iCurPiece, ttoPiece); 637 if (bEnd) { 638 int32_t iPieces = pLine->GetSize(); 639 if (m_iCurPiece < iPieces) { 640 pLine->RemoveLast(iPieces - m_iCurPiece - 1); 641 } 642 } 643 } 644 if (!bEnd && bNeedReload) { 645 m_iCurPiece = 0; 646 } 647 } 648 void CFDE_TextOut::ReplaceWidthEllipsis() { 649 LoadEllipsis(); 650 int32_t iLength = m_wsEllipsis.GetLength(); 651 if (iLength < 1) { 652 return; 653 } 654 int32_t iLines = m_ttoLines.GetSize(); 655 for (int32_t i = 0; i < iLines; i++) { 656 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(i); 657 if (!pLine->m_bNewReload) { 658 continue; 659 } 660 int32_t iEllipsisCharIndex = iLength - 1; 661 int32_t iCharWidth = 0; 662 int32_t iCharCount = 0; 663 int32_t iPiece = pLine->GetSize(); 664 while (iPiece-- > 0) { 665 FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(iPiece); 666 if (pPiece == NULL) { 667 break; 668 } 669 for (int32_t j = pPiece->iChars - 1; j >= 0; j--) { 670 if (iEllipsisCharIndex < 0) { 671 break; 672 } 673 int32_t index = pPiece->iStartChar + j; 674 iCharWidth += m_pCharWidths[index]; 675 iCharCount++; 676 if (iCharCount <= iLength) { 677 m_wsText.SetAt(index, m_wsEllipsis.GetAt(iEllipsisCharIndex)); 678 m_pCharWidths[index] = m_pEllCharWidths[iEllipsisCharIndex]; 679 } else if (iCharWidth <= m_iEllipsisWidth) { 680 m_wsText.SetAt(index, 0); 681 m_pCharWidths[index] = 0; 682 } 683 iEllipsisCharIndex--; 684 } 685 if (iEllipsisCharIndex < 0) { 686 break; 687 } 688 } 689 } 690 } 691 void CFDE_TextOut::Reload(const CFX_RectF& rect) { 692 int32_t iCount = m_ttoLines.GetSize(); 693 for (int32_t i = 0; i < iCount; i++) { 694 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(i); 695 if (pLine == NULL || !pLine->m_bNewReload) { 696 continue; 697 } 698 m_iCurLine = i; 699 m_iCurPiece = 0; 700 ReloadLinePiece(pLine, rect); 701 } 702 } 703 void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect) { 704 const FX_WCHAR* pwsStr = (const FX_WCHAR*)m_wsText; 705 FX_BOOL bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 706 int32_t iPieceWidths = 0; 707 FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(0); 708 int32_t iStartChar = pPiece->iStartChar; 709 m_fLinePos = bVertical ? pPiece->rtPiece.left : pPiece->rtPiece.top; 710 int32_t iPieceCount = pLine->GetSize(); 711 int32_t iPieceIndex = 0; 712 FX_DWORD dwBreakStatus = 0; 713 FX_WCHAR wch; 714 while (iPieceIndex < iPieceCount) { 715 int32_t iStar = iStartChar; 716 int32_t iEnd = pPiece->iChars + iStar; 717 while (iStar < iEnd) { 718 wch = *(pwsStr + iStar); 719 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 720 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 721 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, TRUE, rect); 722 } 723 iStar++; 724 } 725 iPieceIndex++; 726 pPiece = pLine->GetPtrAt(iPieceIndex); 727 } 728 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 729 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 730 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, TRUE, rect); 731 } 732 m_pTxtBreak->Reset(); 733 } 734 void CFDE_TextOut::DoAlignment(const CFX_RectF& rect) { 735 FX_BOOL bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 736 FX_FLOAT fLineStopS = bVertical ? rect.right() : rect.bottom(); 737 int32_t iLines = m_ttoLines.GetSize(); 738 if (iLines < 1) { 739 return; 740 } 741 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(iLines - 1); 742 FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(0); 743 if (pPiece == NULL) { 744 return; 745 } 746 FX_FLOAT fLineStopD = 747 bVertical ? pPiece->rtPiece.right() : pPiece->rtPiece.bottom(); 748 FX_FLOAT fInc = fLineStopS - fLineStopD; 749 if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft && 750 m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) { 751 fInc /= 2.0f; 752 } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) { 753 fInc = 0.0f; 754 } 755 if (fInc < 1.0f) { 756 return; 757 } 758 for (int32_t i = 0; i < iLines; i++) { 759 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(i); 760 int32_t iPieces = pLine->GetSize(); 761 for (int32_t j = 0; j < iPieces; j++) { 762 FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(j); 763 if (bVertical) { 764 pPiece->rtPiece.left += fInc; 765 } else { 766 pPiece->rtPiece.top += fInc; 767 } 768 } 769 } 770 } 771 void CFDE_TextOut::OnDraw(const CFX_RectF& rtClip) { 772 if (m_pRenderDevice == NULL) { 773 return; 774 } 775 int32_t iLines = m_ttoLines.GetSize(); 776 if (iLines < 1) { 777 return; 778 } 779 IFDE_SolidBrush* pBrush = 780 (IFDE_SolidBrush*)IFDE_Brush::Create(FDE_BRUSHTYPE_Solid); 781 pBrush->SetColor(m_TxtColor); 782 IFDE_Pen* pPen = NULL; 783 FDE_HDEVICESTATE hDev = m_pRenderDevice->SaveState(); 784 if (rtClip.Width() > 0.0f && rtClip.Height() > 0.0f) { 785 m_pRenderDevice->SetClipRect(rtClip); 786 } 787 for (int32_t i = 0; i < iLines; i++) { 788 CFDE_TTOLine* pLine = m_ttoLines.GetPtrAt(i); 789 int32_t iPieces = pLine->GetSize(); 790 for (int32_t j = 0; j < iPieces; j++) { 791 FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(j); 792 if (pPiece == NULL) { 793 continue; 794 } 795 int32_t iCount = GetDisplayPos(pPiece); 796 if (iCount > 0) { 797 m_pRenderDevice->DrawString(pBrush, m_pFont, m_pCharPos, iCount, 798 m_fFontSize, &m_Matrix); 799 } 800 DrawLine(pPiece, pPen); 801 } 802 } 803 m_pRenderDevice->RestoreState(hDev); 804 if (pBrush) { 805 pBrush->Release(); 806 } 807 if (pPen) { 808 pPen->Release(); 809 } 810 } 811 int32_t CFDE_TextOut::GetDisplayPos(FDE_LPTTOPIECE pPiece) { 812 FX_TXTRUN tr; 813 ToTextRun(pPiece, tr); 814 ExpandBuffer(tr.iLength, 2); 815 return m_pTxtBreak->GetDisplayPos(&tr, m_pCharPos); 816 } 817 int32_t CFDE_TextOut::GetCharRects(FDE_LPTTOPIECE pPiece) { 818 FX_TXTRUN tr; 819 ToTextRun(pPiece, tr); 820 m_rectArray.RemoveAll(); 821 return m_pTxtBreak->GetCharRects(&tr, m_rectArray); 822 } 823 void CFDE_TextOut::ToTextRun(const FDE_LPTTOPIECE pPiece, FX_TXTRUN& tr) { 824 tr.pAccess = NULL; 825 tr.pIdentity = NULL; 826 tr.pStr = (const FX_WCHAR*)m_wsText + pPiece->iStartChar; 827 tr.pWidths = m_pCharWidths + pPiece->iStartChar; 828 tr.iLength = pPiece->iChars; 829 tr.pFont = m_pFont; 830 tr.fFontSize = m_fFontSize; 831 tr.dwStyles = m_dwTxtBkStyles; 832 tr.iCharRotation = 0; 833 tr.dwCharStyles = pPiece->dwCharStyles; 834 tr.wLineBreakChar = m_wParagraphBkChar; 835 tr.pRect = &pPiece->rtPiece; 836 } 837 void CFDE_TextOut::DrawLine(const FDE_LPTTOPIECE pPiece, IFDE_Pen*& pPen) { 838 FX_BOOL bUnderLine = !!(m_dwStyles & FDE_TTOSTYLE_Underline); 839 FX_BOOL bStrikeOut = !!(m_dwStyles & FDE_TTOSTYLE_Strikeout); 840 FX_BOOL bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey); 841 FX_BOOL bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 842 if (!bUnderLine && !bStrikeOut && !bHotKey) { 843 return; 844 } 845 if (pPen == NULL) { 846 pPen = IFDE_Pen::Create(); 847 pPen->SetColor(m_TxtColor); 848 } 849 IFDE_Path* pPath = IFDE_Path::Create(); 850 int32_t iLineCount = 0; 851 CFX_RectF rtText = pPiece->rtPiece; 852 CFX_PointF pt1, pt2; 853 if (bUnderLine) { 854 if (bVertical) { 855 pt1.x = rtText.left; 856 pt1.y = rtText.top; 857 pt2.x = rtText.left; 858 pt2.y = rtText.bottom(); 859 } else { 860 pt1.x = rtText.left; 861 pt1.y = rtText.bottom(); 862 pt2.x = rtText.right(); 863 pt2.y = rtText.bottom(); 864 } 865 pPath->AddLine(pt1, pt2); 866 iLineCount++; 867 } 868 if (bStrikeOut) { 869 if (bVertical) { 870 pt1.x = rtText.left + rtText.width * 2.0f / 5.0f; 871 pt1.y = rtText.top; 872 ; 873 pt2.x = pt1.x; 874 pt2.y = rtText.bottom(); 875 } else { 876 pt1.x = rtText.left; 877 pt1.y = rtText.bottom() - rtText.height * 2.0f / 5.0f; 878 pt2.x = rtText.right(); 879 pt2.y = pt1.y; 880 } 881 pPath->AddLine(pt1, pt2); 882 iLineCount++; 883 } 884 if (bHotKey) { 885 int32_t iHotKeys = m_hotKeys.GetSize(); 886 int32_t iCount = GetCharRects(pPiece); 887 if (iCount > 0) { 888 for (int32_t i = 0; i < iHotKeys; i++) { 889 int32_t iCharIndex = m_hotKeys.GetAt(i); 890 if (iCharIndex >= pPiece->iStartChar && 891 iCharIndex < pPiece->iStartChar + pPiece->iChars) { 892 CFX_RectF rect = m_rectArray.GetAt(iCharIndex - pPiece->iStartChar); 893 if (bVertical) { 894 pt1.x = rect.left; 895 pt1.y = rect.top; 896 pt2.x = rect.left; 897 pt2.y = rect.bottom(); 898 } else { 899 pt1.x = rect.left; 900 pt1.y = rect.bottom(); 901 pt2.x = rect.right(); 902 pt2.y = rect.bottom(); 903 } 904 pPath->AddLine(pt1, pt2); 905 iLineCount++; 906 } 907 } 908 } 909 } 910 if (iLineCount > 0) { 911 m_pRenderDevice->DrawPath(pPen, 1, pPath, &m_Matrix); 912 } 913 pPath->Release(); 914 } 915 CFDE_TTOLine::CFDE_TTOLine() 916 : m_bNewReload(FALSE), m_pieces(5), m_iPieceCount(0) {} 917 CFDE_TTOLine::CFDE_TTOLine(const CFDE_TTOLine& ttoLine) : m_pieces(5) { 918 m_bNewReload = ttoLine.m_bNewReload; 919 m_iPieceCount = ttoLine.m_iPieceCount; 920 m_pieces.Copy(ttoLine.m_pieces); 921 } 922 CFDE_TTOLine::~CFDE_TTOLine() {} 923 int32_t CFDE_TTOLine::AddPiece(int32_t index, const FDE_TTOPIECE& ttoPiece) { 924 if (index >= m_iPieceCount) { 925 index = m_pieces.Add(ttoPiece) + 1; 926 m_iPieceCount++; 927 } else { 928 FDE_TTOPIECE& piece = m_pieces.GetAt(index); 929 piece = ttoPiece; 930 } 931 return index; 932 } 933 int32_t CFDE_TTOLine::GetSize() const { 934 return m_iPieceCount; 935 } 936 FDE_LPTTOPIECE CFDE_TTOLine::GetPtrAt(int32_t index) { 937 if (index >= m_iPieceCount) { 938 return NULL; 939 } 940 return m_pieces.GetPtrAt(index); 941 } 942 void CFDE_TTOLine::RemoveLast(int32_t iCount) { 943 m_pieces.RemoveLast(iCount); 944 } 945 void CFDE_TTOLine::RemoveAll(FX_BOOL bLeaveMemory) { 946 m_pieces.RemoveAll(bLeaveMemory); 947 } 948