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