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