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