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