Home | History | Annotate | Download | only in fpdfdoc
      1 // Copyright 2016 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 "core/fpdfdoc/cpdf_variabletext.h"
      8 
      9 #include <algorithm>
     10 #include <utility>
     11 
     12 #include "core/fpdfapi/font/cpdf_font.h"
     13 #include "core/fpdfdoc/cline.h"
     14 #include "core/fpdfdoc/cpvt_word.h"
     15 #include "core/fpdfdoc/cpvt_wordinfo.h"
     16 #include "core/fpdfdoc/csection.h"
     17 #include "core/fpdfdoc/ipvt_fontmap.h"
     18 #include "core/fxcrt/fx_codepage.h"
     19 #include "third_party/base/ptr_util.h"
     20 #include "third_party/base/stl_util.h"
     21 
     22 namespace {
     23 
     24 const float kFontScale = 0.001f;
     25 const uint8_t kReturnLength = 1;
     26 const float kScalePercent = 0.01f;
     27 
     28 const uint8_t gFontSizeSteps[] = {4,  6,  8,   9,   10,  12,  14, 18, 20,
     29                                   25, 30, 35,  40,  45,  50,  55, 60, 70,
     30                                   80, 90, 100, 110, 120, 130, 144};
     31 
     32 }  // namespace
     33 
     34 CPDF_VariableText::Provider::Provider(IPVT_FontMap* pFontMap)
     35     : m_pFontMap(pFontMap) {
     36   ASSERT(m_pFontMap);
     37 }
     38 
     39 CPDF_VariableText::Provider::~Provider() {}
     40 
     41 int32_t CPDF_VariableText::Provider::GetCharWidth(int32_t nFontIndex,
     42                                                   uint16_t word) {
     43   if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) {
     44     uint32_t charcode = pPDFFont->CharCodeFromUnicode(word);
     45     if (charcode != CPDF_Font::kInvalidCharCode)
     46       return pPDFFont->GetCharWidthF(charcode);
     47   }
     48   return 0;
     49 }
     50 
     51 int32_t CPDF_VariableText::Provider::GetTypeAscent(int32_t nFontIndex) {
     52   if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex))
     53     return pPDFFont->GetTypeAscent();
     54   return 0;
     55 }
     56 
     57 int32_t CPDF_VariableText::Provider::GetTypeDescent(int32_t nFontIndex) {
     58   if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex))
     59     return pPDFFont->GetTypeDescent();
     60   return 0;
     61 }
     62 
     63 int32_t CPDF_VariableText::Provider::GetWordFontIndex(uint16_t word,
     64                                                       int32_t charset,
     65                                                       int32_t nFontIndex) {
     66   if (CPDF_Font* pDefFont = m_pFontMap->GetPDFFont(0)) {
     67     if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
     68       return 0;
     69   }
     70   if (CPDF_Font* pSysFont = m_pFontMap->GetPDFFont(1)) {
     71     if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
     72       return 1;
     73   }
     74   return -1;
     75 }
     76 
     77 bool CPDF_VariableText::Provider::IsLatinWord(uint16_t word) {
     78   return (word >= 0x61 && word <= 0x7A) || (word >= 0x41 && word <= 0x5A) ||
     79          word == 0x2D || word == 0x27;
     80 }
     81 
     82 int32_t CPDF_VariableText::Provider::GetDefaultFontIndex() {
     83   return 0;
     84 }
     85 
     86 CPDF_VariableText::Iterator::Iterator(CPDF_VariableText* pVT)
     87     : m_CurPos(-1, -1, -1), m_pVT(pVT) {}
     88 
     89 CPDF_VariableText::Iterator::~Iterator() {}
     90 
     91 void CPDF_VariableText::Iterator::SetAt(int32_t nWordIndex) {
     92   m_CurPos = m_pVT->WordIndexToWordPlace(nWordIndex);
     93 }
     94 
     95 void CPDF_VariableText::Iterator::SetAt(const CPVT_WordPlace& place) {
     96   ASSERT(m_pVT);
     97   m_CurPos = place;
     98 }
     99 
    100 bool CPDF_VariableText::Iterator::NextWord() {
    101   if (m_CurPos == m_pVT->GetEndWordPlace())
    102     return false;
    103 
    104   m_CurPos = m_pVT->GetNextWordPlace(m_CurPos);
    105   return true;
    106 }
    107 
    108 bool CPDF_VariableText::Iterator::PrevWord() {
    109   if (m_CurPos == m_pVT->GetBeginWordPlace())
    110     return false;
    111 
    112   m_CurPos = m_pVT->GetPrevWordPlace(m_CurPos);
    113   return true;
    114 }
    115 
    116 bool CPDF_VariableText::Iterator::NextLine() {
    117   if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
    118     return false;
    119 
    120   CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
    121   if (m_CurPos.nLineIndex <
    122       pdfium::CollectionSize<int32_t>(pSection->m_LineArray) - 1) {
    123     m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex + 1, -1);
    124     return true;
    125   }
    126   if (m_CurPos.nSecIndex <
    127       pdfium::CollectionSize<int32_t>(m_pVT->m_SectionArray) - 1) {
    128     m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1);
    129     return true;
    130   }
    131   return false;
    132 }
    133 
    134 bool CPDF_VariableText::Iterator::GetWord(CPVT_Word& word) const {
    135   word.WordPlace = m_CurPos;
    136   if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
    137     return false;
    138 
    139   CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
    140   if (!pdfium::IndexInBounds(pSection->m_LineArray, m_CurPos.nLineIndex) ||
    141       !pdfium::IndexInBounds(pSection->m_WordArray, m_CurPos.nWordIndex)) {
    142     return false;
    143   }
    144 
    145   CPVT_WordInfo* pWord = pSection->m_WordArray[m_CurPos.nWordIndex].get();
    146   word.Word = pWord->Word;
    147   word.nCharset = pWord->nCharset;
    148   word.fWidth = m_pVT->GetWordWidth(*pWord);
    149   word.ptWord =
    150       m_pVT->InToOut(CFX_PointF(pWord->fWordX + pSection->m_Rect.left,
    151                                 pWord->fWordY + pSection->m_Rect.top));
    152   word.fAscent = m_pVT->GetWordAscent(*pWord);
    153   word.fDescent = m_pVT->GetWordDescent(*pWord);
    154   word.nFontIndex = m_pVT->GetWordFontIndex(*pWord);
    155   word.fFontSize = m_pVT->GetWordFontSize();
    156   return true;
    157 }
    158 
    159 bool CPDF_VariableText::Iterator::GetLine(CPVT_Line& line) const {
    160   ASSERT(m_pVT);
    161   line.lineplace = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex, -1);
    162   if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex))
    163     return false;
    164 
    165   CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get();
    166   if (!pdfium::IndexInBounds(pSection->m_LineArray, m_CurPos.nLineIndex))
    167     return false;
    168 
    169   CLine* pLine = pSection->m_LineArray[m_CurPos.nLineIndex].get();
    170   line.ptLine = m_pVT->InToOut(
    171       CFX_PointF(pLine->m_LineInfo.fLineX + pSection->m_Rect.left,
    172                  pLine->m_LineInfo.fLineY + pSection->m_Rect.top));
    173   line.fLineWidth = pLine->m_LineInfo.fLineWidth;
    174   line.fLineAscent = pLine->m_LineInfo.fLineAscent;
    175   line.fLineDescent = pLine->m_LineInfo.fLineDescent;
    176   line.lineEnd = pLine->GetEndWordPlace();
    177   return true;
    178 }
    179 
    180 CPDF_VariableText::CPDF_VariableText()
    181     : m_nLimitChar(0),
    182       m_nCharArray(0),
    183       m_bMultiLine(false),
    184       m_bLimitWidth(false),
    185       m_bAutoFontSize(false),
    186       m_nAlignment(0),
    187       m_fLineLeading(0.0f),
    188       m_fCharSpace(0.0f),
    189       m_nHorzScale(100),
    190       m_wSubWord(0),
    191       m_fFontSize(0.0f),
    192       m_bInitialized(false),
    193       m_pVTProvider(nullptr) {}
    194 
    195 CPDF_VariableText::~CPDF_VariableText() {}
    196 
    197 void CPDF_VariableText::Initialize() {
    198   if (m_bInitialized)
    199     return;
    200 
    201   CPVT_WordPlace place;
    202   place.nSecIndex = 0;
    203   AddSection(place);
    204 
    205   CPVT_LineInfo lineinfo;
    206   lineinfo.fLineAscent = GetFontAscent(GetDefaultFontIndex(), GetFontSize());
    207   lineinfo.fLineDescent = GetFontDescent(GetDefaultFontIndex(), GetFontSize());
    208   AddLine(place, lineinfo);
    209 
    210   if (!m_SectionArray.empty())
    211     m_SectionArray.front()->ResetLinePlace();
    212 
    213   m_bInitialized = true;
    214 }
    215 
    216 CPVT_WordPlace CPDF_VariableText::InsertWord(const CPVT_WordPlace& place,
    217                                              uint16_t word,
    218                                              int32_t charset) {
    219   int32_t nTotalWords = GetTotalWords();
    220   if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar)
    221     return place;
    222   if (m_nCharArray > 0 && nTotalWords >= m_nCharArray)
    223     return place;
    224 
    225   CPVT_WordPlace newplace = place;
    226   newplace.nWordIndex++;
    227   int32_t nFontIndex =
    228       GetSubWord() > 0 ? GetDefaultFontIndex()
    229                        : GetWordFontIndex(word, charset, GetDefaultFontIndex());
    230   return AddWord(newplace, CPVT_WordInfo(word, charset, nFontIndex));
    231 }
    232 
    233 CPVT_WordPlace CPDF_VariableText::InsertSection(const CPVT_WordPlace& place) {
    234   int32_t nTotalWords = GetTotalWords();
    235   if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar)
    236     return place;
    237   if (m_nCharArray > 0 && nTotalWords >= m_nCharArray)
    238     return place;
    239   if (!m_bMultiLine)
    240     return place;
    241 
    242   CPVT_WordPlace wordplace = place;
    243   UpdateWordPlace(wordplace);
    244   if (!pdfium::IndexInBounds(m_SectionArray, wordplace.nSecIndex))
    245     return place;
    246 
    247   CSection* pSection = m_SectionArray[wordplace.nSecIndex].get();
    248   CPVT_WordPlace NewPlace(wordplace.nSecIndex + 1, 0, -1);
    249   AddSection(NewPlace);
    250   CPVT_WordPlace result = NewPlace;
    251   if (pdfium::IndexInBounds(m_SectionArray, NewPlace.nSecIndex)) {
    252     CSection* pNewSection = m_SectionArray[NewPlace.nSecIndex].get();
    253     for (int32_t w = wordplace.nWordIndex + 1;
    254          w < pdfium::CollectionSize<int32_t>(pSection->m_WordArray); ++w) {
    255       NewPlace.nWordIndex++;
    256       pNewSection->AddWord(NewPlace, *pSection->m_WordArray[w]);
    257     }
    258   }
    259   ClearSectionRightWords(wordplace);
    260   return result;
    261 }
    262 
    263 CPVT_WordPlace CPDF_VariableText::DeleteWords(
    264     const CPVT_WordRange& PlaceRange) {
    265   bool bLastSecPos =
    266       pdfium::IndexInBounds(m_SectionArray, PlaceRange.EndPos.nSecIndex) &&
    267       PlaceRange.EndPos ==
    268           m_SectionArray[PlaceRange.EndPos.nSecIndex]->GetEndWordPlace();
    269 
    270   ClearWords(PlaceRange);
    271   if (PlaceRange.BeginPos.nSecIndex != PlaceRange.EndPos.nSecIndex) {
    272     ClearEmptySections(PlaceRange);
    273     if (!bLastSecPos)
    274       LinkLatterSection(PlaceRange.BeginPos);
    275   }
    276   return PlaceRange.BeginPos;
    277 }
    278 
    279 CPVT_WordPlace CPDF_VariableText::DeleteWord(const CPVT_WordPlace& place) {
    280   return ClearRightWord(AdjustLineHeader(place, true));
    281 }
    282 
    283 CPVT_WordPlace CPDF_VariableText::BackSpaceWord(const CPVT_WordPlace& place) {
    284   return ClearLeftWord(AdjustLineHeader(place, true));
    285 }
    286 
    287 void CPDF_VariableText::SetText(const WideString& swText) {
    288   DeleteWords(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
    289   CPVT_WordPlace wp(0, 0, -1);
    290   if (!m_SectionArray.empty())
    291     m_SectionArray.front()->m_Rect = CPVT_FloatRect();
    292 
    293   int32_t nCharCount = 0;
    294   for (int32_t i = 0, sz = swText.GetLength(); i < sz; i++) {
    295     if (m_nLimitChar > 0 && nCharCount >= m_nLimitChar)
    296       break;
    297     if (m_nCharArray > 0 && nCharCount >= m_nCharArray)
    298       break;
    299 
    300     uint16_t word = swText[i];
    301     switch (word) {
    302       case 0x0D:
    303         if (m_bMultiLine) {
    304           if (i + 1 < sz && swText[i + 1] == 0x0A)
    305             i++;
    306           wp.AdvanceSection();
    307           AddSection(wp);
    308         }
    309         break;
    310       case 0x0A:
    311         if (m_bMultiLine) {
    312           if (i + 1 < sz && swText[i + 1] == 0x0D)
    313             i++;
    314           wp.AdvanceSection();
    315           AddSection(wp);
    316         }
    317         break;
    318       case 0x09:
    319         word = 0x20;
    320       default:
    321         wp = InsertWord(wp, word, FX_CHARSET_Default);
    322         break;
    323     }
    324     nCharCount++;
    325   }
    326 }
    327 
    328 void CPDF_VariableText::UpdateWordPlace(CPVT_WordPlace& place) const {
    329   if (place.nSecIndex < 0)
    330     place = GetBeginWordPlace();
    331   if (place.nSecIndex >= pdfium::CollectionSize<int32_t>(m_SectionArray))
    332     place = GetEndWordPlace();
    333 
    334   place = AdjustLineHeader(place, true);
    335   if (pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    336     m_SectionArray[place.nSecIndex]->UpdateWordPlace(place);
    337 }
    338 
    339 int32_t CPDF_VariableText::WordPlaceToWordIndex(
    340     const CPVT_WordPlace& place) const {
    341   CPVT_WordPlace newplace = place;
    342   UpdateWordPlace(newplace);
    343   int32_t nIndex = 0;
    344   int32_t i = 0;
    345   int32_t sz = 0;
    346   for (i = 0, sz = pdfium::CollectionSize<int32_t>(m_SectionArray);
    347        i < sz && i < newplace.nSecIndex; i++) {
    348     CSection* pSection = m_SectionArray[i].get();
    349     nIndex += pdfium::CollectionSize<int32_t>(pSection->m_WordArray);
    350     if (i != sz - 1)
    351       nIndex += kReturnLength;
    352   }
    353   if (pdfium::IndexInBounds(m_SectionArray, i))
    354     nIndex += newplace.nWordIndex + kReturnLength;
    355   return nIndex;
    356 }
    357 
    358 CPVT_WordPlace CPDF_VariableText::WordIndexToWordPlace(int32_t index) const {
    359   CPVT_WordPlace place = GetBeginWordPlace();
    360   int32_t nOldIndex = 0;
    361   int32_t nIndex = 0;
    362   bool bFound = false;
    363   for (int32_t i = 0, sz = pdfium::CollectionSize<int32_t>(m_SectionArray);
    364        i < sz; i++) {
    365     CSection* pSection = m_SectionArray[i].get();
    366     nIndex += pdfium::CollectionSize<int32_t>(pSection->m_WordArray);
    367     if (nIndex == index) {
    368       place = pSection->GetEndWordPlace();
    369       bFound = true;
    370       break;
    371     }
    372     if (nIndex > index) {
    373       place.nSecIndex = i;
    374       place.nWordIndex = index - nOldIndex - 1;
    375       pSection->UpdateWordPlace(place);
    376       bFound = true;
    377       break;
    378     }
    379     if (i != sz - 1)
    380       nIndex += kReturnLength;
    381     nOldIndex = nIndex;
    382   }
    383   if (!bFound)
    384     place = GetEndWordPlace();
    385   return place;
    386 }
    387 
    388 CPVT_WordPlace CPDF_VariableText::GetBeginWordPlace() const {
    389   return m_bInitialized ? CPVT_WordPlace(0, 0, -1) : CPVT_WordPlace();
    390 }
    391 
    392 CPVT_WordPlace CPDF_VariableText::GetEndWordPlace() const {
    393   if (m_SectionArray.empty())
    394     return CPVT_WordPlace();
    395   return m_SectionArray.back()->GetEndWordPlace();
    396 }
    397 
    398 CPVT_WordPlace CPDF_VariableText::GetPrevWordPlace(
    399     const CPVT_WordPlace& place) const {
    400   if (place.nSecIndex < 0)
    401     return GetBeginWordPlace();
    402   if (place.nSecIndex >= pdfium::CollectionSize<int32_t>(m_SectionArray))
    403     return GetEndWordPlace();
    404 
    405   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    406   if (place > pSection->GetBeginWordPlace())
    407     return pSection->GetPrevWordPlace(place);
    408   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex - 1))
    409     return GetBeginWordPlace();
    410   return m_SectionArray[place.nSecIndex - 1]->GetEndWordPlace();
    411 }
    412 
    413 CPVT_WordPlace CPDF_VariableText::GetNextWordPlace(
    414     const CPVT_WordPlace& place) const {
    415   if (place.nSecIndex < 0)
    416     return GetBeginWordPlace();
    417   if (place.nSecIndex >= pdfium::CollectionSize<int32_t>(m_SectionArray))
    418     return GetEndWordPlace();
    419 
    420   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    421   if (place < pSection->GetEndWordPlace())
    422     return pSection->GetNextWordPlace(place);
    423   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex + 1))
    424     return GetEndWordPlace();
    425   return m_SectionArray[place.nSecIndex + 1]->GetBeginWordPlace();
    426 }
    427 
    428 CPVT_WordPlace CPDF_VariableText::SearchWordPlace(
    429     const CFX_PointF& point) const {
    430   CFX_PointF pt = OutToIn(point);
    431   CPVT_WordPlace place = GetBeginWordPlace();
    432   int32_t nLeft = 0;
    433   int32_t nRight = pdfium::CollectionSize<int32_t>(m_SectionArray) - 1;
    434   int32_t nMid = pdfium::CollectionSize<int32_t>(m_SectionArray) / 2;
    435   bool bUp = true;
    436   bool bDown = true;
    437   while (nLeft <= nRight) {
    438     if (!pdfium::IndexInBounds(m_SectionArray, nMid))
    439       break;
    440     CSection* pSection = m_SectionArray[nMid].get();
    441     if (IsFloatBigger(pt.y, pSection->m_Rect.top))
    442       bUp = false;
    443     if (IsFloatBigger(pSection->m_Rect.bottom, pt.y))
    444       bDown = false;
    445     if (IsFloatSmaller(pt.y, pSection->m_Rect.top)) {
    446       nRight = nMid - 1;
    447       nMid = (nLeft + nRight) / 2;
    448       continue;
    449     }
    450     if (IsFloatBigger(pt.y, pSection->m_Rect.bottom)) {
    451       nLeft = nMid + 1;
    452       nMid = (nLeft + nRight) / 2;
    453       continue;
    454     }
    455     place = pSection->SearchWordPlace(
    456         CFX_PointF(pt.x - pSection->m_Rect.left, pt.y - pSection->m_Rect.top));
    457     place.nSecIndex = nMid;
    458     return place;
    459   }
    460   if (bUp)
    461     place = GetBeginWordPlace();
    462   if (bDown)
    463     place = GetEndWordPlace();
    464   return place;
    465 }
    466 
    467 CPVT_WordPlace CPDF_VariableText::GetUpWordPlace(
    468     const CPVT_WordPlace& place,
    469     const CFX_PointF& point) const {
    470   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    471     return place;
    472 
    473   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    474   CPVT_WordPlace temp = place;
    475   CFX_PointF pt = OutToIn(point);
    476   if (temp.nLineIndex-- > 0) {
    477     return pSection->SearchWordPlace(pt.x - pSection->m_Rect.left, temp);
    478   }
    479   if (temp.nSecIndex-- > 0) {
    480     if (pdfium::IndexInBounds(m_SectionArray, temp.nSecIndex)) {
    481       CSection* pLastSection = m_SectionArray[temp.nSecIndex].get();
    482       temp.nLineIndex =
    483           pdfium::CollectionSize<int32_t>(pLastSection->m_LineArray) - 1;
    484       return pLastSection->SearchWordPlace(pt.x - pLastSection->m_Rect.left,
    485                                            temp);
    486     }
    487   }
    488   return place;
    489 }
    490 
    491 CPVT_WordPlace CPDF_VariableText::GetDownWordPlace(
    492     const CPVT_WordPlace& place,
    493     const CFX_PointF& point) const {
    494   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    495     return place;
    496 
    497   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    498   CPVT_WordPlace temp = place;
    499   CFX_PointF pt = OutToIn(point);
    500   if (temp.nLineIndex++ <
    501       pdfium::CollectionSize<int32_t>(pSection->m_LineArray) - 1) {
    502     return pSection->SearchWordPlace(pt.x - pSection->m_Rect.left, temp);
    503   }
    504   temp.AdvanceSection();
    505   if (!pdfium::IndexInBounds(m_SectionArray, temp.nSecIndex))
    506     return place;
    507 
    508   return m_SectionArray[temp.nSecIndex]->SearchWordPlace(
    509       pt.x - pSection->m_Rect.left, temp);
    510 }
    511 
    512 CPVT_WordPlace CPDF_VariableText::GetLineBeginPlace(
    513     const CPVT_WordPlace& place) const {
    514   return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
    515 }
    516 
    517 CPVT_WordPlace CPDF_VariableText::GetLineEndPlace(
    518     const CPVT_WordPlace& place) const {
    519   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    520     return place;
    521 
    522   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    523   if (!pdfium::IndexInBounds(pSection->m_LineArray, place.nLineIndex))
    524     return place;
    525 
    526   return pSection->m_LineArray[place.nLineIndex]->GetEndWordPlace();
    527 }
    528 
    529 CPVT_WordPlace CPDF_VariableText::GetSectionBeginPlace(
    530     const CPVT_WordPlace& place) const {
    531   return CPVT_WordPlace(place.nSecIndex, 0, -1);
    532 }
    533 
    534 CPVT_WordPlace CPDF_VariableText::GetSectionEndPlace(
    535     const CPVT_WordPlace& place) const {
    536   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    537     return place;
    538 
    539   return m_SectionArray[place.nSecIndex]->GetEndWordPlace();
    540 }
    541 
    542 int32_t CPDF_VariableText::GetTotalWords() const {
    543   int32_t nTotal = 0;
    544   for (const auto& pSection : m_SectionArray) {
    545     nTotal +=
    546         pdfium::CollectionSize<int32_t>(pSection->m_WordArray) + kReturnLength;
    547   }
    548   return nTotal - kReturnLength;
    549 }
    550 
    551 CPVT_WordPlace CPDF_VariableText::AddSection(const CPVT_WordPlace& place) {
    552   if (IsValid() && !m_bMultiLine)
    553     return place;
    554 
    555   int32_t nSecIndex = pdfium::clamp(
    556       place.nSecIndex, 0, pdfium::CollectionSize<int32_t>(m_SectionArray));
    557 
    558   auto pSection = pdfium::MakeUnique<CSection>(this);
    559   pSection->m_Rect = CPVT_FloatRect();
    560   pSection->SecPlace.nSecIndex = nSecIndex;
    561   m_SectionArray.insert(m_SectionArray.begin() + nSecIndex,
    562                         std::move(pSection));
    563   return place;
    564 }
    565 
    566 CPVT_WordPlace CPDF_VariableText::AddLine(const CPVT_WordPlace& place,
    567                                           const CPVT_LineInfo& lineinfo) {
    568   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    569     return place;
    570 
    571   return m_SectionArray[place.nSecIndex]->AddLine(lineinfo);
    572 }
    573 
    574 CPVT_WordPlace CPDF_VariableText::AddWord(const CPVT_WordPlace& place,
    575                                           const CPVT_WordInfo& wordinfo) {
    576   if (m_SectionArray.empty())
    577     return place;
    578 
    579   CPVT_WordPlace newplace = place;
    580   newplace.nSecIndex =
    581       pdfium::clamp(newplace.nSecIndex, 0,
    582                     pdfium::CollectionSize<int32_t>(m_SectionArray) - 1);
    583   return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo);
    584 }
    585 
    586 bool CPDF_VariableText::GetWordInfo(const CPVT_WordPlace& place,
    587                                     CPVT_WordInfo& wordinfo) {
    588   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    589     return false;
    590 
    591   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    592   if (!pdfium::IndexInBounds(pSection->m_WordArray, place.nWordIndex))
    593     return false;
    594 
    595   wordinfo = *pSection->m_WordArray[place.nWordIndex];
    596   return true;
    597 }
    598 
    599 bool CPDF_VariableText::SetWordInfo(const CPVT_WordPlace& place,
    600                                     const CPVT_WordInfo& wordinfo) {
    601   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    602     return false;
    603 
    604   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    605   if (!pdfium::IndexInBounds(pSection->m_WordArray, place.nWordIndex))
    606     return false;
    607 
    608   *pSection->m_WordArray[place.nWordIndex] = wordinfo;
    609   return true;
    610 }
    611 
    612 bool CPDF_VariableText::GetLineInfo(const CPVT_WordPlace& place,
    613                                     CPVT_LineInfo& lineinfo) {
    614   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    615     return false;
    616 
    617   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    618   if (!pdfium::IndexInBounds(pSection->m_LineArray, place.nLineIndex))
    619     return false;
    620 
    621   lineinfo = pSection->m_LineArray[place.nLineIndex]->m_LineInfo;
    622   return true;
    623 }
    624 
    625 void CPDF_VariableText::SetPlateRect(const CFX_FloatRect& rect) {
    626   m_rcPlate = rect;
    627 }
    628 
    629 void CPDF_VariableText::SetContentRect(const CPVT_FloatRect& rect) {
    630   m_rcContent = rect;
    631 }
    632 
    633 CFX_FloatRect CPDF_VariableText::GetContentRect() const {
    634   return InToOut(CPVT_FloatRect(m_rcContent));
    635 }
    636 
    637 const CFX_FloatRect& CPDF_VariableText::GetPlateRect() const {
    638   return m_rcPlate;
    639 }
    640 
    641 float CPDF_VariableText::GetWordFontSize() {
    642   return GetFontSize();
    643 }
    644 
    645 int32_t CPDF_VariableText::GetWordFontIndex(const CPVT_WordInfo& WordInfo) {
    646   return WordInfo.nFontIndex;
    647 }
    648 
    649 float CPDF_VariableText::GetWordWidth(int32_t nFontIndex,
    650                                       uint16_t Word,
    651                                       uint16_t SubWord,
    652                                       float fCharSpace,
    653                                       int32_t nHorzScale,
    654                                       float fFontSize,
    655                                       float fWordTail) {
    656   return (GetCharWidth(nFontIndex, Word, SubWord) * fFontSize * kFontScale +
    657           fCharSpace) *
    658              nHorzScale * kScalePercent +
    659          fWordTail;
    660 }
    661 
    662 float CPDF_VariableText::GetWordWidth(const CPVT_WordInfo& WordInfo) {
    663   return GetWordWidth(GetWordFontIndex(WordInfo), WordInfo.Word, GetSubWord(),
    664                       GetCharSpace(), GetHorzScale(), GetWordFontSize(),
    665                       WordInfo.fWordTail);
    666 }
    667 
    668 float CPDF_VariableText::GetLineAscent() {
    669   return GetFontAscent(GetDefaultFontIndex(), GetFontSize());
    670 }
    671 
    672 float CPDF_VariableText::GetLineDescent() {
    673   return GetFontDescent(GetDefaultFontIndex(), GetFontSize());
    674 }
    675 
    676 float CPDF_VariableText::GetFontAscent(int32_t nFontIndex, float fFontSize) {
    677   return (float)GetTypeAscent(nFontIndex) * fFontSize * kFontScale;
    678 }
    679 
    680 float CPDF_VariableText::GetFontDescent(int32_t nFontIndex, float fFontSize) {
    681   return (float)GetTypeDescent(nFontIndex) * fFontSize * kFontScale;
    682 }
    683 
    684 float CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo,
    685                                        float fFontSize) {
    686   return GetFontAscent(GetWordFontIndex(WordInfo), fFontSize);
    687 }
    688 
    689 float CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo,
    690                                         float fFontSize) {
    691   return GetFontDescent(GetWordFontIndex(WordInfo), fFontSize);
    692 }
    693 
    694 float CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo) {
    695   return GetFontAscent(GetWordFontIndex(WordInfo), GetWordFontSize());
    696 }
    697 
    698 float CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo) {
    699   return GetFontDescent(GetWordFontIndex(WordInfo), GetWordFontSize());
    700 }
    701 
    702 float CPDF_VariableText::GetLineLeading() {
    703   return m_fLineLeading;
    704 }
    705 
    706 float CPDF_VariableText::GetLineIndent() {
    707   return 0.0f;
    708 }
    709 
    710 int32_t CPDF_VariableText::GetAlignment() {
    711   return m_nAlignment;
    712 }
    713 
    714 void CPDF_VariableText::ClearSectionRightWords(const CPVT_WordPlace& place) {
    715   CPVT_WordPlace wordplace = AdjustLineHeader(place, true);
    716   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    717     return;
    718 
    719   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    720   if (!pdfium::IndexInBounds(pSection->m_WordArray, wordplace.nWordIndex + 1))
    721     return;
    722 
    723   pSection->m_WordArray.erase(
    724       pSection->m_WordArray.begin() + wordplace.nWordIndex + 1,
    725       pSection->m_WordArray.end());
    726 }
    727 
    728 CPVT_WordPlace CPDF_VariableText::AdjustLineHeader(const CPVT_WordPlace& place,
    729                                                    bool bPrevOrNext) const {
    730   if (place.nWordIndex < 0 && place.nLineIndex > 0)
    731     return bPrevOrNext ? GetPrevWordPlace(place) : GetNextWordPlace(place);
    732   return place;
    733 }
    734 
    735 bool CPDF_VariableText::ClearEmptySection(const CPVT_WordPlace& place) {
    736   if (place.nSecIndex == 0 && m_SectionArray.size() == 1)
    737     return false;
    738 
    739   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    740     return false;
    741 
    742   if (!m_SectionArray[place.nSecIndex]->m_WordArray.empty())
    743     return false;
    744 
    745   m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex);
    746   return true;
    747 }
    748 
    749 void CPDF_VariableText::ClearEmptySections(const CPVT_WordRange& PlaceRange) {
    750   CPVT_WordPlace wordplace;
    751   for (int32_t s = PlaceRange.EndPos.nSecIndex;
    752        s > PlaceRange.BeginPos.nSecIndex; s--) {
    753     wordplace.nSecIndex = s;
    754     ClearEmptySection(wordplace);
    755   }
    756 }
    757 
    758 void CPDF_VariableText::LinkLatterSection(const CPVT_WordPlace& place) {
    759   CPVT_WordPlace oldplace = AdjustLineHeader(place, true);
    760   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex + 1))
    761     return;
    762 
    763   CSection* pNextSection = m_SectionArray[place.nSecIndex + 1].get();
    764   if (pdfium::IndexInBounds(m_SectionArray, oldplace.nSecIndex)) {
    765     CSection* pSection = m_SectionArray[oldplace.nSecIndex].get();
    766     for (auto& pWord : pNextSection->m_WordArray) {
    767       oldplace.nWordIndex++;
    768       pSection->AddWord(oldplace, *pWord);
    769     }
    770   }
    771   m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex + 1);
    772 }
    773 
    774 void CPDF_VariableText::ClearWords(const CPVT_WordRange& PlaceRange) {
    775   CPVT_WordRange NewRange;
    776   NewRange.BeginPos = AdjustLineHeader(PlaceRange.BeginPos, true);
    777   NewRange.EndPos = AdjustLineHeader(PlaceRange.EndPos, true);
    778   for (int32_t s = NewRange.EndPos.nSecIndex; s >= NewRange.BeginPos.nSecIndex;
    779        s--) {
    780     if (pdfium::IndexInBounds(m_SectionArray, s))
    781       m_SectionArray[s]->ClearWords(NewRange);
    782   }
    783 }
    784 
    785 CPVT_WordPlace CPDF_VariableText::ClearLeftWord(const CPVT_WordPlace& place) {
    786   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    787     return place;
    788 
    789   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    790   CPVT_WordPlace leftplace = GetPrevWordPlace(place);
    791   if (leftplace == place)
    792     return place;
    793 
    794   if (leftplace.nSecIndex != place.nSecIndex) {
    795     if (pSection->m_WordArray.empty())
    796       ClearEmptySection(place);
    797     else
    798       LinkLatterSection(leftplace);
    799   } else {
    800     pSection->ClearWord(place);
    801   }
    802   return leftplace;
    803 }
    804 
    805 CPVT_WordPlace CPDF_VariableText::ClearRightWord(const CPVT_WordPlace& place) {
    806   if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
    807     return place;
    808 
    809   CSection* pSection = m_SectionArray[place.nSecIndex].get();
    810   CPVT_WordPlace rightplace = AdjustLineHeader(GetNextWordPlace(place), false);
    811   if (rightplace == place)
    812     return place;
    813 
    814   if (rightplace.nSecIndex != place.nSecIndex)
    815     LinkLatterSection(place);
    816   else
    817     pSection->ClearWord(rightplace);
    818   return place;
    819 }
    820 
    821 void CPDF_VariableText::RearrangeAll() {
    822   Rearrange(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
    823 }
    824 
    825 void CPDF_VariableText::RearrangePart(const CPVT_WordRange& PlaceRange) {
    826   Rearrange(PlaceRange);
    827 }
    828 
    829 CPVT_FloatRect CPDF_VariableText::Rearrange(const CPVT_WordRange& PlaceRange) {
    830   CPVT_FloatRect rcRet;
    831   if (IsValid()) {
    832     if (m_bAutoFontSize) {
    833       SetFontSize(GetAutoFontSize());
    834       rcRet = RearrangeSections(
    835           CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
    836     } else {
    837       rcRet = RearrangeSections(PlaceRange);
    838     }
    839   }
    840   SetContentRect(rcRet);
    841   return rcRet;
    842 }
    843 
    844 float CPDF_VariableText::GetAutoFontSize() {
    845   int32_t nTotal = sizeof(gFontSizeSteps) / sizeof(uint8_t);
    846   if (IsMultiLine())
    847     nTotal /= 4;
    848   if (nTotal <= 0)
    849     return 0;
    850   if (GetPlateWidth() <= 0)
    851     return 0;
    852 
    853   int32_t nLeft = 0;
    854   int32_t nRight = nTotal - 1;
    855   int32_t nMid = nTotal / 2;
    856   while (nLeft <= nRight) {
    857     if (IsBigger(gFontSizeSteps[nMid]))
    858       nRight = nMid - 1;
    859     else
    860       nLeft = nMid + 1;
    861     nMid = (nLeft + nRight) / 2;
    862   }
    863   return (float)gFontSizeSteps[nMid];
    864 }
    865 
    866 bool CPDF_VariableText::IsBigger(float fFontSize) const {
    867   CFX_SizeF szTotal;
    868   for (const auto& pSection : m_SectionArray) {
    869     CFX_SizeF size = pSection->GetSectionSize(fFontSize);
    870     szTotal.width = std::max(size.width, szTotal.width);
    871     szTotal.height += size.height;
    872     if (IsFloatBigger(szTotal.width, GetPlateWidth()) ||
    873         IsFloatBigger(szTotal.height, GetPlateHeight())) {
    874       return true;
    875     }
    876   }
    877   return false;
    878 }
    879 
    880 CPVT_FloatRect CPDF_VariableText::RearrangeSections(
    881     const CPVT_WordRange& PlaceRange) {
    882   CPVT_WordPlace place;
    883   float fPosY = 0;
    884   float fOldHeight;
    885   int32_t nSSecIndex = PlaceRange.BeginPos.nSecIndex;
    886   int32_t nESecIndex = PlaceRange.EndPos.nSecIndex;
    887   CPVT_FloatRect rcRet;
    888   for (int32_t s = 0, sz = pdfium::CollectionSize<int32_t>(m_SectionArray);
    889        s < sz; s++) {
    890     place.nSecIndex = s;
    891     CSection* pSection = m_SectionArray[s].get();
    892     pSection->SecPlace = place;
    893     CPVT_FloatRect rcSec = pSection->m_Rect;
    894     if (s >= nSSecIndex) {
    895       if (s <= nESecIndex) {
    896         rcSec = pSection->Rearrange();
    897         rcSec.top += fPosY;
    898         rcSec.bottom += fPosY;
    899       } else {
    900         fOldHeight = pSection->m_Rect.bottom - pSection->m_Rect.top;
    901         rcSec.top = fPosY;
    902         rcSec.bottom = fPosY + fOldHeight;
    903       }
    904       pSection->m_Rect = rcSec;
    905       pSection->ResetLinePlace();
    906     }
    907     if (s == 0) {
    908       rcRet = rcSec;
    909     } else {
    910       rcRet.left = std::min(rcSec.left, rcRet.left);
    911       rcRet.top = std::min(rcSec.top, rcRet.top);
    912       rcRet.right = std::max(rcSec.right, rcRet.right);
    913       rcRet.bottom = std::max(rcSec.bottom, rcRet.bottom);
    914     }
    915     fPosY += rcSec.Height();
    916   }
    917   return rcRet;
    918 }
    919 
    920 int32_t CPDF_VariableText::GetCharWidth(int32_t nFontIndex,
    921                                         uint16_t Word,
    922                                         uint16_t SubWord) {
    923   if (!m_pVTProvider)
    924     return 0;
    925   uint16_t word = SubWord ? SubWord : Word;
    926   return m_pVTProvider->GetCharWidth(nFontIndex, word);
    927 }
    928 
    929 int32_t CPDF_VariableText::GetTypeAscent(int32_t nFontIndex) {
    930   return m_pVTProvider ? m_pVTProvider->GetTypeAscent(nFontIndex) : 0;
    931 }
    932 
    933 int32_t CPDF_VariableText::GetTypeDescent(int32_t nFontIndex) {
    934   return m_pVTProvider ? m_pVTProvider->GetTypeDescent(nFontIndex) : 0;
    935 }
    936 
    937 int32_t CPDF_VariableText::GetWordFontIndex(uint16_t word,
    938                                             int32_t charset,
    939                                             int32_t nFontIndex) {
    940   return m_pVTProvider
    941              ? m_pVTProvider->GetWordFontIndex(word, charset, nFontIndex)
    942              : -1;
    943 }
    944 
    945 int32_t CPDF_VariableText::GetDefaultFontIndex() {
    946   return m_pVTProvider ? m_pVTProvider->GetDefaultFontIndex() : -1;
    947 }
    948 
    949 bool CPDF_VariableText::IsLatinWord(uint16_t word) {
    950   return m_pVTProvider ? m_pVTProvider->IsLatinWord(word) : false;
    951 }
    952 
    953 CPDF_VariableText::Iterator* CPDF_VariableText::GetIterator() {
    954   if (!m_pVTIterator)
    955     m_pVTIterator = pdfium::MakeUnique<CPDF_VariableText::Iterator>(this);
    956   return m_pVTIterator.get();
    957 }
    958 
    959 void CPDF_VariableText::SetProvider(CPDF_VariableText::Provider* pProvider) {
    960   m_pVTProvider = pProvider;
    961 }
    962 
    963 CFX_PointF CPDF_VariableText::GetBTPoint() const {
    964   return CFX_PointF(m_rcPlate.left, m_rcPlate.top);
    965 }
    966 
    967 CFX_PointF CPDF_VariableText::GetETPoint() const {
    968   return CFX_PointF(m_rcPlate.right, m_rcPlate.bottom);
    969 }
    970 
    971 CFX_PointF CPDF_VariableText::InToOut(const CFX_PointF& point) const {
    972   return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
    973 }
    974 
    975 CFX_PointF CPDF_VariableText::OutToIn(const CFX_PointF& point) const {
    976   return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
    977 }
    978 
    979 CFX_FloatRect CPDF_VariableText::InToOut(const CPVT_FloatRect& rect) const {
    980   CFX_PointF ptLeftTop = InToOut(CFX_PointF(rect.left, rect.top));
    981   CFX_PointF ptRightBottom = InToOut(CFX_PointF(rect.right, rect.bottom));
    982   return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
    983                        ptLeftTop.y);
    984 }
    985 
    986 CPVT_FloatRect CPDF_VariableText::OutToIn(const CFX_FloatRect& rect) const {
    987   CFX_PointF ptLeftTop = OutToIn(CFX_PointF(rect.left, rect.top));
    988   CFX_PointF ptRightBottom = OutToIn(CFX_PointF(rect.right, rect.bottom));
    989   return CPVT_FloatRect(ptLeftTop.x, ptLeftTop.y, ptRightBottom.x,
    990                         ptRightBottom.y);
    991 }
    992