Home | History | Annotate | Download | only in fde
      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