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 "xfa/src/fee/include/ifde_txtedtbuf.h"
11#include "xfa/src/fee/include/ifde_txtedtengine.h"
12#include "xfa/src/fee/include/ifde_txtedtpage.h"
13#include "xfa/src/fee/include/fx_wordbreak.h"
14#include "fde_txtedtpage.h"
15#include "fde_txtedtengine.h"
16#include "fde_txtedtparag.h"
17#include "fde_txtedtbuf.h"
18#define FDE_TXTEDT_TOLERANCE 0.1f
19IFDE_TxtEdtPage* IFDE_TxtEdtPage::Create(IFDE_TxtEdtEngine* pEngine,
20                                         int32_t nIndex) {
21  return (IFDE_TxtEdtPage*)new CFDE_TxtEdtPage(pEngine, nIndex);
22}
23CFDE_TxtEdtTextSet::CFDE_TxtEdtTextSet(CFDE_TxtEdtPage* pPage)
24    : m_pPage(pPage) {}
25CFDE_TxtEdtTextSet::~CFDE_TxtEdtTextSet() {}
26FDE_VISUALOBJTYPE CFDE_TxtEdtTextSet::GetType() {
27  return FDE_VISUALOBJ_Text;
28}
29FX_BOOL CFDE_TxtEdtTextSet::GetBBox(FDE_HVISUALOBJ hVisualObj,
30                                    CFX_RectF& bbox) {
31  return FALSE;
32}
33FX_BOOL CFDE_TxtEdtTextSet::GetMatrix(FDE_HVISUALOBJ hVisualObj,
34                                      CFX_Matrix& matrix) {
35  return FALSE;
36}
37FX_BOOL CFDE_TxtEdtTextSet::GetRect(FDE_HVISUALOBJ hVisualObj, CFX_RectF& rt) {
38  rt = ((FDE_LPTEXTEDITPIECE)(hVisualObj))->rtPiece;
39  return TRUE;
40}
41FX_BOOL CFDE_TxtEdtTextSet::GetClip(FDE_HVISUALOBJ hVisualObj, CFX_RectF& rt) {
42  return FALSE;
43}
44int32_t CFDE_TxtEdtTextSet::GetString(FDE_HVISUALOBJ hText,
45                                      CFX_WideString& wsText) {
46  FDE_LPTEXTEDITPIECE pPiece = (FDE_LPTEXTEDITPIECE)hText;
47  FX_WCHAR* pBuffer = wsText.GetBuffer(pPiece->nCount);
48  for (int32_t i = 0; i < pPiece->nCount; i++) {
49    pBuffer[i] = m_pPage->GetChar((void*)hText, i);
50  }
51  wsText.ReleaseBuffer(pPiece->nCount);
52  return pPiece->nCount;
53}
54IFX_Font* CFDE_TxtEdtTextSet::GetFont(FDE_HVISUALOBJ hText) {
55  return m_pPage->GetEngine()->GetEditParams()->pFont;
56}
57FX_FLOAT CFDE_TxtEdtTextSet::GetFontSize(FDE_HVISUALOBJ hText) {
58  return m_pPage->GetEngine()->GetEditParams()->fFontSize;
59}
60FX_ARGB CFDE_TxtEdtTextSet::GetFontColor(FDE_HVISUALOBJ hText) {
61  return m_pPage->GetEngine()->GetEditParams()->dwFontColor;
62}
63int32_t CFDE_TxtEdtTextSet::GetDisplayPos(FDE_HVISUALOBJ hText,
64                                          FXTEXT_CHARPOS* pCharPos,
65                                          FX_BOOL bCharCode,
66                                          CFX_WideString* pWSForms) {
67  if (hText == NULL) {
68    return 0;
69  }
70  FDE_LPTEXTEDITPIECE pPiece = (FDE_LPTEXTEDITPIECE)hText;
71  int32_t nLength = pPiece->nCount;
72  if (nLength < 1) {
73    return 0;
74  }
75  CFDE_TxtEdtEngine* pEngine = (CFDE_TxtEdtEngine*)(m_pPage->GetEngine());
76  const FDE_TXTEDTPARAMS* pTextParams = pEngine->GetEditParams();
77  IFX_TxtBreak* pBreak = pEngine->GetTextBreak();
78  FX_DWORD dwLayoutStyle = pBreak->GetLayoutStyles();
79  FX_TXTRUN tr;
80  tr.pAccess = m_pPage;
81  tr.pIdentity = (void*)hText;
82  tr.pStr = NULL;
83  tr.pWidths = NULL;
84  tr.iLength = nLength;
85  tr.pFont = pTextParams->pFont;
86  tr.fFontSize = pTextParams->fFontSize;
87  tr.dwStyles = dwLayoutStyle;
88  tr.iCharRotation = pTextParams->nCharRotation;
89  tr.dwCharStyles = pPiece->dwCharStyles;
90  tr.pRect = &(pPiece->rtPiece);
91  tr.wLineBreakChar = pTextParams->wLineBreakChar;
92  return pBreak->GetDisplayPos(&tr, pCharPos, bCharCode, pWSForms);
93}
94int32_t CFDE_TxtEdtTextSet::GetCharRects(FDE_HVISUALOBJ hText,
95                                         CFX_RectFArray& rtArray) {
96  return GetCharRects_Impl(hText, rtArray);
97}
98int32_t CFDE_TxtEdtTextSet::GetCharRects_Impl(FDE_HVISUALOBJ hText,
99                                              CFX_RectFArray& rtArray,
100                                              FX_BOOL bBBox) {
101  if (hText == NULL) {
102    return 0;
103  }
104  FDE_LPTEXTEDITPIECE pPiece = (FDE_LPTEXTEDITPIECE)hText;
105  CFDE_TxtEdtEngine* pEngine = (CFDE_TxtEdtEngine*)(m_pPage->GetEngine());
106  int32_t nLength = pPiece->nCount;
107  if (nLength < 1) {
108    return 0;
109  }
110  const FDE_TXTEDTPARAMS* pTextParams = pEngine->GetEditParams();
111  FX_DWORD dwLayoutStyle = pEngine->GetTextBreak()->GetLayoutStyles();
112  FX_TXTRUN tr;
113  tr.pAccess = m_pPage;
114  tr.pIdentity = (void*)hText;
115  tr.pStr = NULL;
116  tr.pWidths = NULL;
117  tr.iLength = nLength;
118  tr.pFont = pTextParams->pFont;
119  tr.fFontSize = pTextParams->fFontSize;
120  tr.dwStyles = dwLayoutStyle;
121  tr.iCharRotation = pTextParams->nCharRotation;
122  tr.dwCharStyles = pPiece->dwCharStyles;
123  tr.pRect = &(pPiece->rtPiece);
124  tr.wLineBreakChar = pTextParams->wLineBreakChar;
125  return pEngine->GetTextBreak()->GetCharRects(&tr, rtArray, bBBox);
126}
127CFDE_TxtEdtPage::CFDE_TxtEdtPage(IFDE_TxtEdtEngine* pEngine, int32_t nPageIndex)
128    : m_pIter(nullptr),
129      m_pTextSet(nullptr),
130      m_pBgnParag(nullptr),
131      m_pEndParag(nullptr),
132      m_nRefCount(0),
133      m_nPageStart(-1),
134      m_nCharCount(0),
135      m_nPageIndex(nPageIndex),
136      m_bLoaded(FALSE),
137      m_pCharWidth(nullptr) {
138  FXSYS_memset(&m_rtPage, 0, sizeof(CFX_RectF));
139  FXSYS_memset(&m_rtPageMargin, 0, sizeof(CFX_RectF));
140  FXSYS_memset(&m_rtPageContents, 0, sizeof(CFX_RectF));
141  FXSYS_memset(&m_rtPageCanvas, 0, sizeof(CFX_RectF));
142  m_pEditEngine = (CFDE_TxtEdtEngine*)pEngine;
143}
144CFDE_TxtEdtPage::~CFDE_TxtEdtPage() {
145  m_PieceMassArr.RemoveAll(TRUE);
146  if (m_pTextSet) {
147    delete m_pTextSet;
148    m_pTextSet = NULL;
149  }
150  if (m_pCharWidth) {
151    delete[] m_pCharWidth;
152    m_pCharWidth = NULL;
153  }
154  if (m_pIter != NULL) {
155    m_pIter->Release();
156    m_pIter = NULL;
157  }
158}
159void CFDE_TxtEdtPage::Release() {
160  delete this;
161}
162IFDE_TxtEdtEngine* CFDE_TxtEdtPage::GetEngine() const {
163  return (IFDE_TxtEdtEngine*)m_pEditEngine;
164}
165FDE_VISUALOBJTYPE CFDE_TxtEdtPage::GetType() {
166  return FDE_VISUALOBJ_Text;
167}
168FX_BOOL CFDE_TxtEdtPage::GetBBox(FDE_HVISUALOBJ hVisualObj, CFX_RectF& bbox) {
169  return FALSE;
170}
171FX_BOOL CFDE_TxtEdtPage::GetMatrix(FDE_HVISUALOBJ hVisualObj,
172                                   CFX_Matrix& matrix) {
173  return FALSE;
174}
175FX_BOOL CFDE_TxtEdtPage::GetRect(FDE_HVISUALOBJ hVisualObj, CFX_RectF& rt) {
176  return FALSE;
177}
178FX_BOOL CFDE_TxtEdtPage::GetClip(FDE_HVISUALOBJ hVisualObj, CFX_RectF& rt) {
179  return FALSE;
180}
181int32_t CFDE_TxtEdtPage::GetCharRect(int32_t nIndex,
182                                     CFX_RectF& rect,
183                                     FX_BOOL bBBox) const {
184  FXSYS_assert(m_nRefCount > 0);
185  FXSYS_assert(nIndex >= 0 && nIndex < m_nCharCount);
186  if (m_nRefCount < 1) {
187    return 0;
188  }
189  int32_t nCount = m_PieceMassArr.GetSize();
190  for (int32_t i = 0; i < nCount; i++) {
191    const FDE_LPTEXTEDITPIECE pPiece = m_PieceMassArr.GetPtrAt(i);
192    if (nIndex >= pPiece->nStart &&
193        nIndex < (pPiece->nStart + pPiece->nCount)) {
194      CFX_RectFArray rectArr;
195      if (bBBox) {
196        m_pTextSet->GetCharRects_Impl((FDE_HVISUALOBJ)pPiece, rectArr, bBBox);
197      } else {
198        m_pTextSet->GetCharRects((FDE_HVISUALOBJ)pPiece, rectArr);
199      }
200      rect = rectArr[nIndex - pPiece->nStart];
201      return pPiece->nBidiLevel;
202    }
203  }
204  FXSYS_assert(0);
205  return 0;
206}
207int32_t CFDE_TxtEdtPage::GetCharIndex(const CFX_PointF& fPoint,
208                                      FX_BOOL& bBefore) {
209  FX_BOOL bVertical = m_pEditEngine->GetEditParams()->dwLayoutStyles &
210                      FDE_TEXTEDITLAYOUT_DocVertical;
211  CFX_PointF ptF = fPoint;
212  NormalizePt2Rect(ptF, m_rtPageContents, FDE_TXTEDT_TOLERANCE);
213  int32_t nCount = m_PieceMassArr.GetSize();
214  CFX_RectF rtLine;
215  int32_t nBgn = 0;
216  int32_t nEnd = 0;
217  FX_BOOL bInLine = FALSE;
218  int32_t i = 0;
219  for (i = 0; i < nCount; i++) {
220    const FDE_LPTEXTEDITPIECE pPiece = m_PieceMassArr.GetPtrAt(i);
221    if (!bInLine && (bVertical ? (pPiece->rtPiece.left <= ptF.x &&
222                                  pPiece->rtPiece.right() > ptF.x)
223                               : (pPiece->rtPiece.top <= ptF.y &&
224                                  pPiece->rtPiece.bottom() > ptF.y))) {
225      nBgn = nEnd = i;
226      rtLine = pPiece->rtPiece;
227      bInLine = TRUE;
228    } else if (bInLine) {
229      if (bVertical ? (!(pPiece->rtPiece.left <= ptF.x &&
230                         pPiece->rtPiece.right() > ptF.x))
231                    : (pPiece->rtPiece.bottom() <= ptF.y ||
232                       pPiece->rtPiece.top > ptF.y)) {
233        nEnd = i - 1;
234        break;
235      } else {
236        rtLine.Union(pPiece->rtPiece);
237      }
238    }
239  }
240  NormalizePt2Rect(ptF, rtLine, FDE_TXTEDT_TOLERANCE);
241  int32_t nCaret = 0;
242  FDE_LPTEXTEDITPIECE pPiece = NULL;
243  for (i = nBgn; i <= nEnd; i++) {
244    pPiece = m_PieceMassArr.GetPtrAt(i);
245    nCaret = m_nPageStart + pPiece->nStart;
246    if (pPiece->rtPiece.Contains(ptF)) {
247      CFX_RectFArray rectArr;
248      m_pTextSet->GetCharRects((FDE_HVISUALOBJ)pPiece, rectArr);
249      int32_t nRtCount = rectArr.GetSize();
250      for (int32_t j = 0; j < nRtCount; j++) {
251        if (rectArr[j].Contains(ptF)) {
252          nCaret = m_nPageStart + pPiece->nStart + j;
253          if (nCaret >= m_pEditEngine->GetTextBufLength()) {
254            bBefore = TRUE;
255            return m_pEditEngine->GetTextBufLength();
256          }
257          FX_WCHAR wChar = m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret);
258          if (wChar == L'\n' || wChar == L'\r') {
259            if (wChar == L'\n') {
260              if (m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret - 1) ==
261                  L'\r') {
262                nCaret--;
263              }
264            }
265            bBefore = TRUE;
266            return nCaret;
267          }
268          if (bVertical
269                  ? (ptF.y > ((rectArr[j].top + rectArr[j].bottom()) / 2))
270                  : (ptF.x > ((rectArr[j].left + rectArr[j].right()) / 2))) {
271            bBefore = FX_IsOdd(pPiece->nBidiLevel);
272          } else {
273            bBefore = !FX_IsOdd(pPiece->nBidiLevel);
274          }
275          return nCaret;
276        }
277      }
278    }
279  }
280  bBefore = TRUE;
281  return nCaret;
282}
283int32_t CFDE_TxtEdtPage::GetCharStart() const {
284  return m_nPageStart;
285}
286int32_t CFDE_TxtEdtPage::GetCharCount() const {
287  return m_nCharCount;
288}
289int32_t CFDE_TxtEdtPage::GetDisplayPos(const CFX_RectF& rtClip,
290                                       FXTEXT_CHARPOS*& pCharPos,
291                                       FX_LPRECTF pBBox) const {
292  pCharPos = FX_Alloc(FXTEXT_CHARPOS, m_nCharCount);
293  int32_t nCharPosCount = 0;
294  FDE_HVISUALOBJ hVisualObj = NULL;
295  int32_t nVisualObjCount = m_PieceMassArr.GetSize();
296  FXTEXT_CHARPOS* pos = pCharPos;
297  CFX_RectF rtObj;
298  for (int32_t i = 0; i < nVisualObjCount; i++) {
299    hVisualObj = (FDE_HVISUALOBJ)m_PieceMassArr.GetPtrAt(i);
300    m_pTextSet->GetRect(hVisualObj, rtObj);
301    if (!rtClip.IntersectWith(rtObj)) {
302      continue;
303    }
304    int32_t nCount = m_pTextSet->GetDisplayPos(hVisualObj, pos, FALSE);
305    nCharPosCount += nCount;
306    pos += nCount;
307  }
308  if ((nCharPosCount * 5) < (m_nCharCount << 2)) {
309    FXTEXT_CHARPOS* pTemp = FX_Alloc(FXTEXT_CHARPOS, nCharPosCount);
310    FXSYS_memcpy(pTemp, pCharPos, sizeof(FXTEXT_CHARPOS) * nCharPosCount);
311    FX_Free(pCharPos);
312    pCharPos = pTemp;
313  }
314  return nCharPosCount;
315}
316void CFDE_TxtEdtPage::CalcRangeRectArray(int32_t nStart,
317                                         int32_t nCount,
318                                         CFX_RectFArray& RectFArr) const {
319  int32_t nPieceCount = m_PieceMassArr.GetSize();
320  int32_t nEnd = nStart + nCount - 1;
321  FX_BOOL bInRange = FALSE;
322  for (int32_t i = 0; i < nPieceCount; i++) {
323    FDE_LPTEXTEDITPIECE piece = m_PieceMassArr.GetPtrAt(i);
324    if (!bInRange) {
325      if (nStart >= piece->nStart && nStart < (piece->nStart + piece->nCount)) {
326        int32_t nRangeEnd = piece->nCount - 1;
327        FX_BOOL bEnd = FALSE;
328        if (nEnd >= piece->nStart && nEnd < (piece->nStart + piece->nCount)) {
329          nRangeEnd = nEnd - piece->nStart;
330          bEnd = TRUE;
331        }
332        CFX_RectFArray rcArr;
333        m_pTextSet->GetCharRects((FDE_HVISUALOBJ)piece, rcArr);
334        CFX_RectF rectPiece = rcArr[nStart - piece->nStart];
335        rectPiece.Union(rcArr[nRangeEnd]);
336        RectFArr.Add(rectPiece);
337        if (bEnd) {
338          return;
339        }
340        bInRange = TRUE;
341      }
342    } else {
343      if (nEnd >= piece->nStart && nEnd < (piece->nStart + piece->nCount)) {
344        CFX_RectFArray rcArr;
345        m_pTextSet->GetCharRects((FDE_HVISUALOBJ)piece, rcArr);
346        CFX_RectF rectPiece = rcArr[0];
347        rectPiece.Union(rcArr[nEnd - piece->nStart]);
348        RectFArr.Add(rectPiece);
349        return;
350      }
351      RectFArr.Add(piece->rtPiece);
352    }
353  }
354  return;
355}
356int32_t CFDE_TxtEdtPage::SelectWord(const CFX_PointF& fPoint, int32_t& nCount) {
357  if (m_nRefCount < 0) {
358    return -1;
359  }
360  IFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf();
361  FX_BOOL bBefore;
362  int32_t nIndex = GetCharIndex(fPoint, bBefore);
363  if (nIndex == m_pEditEngine->GetTextBufLength()) {
364    nIndex = m_pEditEngine->GetTextBufLength() - 1;
365  }
366  if (nIndex < 0) {
367    return -1;
368  }
369  IFX_WordBreak* pIter = FX_WordBreak_Create();
370  pIter->Attach(new CFDE_TxtEdtBufIter((CFDE_TxtEdtBuf*)pBuf));
371  pIter->SetAt(nIndex);
372  nCount = pIter->GetWordLength();
373  int32_t nRet = pIter->GetWordPos();
374  pIter->Release();
375  return nRet;
376}
377FX_BOOL CFDE_TxtEdtPage::IsLoaded(FX_LPCRECTF pClipBox) {
378  return m_bLoaded;
379}
380int32_t CFDE_TxtEdtPage::LoadPage(FX_LPCRECTF pClipBox, IFX_Pause* pPause) {
381  if (m_nRefCount > 0) {
382    m_nRefCount++;
383    return m_nRefCount;
384  }
385  IFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf();
386  const FDE_TXTEDTPARAMS* pParams = m_pEditEngine->GetEditParams();
387  if (m_pIter != NULL) {
388    m_pIter->Release();
389  }
390  FX_WCHAR wcAlias = 0;
391  if (pParams->dwMode & FDE_TEXTEDITMODE_Password) {
392    wcAlias = m_pEditEngine->GetAliasChar();
393  }
394  m_pIter = new CFDE_TxtEdtBufIter((CFDE_TxtEdtBuf*)pBuf, wcAlias);
395  IFX_TxtBreak* pBreak = m_pEditEngine->GetTextBreak();
396  pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
397  pBreak->ClearBreakPieces();
398  int32_t nPageLineCount = m_pEditEngine->GetPageLineCount();
399  int32_t nStartLine = nPageLineCount * m_nPageIndex;
400  int32_t nEndLine = std::min((nStartLine + nPageLineCount - 1),
401                              (m_pEditEngine->GetLineCount() - 1));
402  int32_t nPageStart, nPageEnd, nTemp, nBgnParag, nStartLineInParag, nEndParag,
403      nEndLineInParag;
404  nBgnParag = m_pEditEngine->Line2Parag(0, 0, nStartLine, nStartLineInParag);
405  m_pBgnParag = (CFDE_TxtEdtParag*)m_pEditEngine->GetParag(nBgnParag);
406  m_pBgnParag->LoadParag();
407  m_pBgnParag->GetLineRange(nStartLine - nStartLineInParag, nPageStart, nTemp);
408  nEndParag = m_pEditEngine->Line2Parag(nBgnParag, nStartLineInParag, nEndLine,
409                                        nEndLineInParag);
410  m_pEndParag = (CFDE_TxtEdtParag*)m_pEditEngine->GetParag(nEndParag);
411  m_pEndParag->LoadParag();
412  m_pEndParag->GetLineRange(nEndLine - nEndLineInParag, nPageEnd, nTemp);
413  nPageEnd += (nTemp - 1);
414  FX_BOOL bVertial = pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_DocVertical;
415  FX_BOOL bLineReserve =
416      pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_LineReserve;
417  FX_FLOAT fLineStart =
418      bVertial
419          ? (bLineReserve ? (pParams->fPlateWidth - pParams->fLineSpace) : 0.0f)
420          : 0.0f;
421  FX_FLOAT fLineStep =
422      (bVertial && bLineReserve) ? (-pParams->fLineSpace) : pParams->fLineSpace;
423  FX_FLOAT fLinePos = fLineStart;
424  if (m_pTextSet == NULL) {
425    m_pTextSet = new CFDE_TxtEdtTextSet(this);
426  }
427  m_PieceMassArr.RemoveAll(TRUE);
428  FX_DWORD dwBreakStatus = FX_TXTBREAK_None;
429  int32_t nPieceStart = 0;
430  if (m_pCharWidth != NULL) {
431    delete[] m_pCharWidth;
432  }
433  m_pCharWidth = new int32_t[nPageEnd - nPageStart + 1];
434  pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
435  pBreak->ClearBreakPieces();
436  m_nPageStart = nPageStart;
437  m_nCharCount = nPageEnd - nPageStart + 1;
438  FX_BOOL bReload = FALSE;
439  FX_FLOAT fDefCharWidth = 0;
440  IFX_CharIter* pIter = m_pIter->Clone();
441  pIter->SetAt(nPageStart);
442  m_pIter->SetAt(nPageStart);
443  FX_BOOL bFirstPiece = TRUE;
444  do {
445    if (bReload) {
446      dwBreakStatus = pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
447    } else {
448      FX_WCHAR wAppend = pIter->GetChar();
449      dwBreakStatus = pBreak->AppendChar(wAppend);
450    }
451    if (pIter->GetAt() == nPageEnd && dwBreakStatus < FX_TXTBREAK_LineBreak) {
452      dwBreakStatus = pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
453    }
454    if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
455      int32_t nPieceCount = pBreak->CountBreakPieces();
456      for (int32_t j = 0; j < nPieceCount; j++) {
457        const CFX_TxtPiece* pPiece = pBreak->GetBreakPiece(j);
458        FDE_TEXTEDITPIECE TxtEdtPiece;
459        FXSYS_memset(&TxtEdtPiece, 0, sizeof(FDE_TEXTEDITPIECE));
460        TxtEdtPiece.nBidiLevel = pPiece->m_iBidiLevel;
461        TxtEdtPiece.nCount = pPiece->GetLength();
462        TxtEdtPiece.nStart = nPieceStart;
463        TxtEdtPiece.dwCharStyles = pPiece->m_dwCharStyles;
464        if (FX_IsOdd(pPiece->m_iBidiLevel)) {
465          TxtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
466        }
467        FX_FLOAT fParaBreakWidth = 0.0f;
468        if (pPiece->m_dwStatus > FX_TXTBREAK_PieceBreak) {
469          FX_WCHAR wRtChar = pParams->wLineBreakChar;
470          if (TxtEdtPiece.nCount >= 2) {
471            FX_WCHAR wChar = pBuf->GetCharByIndex(
472                m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1);
473            FX_WCHAR wCharPre = pBuf->GetCharByIndex(
474                m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 2);
475            if (wChar == wRtChar) {
476              fParaBreakWidth += fDefCharWidth;
477            }
478            if (wCharPre == wRtChar) {
479              fParaBreakWidth += fDefCharWidth;
480            }
481          } else if (TxtEdtPiece.nCount >= 1) {
482            FX_WCHAR wChar = pBuf->GetCharByIndex(
483                m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1);
484            if (wChar == wRtChar) {
485              fParaBreakWidth += fDefCharWidth;
486            }
487          }
488        }
489        if (pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_DocVertical) {
490          TxtEdtPiece.rtPiece.left = fLinePos;
491          TxtEdtPiece.rtPiece.top = (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;
492          TxtEdtPiece.rtPiece.width = pParams->fLineSpace;
493          TxtEdtPiece.rtPiece.height =
494              (FX_FLOAT)pPiece->m_iWidth / 20000.0f + fParaBreakWidth;
495        } else {
496          TxtEdtPiece.rtPiece.left = (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;
497          TxtEdtPiece.rtPiece.top = fLinePos;
498          TxtEdtPiece.rtPiece.width =
499              (FX_FLOAT)pPiece->m_iWidth / 20000.0f + fParaBreakWidth;
500          TxtEdtPiece.rtPiece.height = pParams->fLineSpace;
501        }
502        if (bFirstPiece) {
503          m_rtPageContents = TxtEdtPiece.rtPiece;
504          bFirstPiece = FALSE;
505        } else {
506          m_rtPageContents.Union(TxtEdtPiece.rtPiece);
507        }
508        nPieceStart += TxtEdtPiece.nCount;
509        m_PieceMassArr.Add(TxtEdtPiece);
510        for (int32_t k = 0; k < TxtEdtPiece.nCount; k++) {
511          CFX_Char* ptc = pPiece->GetCharPtr(k);
512          m_pCharWidth[TxtEdtPiece.nStart + k] = ptc->m_iCharWidth;
513        }
514      }
515      fLinePos += fLineStep;
516      pBreak->ClearBreakPieces();
517    }
518    if (pIter->GetAt() == nPageEnd && dwBreakStatus == FX_TXTBREAK_LineBreak) {
519      bReload = TRUE;
520      pIter->Next(TRUE);
521    }
522  } while (pIter->Next(FALSE) && (pIter->GetAt() <= nPageEnd));
523  if (m_rtPageContents.left != 0) {
524    FX_FLOAT fDelta = 0.0f;
525    if (m_rtPageContents.width < pParams->fPlateWidth) {
526      if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Right) {
527        fDelta = pParams->fPlateWidth - m_rtPageContents.width;
528      } else if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Center) {
529        if ((pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) &&
530            m_nCharCount > 1) {
531          int32_t nCount = m_nCharCount - 1;
532          int32_t n = (m_pEditEngine->m_nLimit - nCount) / 2;
533          fDelta = (m_rtPageContents.width / nCount) * n;
534        } else {
535          fDelta = (pParams->fPlateWidth - m_rtPageContents.width) / 2;
536        }
537      }
538    }
539    FX_FLOAT fOffset = m_rtPageContents.left - fDelta;
540    int32_t nCount = m_PieceMassArr.GetSize();
541    for (int32_t i = 0; i < nCount; i++) {
542      FDE_LPTEXTEDITPIECE pPiece = m_PieceMassArr.GetPtrAt(i);
543      pPiece->rtPiece.Offset(-fOffset, 0.0f);
544    }
545    m_rtPageContents.Offset(-fOffset, 0.0f);
546  }
547  if (m_pEditEngine->GetEditParams()->dwLayoutStyles &
548      FDE_TEXTEDITLAYOUT_LastLineHeight) {
549    m_rtPageContents.height -= pParams->fLineSpace - pParams->fFontSize;
550    int32_t nCount = m_PieceMassArr.GetSize();
551    FDE_LPTEXTEDITPIECE pPiece = m_PieceMassArr.GetPtrAt(nCount - 1);
552    pPiece->rtPiece.height = pParams->fFontSize;
553  }
554  pIter->Release();
555  m_nRefCount = 1;
556  m_bLoaded = TRUE;
557  return 0;
558}
559void CFDE_TxtEdtPage::UnloadPage(FX_LPCRECTF pClipBox) {
560  FXSYS_assert(m_nRefCount > 0);
561  m_nRefCount--;
562  if (m_nRefCount == 0) {
563    m_PieceMassArr.RemoveAll();
564    if (m_pTextSet) {
565      delete m_pTextSet;
566      m_pTextSet = NULL;
567    }
568    if (m_pCharWidth) {
569      delete[] m_pCharWidth;
570      m_pCharWidth = NULL;
571    }
572    if (m_pBgnParag) {
573      m_pBgnParag->UnloadParag();
574    }
575    if (m_pEndParag) {
576      m_pEndParag->UnloadParag();
577    }
578    if (m_pIter) {
579      m_pIter->Release();
580      m_pIter = NULL;
581    }
582    m_pBgnParag = NULL;
583    m_pEndParag = NULL;
584  }
585  return;
586}
587const CFX_RectF& CFDE_TxtEdtPage::GetContentsBox() {
588  return m_rtPageContents;
589}
590FX_POSITION CFDE_TxtEdtPage::GetFirstPosition(FDE_HVISUALOBJ hCanvas) {
591  if (m_PieceMassArr.GetSize() < 1) {
592    return NULL;
593  }
594  return (FX_POSITION)1;
595}
596FDE_HVISUALOBJ CFDE_TxtEdtPage::GetNext(FDE_HVISUALOBJ hCanvas,
597                                        FX_POSITION& pos,
598                                        IFDE_VisualSet*& pVisualSet) {
599  if (m_pTextSet == NULL) {
600    pos = NULL;
601    return NULL;
602  }
603  int32_t nPos = (int32_t)(uintptr_t)pos;
604  pVisualSet = m_pTextSet;
605  if (nPos + 1 > m_PieceMassArr.GetSize()) {
606    pos = NULL;
607  } else {
608    pos = (FX_POSITION)(uintptr_t)(nPos + 1);
609  }
610  return (FDE_HVISUALOBJ)(m_PieceMassArr.GetPtrAt(nPos - 1));
611}
612FDE_HVISUALOBJ CFDE_TxtEdtPage::GetParentCanvas(FDE_HVISUALOBJ hCanvas,
613                                                IFDE_VisualSet*& pVisualSet) {
614  return NULL;
615}
616FX_WCHAR CFDE_TxtEdtPage::GetChar(void* pIdentity, int32_t index) const {
617  int32_t nIndex =
618      m_nPageStart + ((FDE_LPTEXTEDITPIECE)pIdentity)->nStart + index;
619  if (nIndex != m_pIter->GetAt()) {
620    m_pIter->SetAt(nIndex);
621  }
622  FX_WCHAR wChar = m_pIter->GetChar();
623  m_pIter->Next();
624  return wChar;
625}
626int32_t CFDE_TxtEdtPage::GetWidth(void* pIdentity, int32_t index) const {
627  int32_t nWidth =
628      m_pCharWidth[((FDE_LPTEXTEDITPIECE)pIdentity)->nStart + index];
629  return nWidth;
630}
631void CFDE_TxtEdtPage::NormalizePt2Rect(CFX_PointF& ptF,
632                                       const CFX_RectF& rtF,
633                                       FX_FLOAT fTolerance) const {
634  if (rtF.Contains(ptF.x, ptF.y)) {
635    return;
636  }
637  if (ptF.x < rtF.left) {
638    ptF.x = rtF.left;
639  } else if (ptF.x >= rtF.right()) {
640    ptF.x = rtF.right() - fTolerance;
641  }
642  if (ptF.y < rtF.top) {
643    ptF.y = rtF.top;
644  } else if (ptF.y >= rtF.bottom()) {
645    ptF.y = rtF.bottom() - fTolerance;
646  }
647}
648