Add type cast definitions for CPDF_String.
[pdfium.git] / core / src / fpdftext / fpdf_text_int.cpp
1 // Copyright 2014 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 <ctype.h>
8 #include <algorithm>
9
10 #include "../../../third_party/base/nonstd_unique_ptr.h"
11 #include "../../include/fpdfapi/fpdf_module.h"
12 #include "../../include/fpdfapi/fpdf_page.h"
13 #include "../../include/fpdfapi/fpdf_pageobj.h"
14 #include "../../include/fpdfapi/fpdf_resource.h"
15 #include "../../include/fpdftext/fpdf_text.h"
16 #include "../../include/fxcrt/fx_bidi.h"
17 #include "../../include/fxcrt/fx_ucd.h"
18 #include "text_int.h"
19
20 namespace {
21
22 FX_BOOL _IsIgnoreSpaceCharacter(FX_WCHAR curChar) {
23   if (curChar < 255) {
24     return FALSE;
25   }
26   if ((curChar >= 0x0600 && curChar <= 0x06FF) ||
27       (curChar >= 0xFE70 && curChar <= 0xFEFF) ||
28       (curChar >= 0xFB50 && curChar <= 0xFDFF) ||
29       (curChar >= 0x0400 && curChar <= 0x04FF) ||
30       (curChar >= 0x0500 && curChar <= 0x052F) ||
31       (curChar >= 0xA640 && curChar <= 0xA69F) ||
32       (curChar >= 0x2DE0 && curChar <= 0x2DFF) || curChar == 8467 ||
33       (curChar >= 0x2000 && curChar <= 0x206F)) {
34     return FALSE;
35   }
36   return TRUE;
37 }
38
39 FX_FLOAT _NormalizeThreshold(FX_FLOAT threshold) {
40   if (threshold < 300) {
41     return threshold / 2.0f;
42   }
43   if (threshold < 500) {
44     return threshold / 4.0f;
45   }
46   if (threshold < 700) {
47     return threshold / 5.0f;
48   }
49   return threshold / 6.0f;
50 }
51
52 FX_FLOAT _CalculateBaseSpace(const CPDF_TextObject* pTextObj,
53                              const CFX_AffineMatrix& matrix) {
54   FX_FLOAT baseSpace = 0.0;
55   const int nItems = pTextObj->CountItems();
56   if (pTextObj->m_TextState.GetObject()->m_CharSpace && nItems >= 3) {
57     FX_BOOL bAllChar = TRUE;
58     FX_FLOAT spacing = matrix.TransformDistance(
59         pTextObj->m_TextState.GetObject()->m_CharSpace);
60     baseSpace = spacing;
61     for (int i = 0; i < nItems; i++) {
62       CPDF_TextObjectItem item;
63       pTextObj->GetItemInfo(i, &item);
64       if (item.m_CharCode == (FX_DWORD)-1) {
65         FX_FLOAT fontsize_h = pTextObj->m_TextState.GetFontSizeH();
66         FX_FLOAT kerning = -fontsize_h * item.m_OriginX / 1000;
67         baseSpace = std::min(baseSpace, kerning + spacing);
68         bAllChar = FALSE;
69       }
70     }
71     if (baseSpace < 0.0 || (nItems == 3 && !bAllChar)) {
72       baseSpace = 0.0;
73     }
74   }
75   return baseSpace;
76 }
77
78 }  // namespace
79
80 CPDFText_ParseOptions::CPDFText_ParseOptions()
81     : m_bGetCharCodeOnly(FALSE),
82       m_bNormalizeObjs(TRUE),
83       m_bOutputHyphen(FALSE) {}
84
85 IPDF_TextPage* IPDF_TextPage::CreateTextPage(const CPDF_Page* pPage,
86                                              int flags) {
87   return new CPDF_TextPage(pPage, flags);
88 }
89
90 IPDF_TextPageFind* IPDF_TextPageFind::CreatePageFind(
91     const IPDF_TextPage* pTextPage) {
92   return pTextPage ? new CPDF_TextPageFind(pTextPage) : nullptr;
93 }
94
95 IPDF_LinkExtract* IPDF_LinkExtract::CreateLinkExtract() {
96   return new CPDF_LinkExtract();
97 }
98
99 #define TEXT_BLANK_CHAR L' '
100 #define TEXT_LINEFEED_CHAR L'\n'
101 #define TEXT_RETURN_CHAR L'\r'
102 #define TEXT_EMPTY L""
103 #define TEXT_BLANK L" "
104 #define TEXT_RETURN_LINEFEED L"\r\n"
105 #define TEXT_LINEFEED L"\n"
106 #define TEXT_CHARRATIO_GAPDELTA 0.070
107
108 CPDF_TextPage::CPDF_TextPage(const CPDF_Page* pPage, int flags)
109     : m_pPage(pPage),
110       m_charList(512),
111       m_TempCharList(50),
112       m_parserflag(flags),
113       m_pPreTextObj(nullptr),
114       m_bIsParsed(false),
115       m_TextlineDir(-1),
116       m_CurlineRect(0, 0, 0, 0) {
117   m_TextBuf.EstimateSize(0, 10240);
118   pPage->GetDisplayMatrix(m_DisplayMatrix, 0, 0, (int)pPage->GetPageWidth(),
119                           (int)pPage->GetPageHeight(), 0);
120 }
121
122 void CPDF_TextPage::NormalizeObjects(FX_BOOL bNormalize) {
123   m_ParseOptions.m_bNormalizeObjs = bNormalize;
124 }
125 bool CPDF_TextPage::IsControlChar(const PAGECHAR_INFO& charInfo) {
126   switch (charInfo.m_Unicode) {
127     case 0x2:
128     case 0x3:
129     case 0x93:
130     case 0x94:
131     case 0x96:
132     case 0x97:
133     case 0x98:
134     case 0xfffe:
135       return charInfo.m_Flag != FPDFTEXT_CHAR_HYPHEN;
136     default:
137       return false;
138   }
139 }
140 FX_BOOL CPDF_TextPage::ParseTextPage() {
141   m_bIsParsed = false;
142   if (!m_pPage)
143     return FALSE;
144
145   m_TextBuf.Clear();
146   m_charList.RemoveAll();
147   m_pPreTextObj = NULL;
148   ProcessObject();
149   m_bIsParsed = true;
150   if (!m_ParseOptions.m_bGetCharCodeOnly) {
151     m_CharIndex.RemoveAll();
152     int nCount = m_charList.GetSize();
153     if (nCount) {
154       m_CharIndex.Add(0);
155     }
156     for (int i = 0; i < nCount; i++) {
157       int indexSize = m_CharIndex.GetSize();
158       FX_BOOL bNormal = FALSE;
159       PAGECHAR_INFO charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(i);
160       if (charinfo.m_Flag == FPDFTEXT_CHAR_GENERATED) {
161         bNormal = TRUE;
162       } else if (charinfo.m_Unicode == 0 || IsControlChar(charinfo))
163         bNormal = FALSE;
164       else {
165         bNormal = TRUE;
166       }
167       if (bNormal) {
168         if (indexSize % 2) {
169           m_CharIndex.Add(1);
170         } else {
171           if (indexSize <= 0) {
172             continue;
173           }
174           m_CharIndex.SetAt(indexSize - 1,
175                             m_CharIndex.GetAt(indexSize - 1) + 1);
176         }
177       } else {
178         if (indexSize % 2) {
179           if (indexSize <= 0) {
180             continue;
181           }
182           m_CharIndex.SetAt(indexSize - 1, i + 1);
183         } else {
184           m_CharIndex.Add(i + 1);
185         }
186       }
187     }
188     int indexSize = m_CharIndex.GetSize();
189     if (indexSize % 2) {
190       m_CharIndex.RemoveAt(indexSize - 1);
191     }
192   }
193   return TRUE;
194 }
195 int CPDF_TextPage::CountChars() const {
196   if (m_ParseOptions.m_bGetCharCodeOnly) {
197     return m_TextBuf.GetSize();
198   }
199   return m_charList.GetSize();
200 }
201 int CPDF_TextPage::CharIndexFromTextIndex(int TextIndex) const {
202   int indexSize = m_CharIndex.GetSize();
203   int count = 0;
204   for (int i = 0; i < indexSize; i += 2) {
205     count += m_CharIndex.GetAt(i + 1);
206     if (count > TextIndex) {
207       return TextIndex - count + m_CharIndex.GetAt(i + 1) +
208              m_CharIndex.GetAt(i);
209     }
210   }
211   return -1;
212 }
213 int CPDF_TextPage::TextIndexFromCharIndex(int CharIndex) const {
214   int indexSize = m_CharIndex.GetSize();
215   int count = 0;
216   for (int i = 0; i < indexSize; i += 2) {
217     count += m_CharIndex.GetAt(i + 1);
218     if (m_CharIndex.GetAt(i + 1) + m_CharIndex.GetAt(i) > CharIndex) {
219       if (CharIndex - m_CharIndex.GetAt(i) < 0) {
220         return -1;
221       }
222       return CharIndex - m_CharIndex.GetAt(i) + count -
223              m_CharIndex.GetAt(i + 1);
224     }
225   }
226   return -1;
227 }
228 void CPDF_TextPage::GetRectArray(int start,
229                                  int nCount,
230                                  CFX_RectArray& rectArray) const {
231   if (m_ParseOptions.m_bGetCharCodeOnly) {
232     return;
233   }
234   if (start < 0 || nCount == 0) {
235     return;
236   }
237   if (!m_bIsParsed) {
238     return;
239   }
240   PAGECHAR_INFO info_curchar;
241   CPDF_TextObject* pCurObj = NULL;
242   CFX_FloatRect rect;
243   int curPos = start;
244   FX_BOOL flagNewRect = TRUE;
245   if (nCount + start > m_charList.GetSize() || nCount == -1) {
246     nCount = m_charList.GetSize() - start;
247   }
248   while (nCount--) {
249     info_curchar = *(PAGECHAR_INFO*)m_charList.GetAt(curPos++);
250     if (info_curchar.m_Flag == FPDFTEXT_CHAR_GENERATED) {
251       continue;
252     }
253     if (info_curchar.m_CharBox.Width() < 0.01 ||
254         info_curchar.m_CharBox.Height() < 0.01) {
255       continue;
256     }
257     if (!pCurObj) {
258       pCurObj = info_curchar.m_pTextObj;
259     }
260     if (pCurObj != info_curchar.m_pTextObj) {
261       rectArray.Add(rect);
262       pCurObj = info_curchar.m_pTextObj;
263       flagNewRect = TRUE;
264     }
265     if (flagNewRect) {
266       FX_FLOAT orgX = info_curchar.m_OriginX, orgY = info_curchar.m_OriginY;
267       CFX_AffineMatrix matrix, matrix_reverse;
268       info_curchar.m_pTextObj->GetTextMatrix(&matrix);
269       matrix.Concat(info_curchar.m_Matrix);
270       matrix_reverse.SetReverse(matrix);
271       matrix_reverse.Transform(orgX, orgY);
272       rect.left = info_curchar.m_CharBox.left;
273       rect.right = info_curchar.m_CharBox.right;
274       if (pCurObj->GetFont()->GetTypeDescent()) {
275         rect.bottom = orgY +
276                       pCurObj->GetFont()->GetTypeDescent() *
277                           pCurObj->GetFontSize() / 1000;
278         FX_FLOAT xPosTemp = orgX;
279         matrix.Transform(xPosTemp, rect.bottom);
280       } else {
281         rect.bottom = info_curchar.m_CharBox.bottom;
282       }
283       if (pCurObj->GetFont()->GetTypeAscent()) {
284         rect.top =
285             orgY +
286             pCurObj->GetFont()->GetTypeAscent() * pCurObj->GetFontSize() / 1000;
287         FX_FLOAT xPosTemp =
288             orgX +
289             GetCharWidth(info_curchar.m_CharCode, pCurObj->GetFont()) *
290                 pCurObj->GetFontSize() / 1000;
291         matrix.Transform(xPosTemp, rect.top);
292       } else {
293         rect.top = info_curchar.m_CharBox.top;
294       }
295       flagNewRect = FALSE;
296       rect = info_curchar.m_CharBox;
297       rect.Normalize();
298     } else {
299       info_curchar.m_CharBox.Normalize();
300       if (rect.left > info_curchar.m_CharBox.left) {
301         rect.left = info_curchar.m_CharBox.left;
302       }
303       if (rect.right < info_curchar.m_CharBox.right) {
304         rect.right = info_curchar.m_CharBox.right;
305       }
306       if (rect.top < info_curchar.m_CharBox.top) {
307         rect.top = info_curchar.m_CharBox.top;
308       }
309       if (rect.bottom > info_curchar.m_CharBox.bottom) {
310         rect.bottom = info_curchar.m_CharBox.bottom;
311       }
312     }
313   }
314   rectArray.Add(rect);
315   return;
316 }
317 int CPDF_TextPage::GetIndexAtPos(CPDF_Point point,
318                                  FX_FLOAT xTolerance,
319                                  FX_FLOAT yTolerance) const {
320   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
321     return -3;
322
323   int pos = 0;
324   int NearPos = -1;
325   double xdif = 5000, ydif = 5000;
326   while (pos < m_charList.GetSize()) {
327     PAGECHAR_INFO charinfo = *(PAGECHAR_INFO*)(m_charList.GetAt(pos));
328     CFX_FloatRect charrect = charinfo.m_CharBox;
329     if (charrect.Contains(point.x, point.y)) {
330       break;
331     }
332     if (xTolerance > 0 || yTolerance > 0) {
333       CFX_FloatRect charRectExt;
334       charrect.Normalize();
335       charRectExt.left = charrect.left - xTolerance / 2;
336       charRectExt.right = charrect.right + xTolerance / 2;
337       charRectExt.top = charrect.top + yTolerance / 2;
338       charRectExt.bottom = charrect.bottom - yTolerance / 2;
339       if (charRectExt.Contains(point.x, point.y)) {
340         double curXdif, curYdif;
341         curXdif = FXSYS_fabs(point.x - charrect.left) <
342                           FXSYS_fabs(point.x - charrect.right)
343                       ? FXSYS_fabs(point.x - charrect.left)
344                       : FXSYS_fabs(point.x - charrect.right);
345         curYdif = FXSYS_fabs(point.y - charrect.bottom) <
346                           FXSYS_fabs(point.y - charrect.top)
347                       ? FXSYS_fabs(point.y - charrect.bottom)
348                       : FXSYS_fabs(point.y - charrect.top);
349         if (curYdif + curXdif < xdif + ydif) {
350           ydif = curYdif;
351           xdif = curXdif;
352           NearPos = pos;
353         }
354       }
355     }
356     ++pos;
357   }
358   if (pos >= m_charList.GetSize()) {
359     pos = NearPos;
360   }
361   return pos;
362 }
363 CFX_WideString CPDF_TextPage::GetTextByRect(const CFX_FloatRect& rect) const {
364   CFX_WideString strText;
365   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
366     return strText;
367
368   int nCount = m_charList.GetSize();
369   int pos = 0;
370   FX_FLOAT posy = 0;
371   FX_BOOL IsContainPreChar = FALSE;
372   FX_BOOL ISAddLineFeed = FALSE;
373   while (pos < nCount) {
374     PAGECHAR_INFO charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(pos++);
375     if (IsRectIntersect(rect, charinfo.m_CharBox)) {
376       if (FXSYS_fabs(posy - charinfo.m_OriginY) > 0 && !IsContainPreChar &&
377           ISAddLineFeed) {
378         posy = charinfo.m_OriginY;
379         if (strText.GetLength() > 0) {
380           strText += L"\r\n";
381         }
382       }
383       IsContainPreChar = TRUE;
384       ISAddLineFeed = FALSE;
385       if (charinfo.m_Unicode) {
386         strText += charinfo.m_Unicode;
387       }
388     } else if (charinfo.m_Unicode == 32) {
389       if (IsContainPreChar && charinfo.m_Unicode) {
390         strText += charinfo.m_Unicode;
391         IsContainPreChar = FALSE;
392         ISAddLineFeed = FALSE;
393       }
394     } else {
395       IsContainPreChar = FALSE;
396       ISAddLineFeed = TRUE;
397     }
398   }
399   return strText;
400 }
401 void CPDF_TextPage::GetRectsArrayByRect(const CFX_FloatRect& rect,
402                                         CFX_RectArray& resRectArray) const {
403   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
404     return;
405
406   CFX_FloatRect curRect;
407   FX_BOOL flagNewRect = TRUE;
408   CPDF_TextObject* pCurObj = NULL;
409   int nCount = m_charList.GetSize();
410   int pos = 0;
411   while (pos < nCount) {
412     PAGECHAR_INFO info_curchar = *(PAGECHAR_INFO*)m_charList.GetAt(pos++);
413     if (info_curchar.m_Flag == FPDFTEXT_CHAR_GENERATED) {
414       continue;
415     }
416     if (IsRectIntersect(rect, info_curchar.m_CharBox)) {
417       if (!pCurObj) {
418         pCurObj = info_curchar.m_pTextObj;
419       }
420       if (pCurObj != info_curchar.m_pTextObj) {
421         resRectArray.Add(curRect);
422         pCurObj = info_curchar.m_pTextObj;
423         flagNewRect = TRUE;
424       }
425       if (flagNewRect) {
426         curRect = info_curchar.m_CharBox;
427         flagNewRect = FALSE;
428         curRect.Normalize();
429       } else {
430         info_curchar.m_CharBox.Normalize();
431         if (curRect.left > info_curchar.m_CharBox.left) {
432           curRect.left = info_curchar.m_CharBox.left;
433         }
434         if (curRect.right < info_curchar.m_CharBox.right) {
435           curRect.right = info_curchar.m_CharBox.right;
436         }
437         if (curRect.top < info_curchar.m_CharBox.top) {
438           curRect.top = info_curchar.m_CharBox.top;
439         }
440         if (curRect.bottom > info_curchar.m_CharBox.bottom) {
441           curRect.bottom = info_curchar.m_CharBox.bottom;
442         }
443       }
444     }
445   }
446   resRectArray.Add(curRect);
447   return;
448 }
449 int CPDF_TextPage::GetIndexAtPos(FX_FLOAT x,
450                                  FX_FLOAT y,
451                                  FX_FLOAT xTolerance,
452                                  FX_FLOAT yTolerance) const {
453   if (m_ParseOptions.m_bGetCharCodeOnly) {
454     return -3;
455   }
456   CPDF_Point point(x, y);
457   return GetIndexAtPos(point, xTolerance, yTolerance);
458 }
459 void CPDF_TextPage::GetCharInfo(int index, FPDF_CHAR_INFO& info) const {
460   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
461     return;
462
463   if (index < 0 || index >= m_charList.GetSize())
464     return;
465
466   PAGECHAR_INFO charinfo;
467   charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(index);
468   info.m_Charcode = charinfo.m_CharCode;
469   info.m_OriginX = charinfo.m_OriginX;
470   info.m_OriginY = charinfo.m_OriginY;
471   info.m_Unicode = charinfo.m_Unicode;
472   info.m_Flag = charinfo.m_Flag;
473   info.m_CharBox = charinfo.m_CharBox;
474   info.m_pTextObj = charinfo.m_pTextObj;
475   if (charinfo.m_pTextObj && charinfo.m_pTextObj->GetFont()) {
476     info.m_FontSize = charinfo.m_pTextObj->GetFontSize();
477   }
478   info.m_Matrix.Copy(charinfo.m_Matrix);
479   return;
480 }
481 void CPDF_TextPage::CheckMarkedContentObject(int32_t& start,
482                                              int32_t& nCount) const {
483   PAGECHAR_INFO charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(start);
484   PAGECHAR_INFO charinfo2 =
485       *(PAGECHAR_INFO*)m_charList.GetAt(start + nCount - 1);
486   if (FPDFTEXT_CHAR_PIECE != charinfo.m_Flag &&
487       FPDFTEXT_CHAR_PIECE != charinfo2.m_Flag) {
488     return;
489   }
490   if (FPDFTEXT_CHAR_PIECE == charinfo.m_Flag) {
491     PAGECHAR_INFO charinfo1 = charinfo;
492     int startIndex = start;
493     while (FPDFTEXT_CHAR_PIECE == charinfo1.m_Flag &&
494            charinfo1.m_Index == charinfo.m_Index) {
495       startIndex--;
496       if (startIndex < 0) {
497         break;
498       }
499       charinfo1 = *(PAGECHAR_INFO*)m_charList.GetAt(startIndex);
500     }
501     startIndex++;
502     start = startIndex;
503   }
504   if (FPDFTEXT_CHAR_PIECE == charinfo2.m_Flag) {
505     PAGECHAR_INFO charinfo3 = charinfo2;
506     int endIndex = start + nCount - 1;
507     while (FPDFTEXT_CHAR_PIECE == charinfo3.m_Flag &&
508            charinfo3.m_Index == charinfo2.m_Index) {
509       endIndex++;
510       if (endIndex >= m_charList.GetSize()) {
511         break;
512       }
513       charinfo3 = *(PAGECHAR_INFO*)m_charList.GetAt(endIndex);
514     }
515     endIndex--;
516     nCount = endIndex - start + 1;
517   }
518 }
519 CFX_WideString CPDF_TextPage::GetPageText(int start, int nCount) const {
520   if (!m_bIsParsed || nCount == 0)
521     return L"";
522
523   if (start < 0)
524     start = 0;
525
526   if (nCount == -1) {
527     nCount = m_charList.GetSize() - start;
528     return m_TextBuf.GetWideString().Mid(start,
529                                          m_TextBuf.GetWideString().GetLength());
530   }
531   if (nCount <= 0 || m_charList.GetSize() <= 0) {
532     return L"";
533   }
534   if (nCount + start > m_charList.GetSize() - 1) {
535     nCount = m_charList.GetSize() - start;
536   }
537   if (nCount <= 0) {
538     return L"";
539   }
540   CheckMarkedContentObject(start, nCount);
541   int startindex = 0;
542   PAGECHAR_INFO charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(start);
543   int startOffset = 0;
544   while (charinfo.m_Index == -1) {
545     startOffset++;
546     if (startOffset > nCount || start + startOffset >= m_charList.GetSize()) {
547       return L"";
548     }
549     charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(start + startOffset);
550   }
551   startindex = charinfo.m_Index;
552   charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(start + nCount - 1);
553   int nCountOffset = 0;
554   while (charinfo.m_Index == -1) {
555     nCountOffset++;
556     if (nCountOffset >= nCount) {
557       return L"";
558     }
559     charinfo =
560         *(PAGECHAR_INFO*)m_charList.GetAt(start + nCount - nCountOffset - 1);
561   }
562   nCount = start + nCount - nCountOffset - startindex;
563   if (nCount <= 0) {
564     return L"";
565   }
566   return m_TextBuf.GetWideString().Mid(startindex, nCount);
567 }
568 int CPDF_TextPage::CountRects(int start, int nCount) {
569   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed || start < 0)
570     return -1;
571
572   if (nCount == -1 || nCount + start > m_charList.GetSize()) {
573     nCount = m_charList.GetSize() - start;
574   }
575   m_SelRects.RemoveAll();
576   GetRectArray(start, nCount, m_SelRects);
577   return m_SelRects.GetSize();
578 }
579 void CPDF_TextPage::GetRect(int rectIndex,
580                             FX_FLOAT& left,
581                             FX_FLOAT& top,
582                             FX_FLOAT& right,
583                             FX_FLOAT& bottom) const {
584   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
585     return;
586
587   if (rectIndex < 0 || rectIndex >= m_SelRects.GetSize())
588     return;
589
590   left = m_SelRects.GetAt(rectIndex).left;
591   top = m_SelRects.GetAt(rectIndex).top;
592   right = m_SelRects.GetAt(rectIndex).right;
593   bottom = m_SelRects.GetAt(rectIndex).bottom;
594 }
595 FX_BOOL CPDF_TextPage::GetBaselineRotate(int start, int end, int& Rotate) {
596   if (m_ParseOptions.m_bGetCharCodeOnly) {
597     return FALSE;
598   }
599   if (end == start) {
600     return FALSE;
601   }
602   FX_FLOAT dx, dy;
603   FPDF_CHAR_INFO info1, info2;
604   GetCharInfo(start, info1);
605   GetCharInfo(end, info2);
606   while (info2.m_CharBox.Width() == 0 || info2.m_CharBox.Height() == 0) {
607     end--;
608     if (end <= start) {
609       return FALSE;
610     }
611     GetCharInfo(end, info2);
612   }
613   dx = (info2.m_OriginX - info1.m_OriginX);
614   dy = (info2.m_OriginY - info1.m_OriginY);
615   if (dx == 0) {
616     if (dy > 0) {
617       Rotate = 90;
618     } else if (dy < 0) {
619       Rotate = 270;
620     } else {
621       Rotate = 0;
622     }
623   } else {
624     float a = FXSYS_atan2(dy, dx);
625     Rotate = (int)(a * 180 / FX_PI + 0.5);
626   }
627   if (Rotate < 0) {
628     Rotate = -Rotate;
629   } else if (Rotate > 0) {
630     Rotate = 360 - Rotate;
631   }
632   return TRUE;
633 }
634 FX_BOOL CPDF_TextPage::GetBaselineRotate(const CFX_FloatRect& rect,
635                                          int& Rotate) {
636   if (m_ParseOptions.m_bGetCharCodeOnly) {
637     return FALSE;
638   }
639   int start, end, count,
640       n = CountBoundedSegments(rect.left, rect.top, rect.right, rect.bottom,
641                                TRUE);
642   if (n < 1) {
643     return FALSE;
644   }
645   if (n > 1) {
646     GetBoundedSegment(n - 1, start, count);
647     end = start + count - 1;
648     GetBoundedSegment(0, start, count);
649   } else {
650     GetBoundedSegment(0, start, count);
651     end = start + count - 1;
652   }
653   return GetBaselineRotate(start, end, Rotate);
654 }
655 FX_BOOL CPDF_TextPage::GetBaselineRotate(int rectIndex, int& Rotate) {
656   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
657     return FALSE;
658
659   if (rectIndex < 0 || rectIndex > m_SelRects.GetSize())
660     return FALSE;
661
662   CFX_FloatRect rect = m_SelRects.GetAt(rectIndex);
663   return GetBaselineRotate(rect, Rotate);
664 }
665 int CPDF_TextPage::CountBoundedSegments(FX_FLOAT left,
666                                         FX_FLOAT top,
667                                         FX_FLOAT right,
668                                         FX_FLOAT bottom,
669                                         FX_BOOL bContains) {
670   if (m_ParseOptions.m_bGetCharCodeOnly)
671     return -1;
672
673   m_Segment.RemoveAll();
674   if (!m_bIsParsed)
675     return -1;
676
677   CFX_FloatRect rect(left, bottom, right, top);
678   rect.Normalize();
679   int nCount = m_charList.GetSize();
680   int pos = 0;
681   FPDF_SEGMENT segment;
682   segment.m_Start = 0;
683   segment.m_nCount = 0;
684   int segmentStatus = 0;
685   FX_BOOL IsContainPreChar = FALSE;
686   while (pos < nCount) {
687     PAGECHAR_INFO charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(pos);
688     if (bContains && rect.Contains(charinfo.m_CharBox)) {
689       if (segmentStatus == 0 || segmentStatus == 2) {
690         segment.m_Start = pos;
691         segment.m_nCount = 1;
692         segmentStatus = 1;
693       } else if (segmentStatus == 1) {
694         segment.m_nCount++;
695       }
696       IsContainPreChar = TRUE;
697     } else if (!bContains &&
698                (IsRectIntersect(rect, charinfo.m_CharBox) ||
699                 rect.Contains(charinfo.m_OriginX, charinfo.m_OriginY))) {
700       if (segmentStatus == 0 || segmentStatus == 2) {
701         segment.m_Start = pos;
702         segment.m_nCount = 1;
703         segmentStatus = 1;
704       } else if (segmentStatus == 1) {
705         segment.m_nCount++;
706       }
707       IsContainPreChar = TRUE;
708     } else if (charinfo.m_Unicode == 32) {
709       if (IsContainPreChar == TRUE) {
710         if (segmentStatus == 0 || segmentStatus == 2) {
711           segment.m_Start = pos;
712           segment.m_nCount = 1;
713           segmentStatus = 1;
714         } else if (segmentStatus == 1) {
715           segment.m_nCount++;
716         }
717         IsContainPreChar = FALSE;
718       } else {
719         if (segmentStatus == 1) {
720           segmentStatus = 2;
721           m_Segment.Add(segment);
722           segment.m_Start = 0;
723           segment.m_nCount = 0;
724         }
725       }
726     } else {
727       if (segmentStatus == 1) {
728         segmentStatus = 2;
729         m_Segment.Add(segment);
730         segment.m_Start = 0;
731         segment.m_nCount = 0;
732       }
733       IsContainPreChar = FALSE;
734     }
735     pos++;
736   }
737   if (segmentStatus == 1) {
738     segmentStatus = 2;
739     m_Segment.Add(segment);
740     segment.m_Start = 0;
741     segment.m_nCount = 0;
742   }
743   return m_Segment.GetSize();
744 }
745 void CPDF_TextPage::GetBoundedSegment(int index, int& start, int& count) const {
746   if (m_ParseOptions.m_bGetCharCodeOnly) {
747     return;
748   }
749   if (index < 0 || index >= m_Segment.GetSize()) {
750     return;
751   }
752   start = m_Segment.GetAt(index).m_Start;
753   count = m_Segment.GetAt(index).m_nCount;
754 }
755 int CPDF_TextPage::GetWordBreak(int index, int direction) const {
756   if (m_ParseOptions.m_bGetCharCodeOnly || !m_bIsParsed)
757     return -1;
758
759   if (direction != FPDFTEXT_LEFT && direction != FPDFTEXT_RIGHT)
760     return -1;
761
762   if (index < 0 || index >= m_charList.GetSize())
763     return -1;
764
765   PAGECHAR_INFO charinfo;
766   charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(index);
767   if (charinfo.m_Index == -1 || charinfo.m_Flag == FPDFTEXT_CHAR_GENERATED) {
768     return index;
769   }
770   if (!IsLetter(charinfo.m_Unicode)) {
771     return index;
772   }
773   int breakPos = index;
774   if (direction == FPDFTEXT_LEFT) {
775     while (--breakPos > 0) {
776       charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(breakPos);
777       if (!IsLetter(charinfo.m_Unicode)) {
778         return breakPos;
779       }
780     }
781   } else if (direction == FPDFTEXT_RIGHT) {
782     while (++breakPos < m_charList.GetSize()) {
783       charinfo = *(PAGECHAR_INFO*)m_charList.GetAt(breakPos);
784       if (!IsLetter(charinfo.m_Unicode)) {
785         return breakPos;
786       }
787     }
788   }
789   return breakPos;
790 }
791 int32_t CPDF_TextPage::FindTextlineFlowDirection() {
792   if (!m_pPage) {
793     return -1;
794   }
795   const int32_t nPageWidth = (int32_t)((CPDF_Page*)m_pPage)->GetPageWidth();
796   const int32_t nPageHeight = (int32_t)((CPDF_Page*)m_pPage)->GetPageHeight();
797   CFX_ByteArray nHorizontalMask;
798   if (!nHorizontalMask.SetSize(nPageWidth)) {
799     return -1;
800   }
801   uint8_t* pDataH = nHorizontalMask.GetData();
802   CFX_ByteArray nVerticalMask;
803   if (!nVerticalMask.SetSize(nPageHeight)) {
804     return -1;
805   }
806   uint8_t* pDataV = nVerticalMask.GetData();
807   int32_t index = 0;
808   FX_FLOAT fLineHeight = 0.0f;
809   CPDF_PageObject* pPageObj = NULL;
810   FX_POSITION pos = NULL;
811   pos = m_pPage->GetFirstObjectPosition();
812   if (!pos) {
813     return -1;
814   }
815   while (pos) {
816     pPageObj = m_pPage->GetNextObject(pos);
817     if (NULL == pPageObj) {
818       continue;
819     }
820     if (PDFPAGE_TEXT != pPageObj->m_Type) {
821       continue;
822     }
823     int32_t minH =
824         (int32_t)pPageObj->m_Left < 0 ? 0 : (int32_t)pPageObj->m_Left;
825     int32_t maxH = (int32_t)pPageObj->m_Right > nPageWidth
826                        ? nPageWidth
827                        : (int32_t)pPageObj->m_Right;
828     int32_t minV =
829         (int32_t)pPageObj->m_Bottom < 0 ? 0 : (int32_t)pPageObj->m_Bottom;
830     int32_t maxV = (int32_t)pPageObj->m_Top > nPageHeight
831                        ? nPageHeight
832                        : (int32_t)pPageObj->m_Top;
833     if (minH >= maxH || minV >= maxV) {
834       continue;
835     }
836     FXSYS_memset(pDataH + minH, 1, maxH - minH);
837     FXSYS_memset(pDataV + minV, 1, maxV - minV);
838     if (fLineHeight <= 0.0f) {
839       fLineHeight = pPageObj->m_Top - pPageObj->m_Bottom;
840     }
841     pPageObj = NULL;
842   }
843   int32_t nStartH = 0;
844   int32_t nEndH = 0;
845   FX_FLOAT nSumH = 0.0f;
846   for (index = 0; index < nPageWidth; index++)
847     if (1 == nHorizontalMask[index]) {
848       break;
849     }
850   nStartH = index;
851   for (index = nPageWidth; index > 0; index--)
852     if (1 == nHorizontalMask[index - 1]) {
853       break;
854     }
855   nEndH = index;
856   for (index = nStartH; index < nEndH; index++) {
857     nSumH += nHorizontalMask[index];
858   }
859   nSumH /= nEndH - nStartH;
860   int32_t nStartV = 0;
861   int32_t nEndV = 0;
862   FX_FLOAT nSumV = 0.0f;
863   for (index = 0; index < nPageHeight; index++)
864     if (1 == nVerticalMask[index]) {
865       break;
866     }
867   nStartV = index;
868   for (index = nPageHeight; index > 0; index--)
869     if (1 == nVerticalMask[index - 1]) {
870       break;
871     }
872   nEndV = index;
873   for (index = nStartV; index < nEndV; index++) {
874     nSumV += nVerticalMask[index];
875   }
876   nSumV /= nEndV - nStartV;
877   if ((nEndV - nStartV) < (int32_t)(2 * fLineHeight)) {
878     return 0;
879   }
880   if ((nEndH - nStartH) < (int32_t)(2 * fLineHeight)) {
881     return 1;
882   }
883   if (nSumH > 0.8f) {
884     return 0;
885   }
886   if (nSumH - nSumV > 0.0f) {
887     return 0;
888   }
889   if (nSumV - nSumH > 0.0f) {
890     return 1;
891   }
892   return -1;
893 }
894 void CPDF_TextPage::ProcessObject() {
895   CPDF_PageObject* pPageObj = NULL;
896   if (!m_pPage) {
897     return;
898   }
899   FX_POSITION pos;
900   pos = m_pPage->GetFirstObjectPosition();
901   if (!pos) {
902     return;
903   }
904   m_TextlineDir = FindTextlineFlowDirection();
905   int nCount = 0;
906   while (pos) {
907     pPageObj = m_pPage->GetNextObject(pos);
908     if (pPageObj) {
909       if (pPageObj->m_Type == PDFPAGE_TEXT) {
910         CFX_AffineMatrix matrix;
911         ProcessTextObject((CPDF_TextObject*)pPageObj, matrix, pos);
912         nCount++;
913       } else if (pPageObj->m_Type == PDFPAGE_FORM) {
914         CFX_AffineMatrix formMatrix(1, 0, 0, 1, 0, 0);
915         ProcessFormObject((CPDF_FormObject*)pPageObj, formMatrix);
916       }
917     }
918     pPageObj = NULL;
919   }
920   int count = m_LineObj.GetSize();
921   for (int i = 0; i < count; i++) {
922     ProcessTextObject(m_LineObj.GetAt(i));
923   }
924   m_LineObj.RemoveAll();
925   CloseTempLine();
926 }
927 void CPDF_TextPage::ProcessFormObject(CPDF_FormObject* pFormObj,
928                                       const CFX_AffineMatrix& formMatrix) {
929   CPDF_PageObject* pPageObj = NULL;
930   FX_POSITION pos;
931   if (!pFormObj) {
932     return;
933   }
934   pos = pFormObj->m_pForm->GetFirstObjectPosition();
935   if (!pos) {
936     return;
937   }
938   CFX_AffineMatrix curFormMatrix;
939   curFormMatrix.Copy(pFormObj->m_FormMatrix);
940   curFormMatrix.Concat(formMatrix);
941   while (pos) {
942     pPageObj = pFormObj->m_pForm->GetNextObject(pos);
943     if (pPageObj) {
944       if (pPageObj->m_Type == PDFPAGE_TEXT) {
945         ProcessTextObject((CPDF_TextObject*)pPageObj, curFormMatrix, pos);
946       } else if (pPageObj->m_Type == PDFPAGE_FORM) {
947         ProcessFormObject((CPDF_FormObject*)pPageObj, curFormMatrix);
948       }
949     }
950     pPageObj = NULL;
951   }
952 }
953 int CPDF_TextPage::GetCharWidth(FX_DWORD charCode, CPDF_Font* pFont) const {
954   if (charCode == -1) {
955     return 0;
956   }
957   int w = pFont->GetCharWidthF(charCode);
958   if (w == 0) {
959     CFX_ByteString str;
960     pFont->AppendChar(str, charCode);
961     w = pFont->GetStringWidth(str, 1);
962     if (w == 0) {
963       FX_RECT BBox;
964       pFont->GetCharBBox(charCode, BBox);
965       w = BBox.right - BBox.left;
966     }
967   }
968   return w;
969 }
970 void CPDF_TextPage::OnPiece(CFX_BidiChar* pBidi, CFX_WideString& str) {
971   int32_t start, count;
972   CFX_BidiChar::Direction ret = pBidi->GetBidiInfo(&start, &count);
973   if (ret == CFX_BidiChar::RIGHT) {
974     for (int i = start + count - 1; i >= start; i--) {
975       m_TextBuf.AppendChar(str.GetAt(i));
976       m_charList.Add(*(PAGECHAR_INFO*)m_TempCharList.GetAt(i));
977     }
978   } else {
979     int end = start + count;
980     for (int i = start; i < end; i++) {
981       m_TextBuf.AppendChar(str.GetAt(i));
982       m_charList.Add(*(PAGECHAR_INFO*)m_TempCharList.GetAt(i));
983     }
984   }
985 }
986 void CPDF_TextPage::AddCharInfoByLRDirection(CFX_WideString& str, int i) {
987   PAGECHAR_INFO Info = *(PAGECHAR_INFO*)m_TempCharList.GetAt(i);
988   FX_WCHAR wChar = str.GetAt(i);
989   if (!IsControlChar(Info)) {
990     Info.m_Index = m_TextBuf.GetLength();
991     if (wChar >= 0xFB00 && wChar <= 0xFB06) {
992       FX_WCHAR* pDst = NULL;
993       FX_STRSIZE nCount = FX_Unicode_GetNormalization(wChar, pDst);
994       if (nCount >= 1) {
995         pDst = FX_Alloc(FX_WCHAR, nCount);
996         FX_Unicode_GetNormalization(wChar, pDst);
997         for (int nIndex = 0; nIndex < nCount; nIndex++) {
998           PAGECHAR_INFO Info2 = Info;
999           Info2.m_Unicode = pDst[nIndex];
1000           Info2.m_Flag = FPDFTEXT_CHAR_PIECE;
1001           m_TextBuf.AppendChar(Info2.m_Unicode);
1002           if (!m_ParseOptions.m_bGetCharCodeOnly) {
1003             m_charList.Add(Info2);
1004           }
1005         }
1006         FX_Free(pDst);
1007         return;
1008       }
1009     }
1010     m_TextBuf.AppendChar(wChar);
1011   } else {
1012     Info.m_Index = -1;
1013   }
1014   if (!m_ParseOptions.m_bGetCharCodeOnly) {
1015     m_charList.Add(Info);
1016   }
1017 }
1018 void CPDF_TextPage::AddCharInfoByRLDirection(CFX_WideString& str, int i) {
1019   PAGECHAR_INFO Info = *(PAGECHAR_INFO*)m_TempCharList.GetAt(i);
1020   if (!IsControlChar(Info)) {
1021     Info.m_Index = m_TextBuf.GetLength();
1022     FX_WCHAR wChar = FX_GetMirrorChar(str.GetAt(i), TRUE, FALSE);
1023     FX_WCHAR* pDst = NULL;
1024     FX_STRSIZE nCount = FX_Unicode_GetNormalization(wChar, pDst);
1025     if (nCount >= 1) {
1026       pDst = FX_Alloc(FX_WCHAR, nCount);
1027       FX_Unicode_GetNormalization(wChar, pDst);
1028       for (int nIndex = 0; nIndex < nCount; nIndex++) {
1029         PAGECHAR_INFO Info2 = Info;
1030         Info2.m_Unicode = pDst[nIndex];
1031         Info2.m_Flag = FPDFTEXT_CHAR_PIECE;
1032         m_TextBuf.AppendChar(Info2.m_Unicode);
1033         if (!m_ParseOptions.m_bGetCharCodeOnly) {
1034           m_charList.Add(Info2);
1035         }
1036       }
1037       FX_Free(pDst);
1038       return;
1039     }
1040     Info.m_Unicode = wChar;
1041     m_TextBuf.AppendChar(Info.m_Unicode);
1042   } else {
1043     Info.m_Index = -1;
1044   }
1045   if (!m_ParseOptions.m_bGetCharCodeOnly) {
1046     m_charList.Add(Info);
1047   }
1048 }
1049 void CPDF_TextPage::CloseTempLine() {
1050   int count1 = m_TempCharList.GetSize();
1051   if (count1 <= 0) {
1052     return;
1053   }
1054   nonstd::unique_ptr<CFX_BidiChar> pBidiChar(new CFX_BidiChar);
1055   CFX_WideString str = m_TempTextBuf.GetWideString();
1056   CFX_WordArray order;
1057   FX_BOOL bR2L = FALSE;
1058   int32_t start = 0, count = 0;
1059   int nR2L = 0, nL2R = 0;
1060   FX_BOOL bPrevSpace = FALSE;
1061   for (int i = 0; i < str.GetLength(); i++) {
1062     if (str.GetAt(i) == 32) {
1063       if (bPrevSpace) {
1064         m_TempTextBuf.Delete(i, 1);
1065         m_TempCharList.Delete(i);
1066         str.Delete(i);
1067         count1--;
1068         i--;
1069         continue;
1070       }
1071       bPrevSpace = TRUE;
1072     } else {
1073       bPrevSpace = FALSE;
1074     }
1075     if (pBidiChar->AppendChar(str.GetAt(i))) {
1076       CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
1077       order.Add(start);
1078       order.Add(count);
1079       order.Add(ret);
1080       if (!bR2L) {
1081         if (ret == CFX_BidiChar::RIGHT) {
1082           nR2L++;
1083         } else if (ret == CFX_BidiChar::LEFT) {
1084           nL2R++;
1085         }
1086       }
1087     }
1088   }
1089   if (pBidiChar->EndChar()) {
1090     CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
1091     order.Add(start);
1092     order.Add(count);
1093     order.Add(ret);
1094     if (!bR2L) {
1095       if (ret == CFX_BidiChar::RIGHT) {
1096         nR2L++;
1097       } else if (ret == CFX_BidiChar::LEFT) {
1098         nL2R++;
1099       }
1100     }
1101   }
1102   if (nR2L > 0 && nR2L >= nL2R) {
1103     bR2L = TRUE;
1104   }
1105   if (m_parserflag == FPDFTEXT_RLTB || bR2L) {
1106     int count = order.GetSize();
1107     for (int i = count - 1; i > 0; i -= 3) {
1108       int ret = order.GetAt(i);
1109       int start = order.GetAt(i - 2);
1110       int count1 = order.GetAt(i - 1);
1111       if (ret == 2 || ret == 0) {
1112         for (int j = start + count1 - 1; j >= start; j--) {
1113           AddCharInfoByRLDirection(str, j);
1114         }
1115       } else {
1116         int j = i;
1117         FX_BOOL bSymbol = FALSE;
1118         while (j > 0 && order.GetAt(j) != 2) {
1119           bSymbol = !order.GetAt(j);
1120           j -= 3;
1121         }
1122         int end = start + count1;
1123         int n = 0;
1124         if (bSymbol) {
1125           n = j + 6;
1126         } else {
1127           n = j + 3;
1128         }
1129         if (n >= i) {
1130           for (int m = start; m < end; m++) {
1131             AddCharInfoByLRDirection(str, m);
1132           }
1133         } else {
1134           j = i;
1135           i = n;
1136           for (; n <= j; n += 3) {
1137             int start = order.GetAt(n - 2);
1138             int count1 = order.GetAt(n - 1);
1139             int end = start + count1;
1140             for (int m = start; m < end; m++) {
1141               AddCharInfoByLRDirection(str, m);
1142             }
1143           }
1144         }
1145       }
1146     }
1147   } else {
1148     int count = order.GetSize();
1149     FX_BOOL bL2R = FALSE;
1150     for (int i = 0; i < count; i += 3) {
1151       int ret = order.GetAt(i + 2);
1152       int start = order.GetAt(i);
1153       int count1 = order.GetAt(i + 1);
1154       if (ret == 2 || (i == 0 && ret == 0 && !bL2R)) {
1155         int j = i + 3;
1156         while (bR2L && j < count) {
1157           if (order.GetAt(j + 2) == 1) {
1158             break;
1159           } else {
1160             j += 3;
1161           }
1162         }
1163         if (j == 3) {
1164           i = -3;
1165           bL2R = TRUE;
1166           continue;
1167         }
1168         int end = m_TempCharList.GetSize() - 1;
1169         if (j < count) {
1170           end = order.GetAt(j) - 1;
1171         }
1172         i = j - 3;
1173         for (int n = end; n >= start; n--) {
1174           AddCharInfoByRLDirection(str, n);
1175         }
1176       } else {
1177         int end = start + count1;
1178         for (int n = start; n < end; n++) {
1179           AddCharInfoByLRDirection(str, n);
1180         }
1181       }
1182     }
1183   }
1184   order.RemoveAll();
1185   m_TempCharList.RemoveAll();
1186   m_TempTextBuf.Delete(0, m_TempTextBuf.GetLength());
1187 }
1188 void CPDF_TextPage::ProcessTextObject(CPDF_TextObject* pTextObj,
1189                                       const CFX_AffineMatrix& formMatrix,
1190                                       FX_POSITION ObjPos) {
1191   CFX_FloatRect re(pTextObj->m_Left, pTextObj->m_Bottom, pTextObj->m_Right,
1192                    pTextObj->m_Top);
1193   if (FXSYS_fabs(pTextObj->m_Right - pTextObj->m_Left) < 0.01f) {
1194     return;
1195   }
1196   int count = m_LineObj.GetSize();
1197   PDFTEXT_Obj Obj;
1198   Obj.m_pTextObj = pTextObj;
1199   Obj.m_formMatrix = formMatrix;
1200   if (count == 0) {
1201     m_LineObj.Add(Obj);
1202     return;
1203   }
1204   if (IsSameAsPreTextObject(pTextObj, ObjPos)) {
1205     return;
1206   }
1207   PDFTEXT_Obj prev_Obj = m_LineObj.GetAt(count - 1);
1208   CPDF_TextObjectItem item;
1209   int nItem = prev_Obj.m_pTextObj->CountItems();
1210   prev_Obj.m_pTextObj->GetItemInfo(nItem - 1, &item);
1211   FX_FLOAT prev_width =
1212       GetCharWidth(item.m_CharCode, prev_Obj.m_pTextObj->GetFont()) *
1213       prev_Obj.m_pTextObj->GetFontSize() / 1000;
1214   CFX_AffineMatrix prev_matrix;
1215   prev_Obj.m_pTextObj->GetTextMatrix(&prev_matrix);
1216   prev_width = FXSYS_fabs(prev_width);
1217   prev_matrix.Concat(prev_Obj.m_formMatrix);
1218   prev_width = prev_matrix.TransformDistance(prev_width);
1219   pTextObj->GetItemInfo(0, &item);
1220   FX_FLOAT this_width = GetCharWidth(item.m_CharCode, pTextObj->GetFont()) *
1221                         pTextObj->GetFontSize() / 1000;
1222   this_width = FXSYS_fabs(this_width);
1223   CFX_AffineMatrix this_matrix;
1224   pTextObj->GetTextMatrix(&this_matrix);
1225   this_width = FXSYS_fabs(this_width);
1226   this_matrix.Concat(formMatrix);
1227   this_width = this_matrix.TransformDistance(this_width);
1228   FX_FLOAT threshold =
1229       prev_width > this_width ? prev_width / 4 : this_width / 4;
1230   FX_FLOAT prev_x = prev_Obj.m_pTextObj->GetPosX(),
1231            prev_y = prev_Obj.m_pTextObj->GetPosY();
1232   prev_Obj.m_formMatrix.Transform(prev_x, prev_y);
1233   m_DisplayMatrix.Transform(prev_x, prev_y);
1234   FX_FLOAT this_x = pTextObj->GetPosX(), this_y = pTextObj->GetPosY();
1235   formMatrix.Transform(this_x, this_y);
1236   m_DisplayMatrix.Transform(this_x, this_y);
1237   if (FXSYS_fabs(this_y - prev_y) > threshold * 2) {
1238     for (int i = 0; i < count; i++) {
1239       ProcessTextObject(m_LineObj.GetAt(i));
1240     }
1241     m_LineObj.RemoveAll();
1242     m_LineObj.Add(Obj);
1243     return;
1244   }
1245   int i = 0;
1246   if (m_ParseOptions.m_bNormalizeObjs) {
1247     for (i = count - 1; i >= 0; i--) {
1248       PDFTEXT_Obj prev_Obj = m_LineObj.GetAt(i);
1249       CFX_AffineMatrix prev_matrix;
1250       prev_Obj.m_pTextObj->GetTextMatrix(&prev_matrix);
1251       FX_FLOAT Prev_x = prev_Obj.m_pTextObj->GetPosX(),
1252                Prev_y = prev_Obj.m_pTextObj->GetPosY();
1253       prev_Obj.m_formMatrix.Transform(Prev_x, Prev_y);
1254       m_DisplayMatrix.Transform(Prev_x, Prev_y);
1255       if (this_x >= Prev_x) {
1256         if (i == count - 1) {
1257           m_LineObj.Add(Obj);
1258         } else {
1259           m_LineObj.InsertAt(i + 1, Obj);
1260         }
1261         break;
1262       }
1263     }
1264     if (i < 0) {
1265       m_LineObj.InsertAt(0, Obj);
1266     }
1267   } else {
1268     m_LineObj.Add(Obj);
1269   }
1270 }
1271 int32_t CPDF_TextPage::PreMarkedContent(PDFTEXT_Obj Obj) {
1272   CPDF_TextObject* pTextObj = Obj.m_pTextObj;
1273   CPDF_ContentMarkData* pMarkData =
1274       (CPDF_ContentMarkData*)pTextObj->m_ContentMark.GetObject();
1275   if (!pMarkData) {
1276     return FPDFTEXT_MC_PASS;
1277   }
1278   int nContentMark = pMarkData->CountItems();
1279   if (nContentMark < 1) {
1280     return FPDFTEXT_MC_PASS;
1281   }
1282   CFX_WideString actText;
1283   FX_BOOL bExist = FALSE;
1284   CPDF_Dictionary* pDict = NULL;
1285   int n = 0;
1286   for (n = 0; n < nContentMark; n++) {
1287     CPDF_ContentMarkItem& item = pMarkData->GetItem(n);
1288     CFX_ByteString tagStr = (CFX_ByteString)item.GetName();
1289     pDict = ToDictionary(static_cast<CPDF_Object*>(item.GetParam()));
1290     CPDF_String* temp =
1291         ToString(pDict ? pDict->GetElement(FX_BSTRC("ActualText")) : nullptr);
1292     if (temp) {
1293       bExist = TRUE;
1294       actText = temp->GetUnicodeText();
1295     }
1296   }
1297   if (!bExist) {
1298     return FPDFTEXT_MC_PASS;
1299   }
1300   if (m_pPreTextObj) {
1301     if (CPDF_ContentMarkData* pPreMarkData =
1302             (CPDF_ContentMarkData*)m_pPreTextObj->m_ContentMark.GetObject()) {
1303       if (pPreMarkData->CountItems() == n) {
1304         CPDF_ContentMarkItem& item = pPreMarkData->GetItem(n - 1);
1305         if (pDict == item.GetParam()) {
1306           return FPDFTEXT_MC_DONE;
1307         }
1308       }
1309     }
1310   }
1311   CPDF_Font* pFont = pTextObj->GetFont();
1312   FX_STRSIZE nItems = actText.GetLength();
1313   if (nItems < 1) {
1314     return FPDFTEXT_MC_PASS;
1315   }
1316   bExist = FALSE;
1317   for (FX_STRSIZE i = 0; i < nItems; i++) {
1318     FX_WCHAR wChar = actText.GetAt(i);
1319     if (-1 == pFont->CharCodeFromUnicode(wChar)) {
1320       continue;
1321     } else {
1322       bExist = TRUE;
1323       break;
1324     }
1325   }
1326   if (!bExist) {
1327     return FPDFTEXT_MC_PASS;
1328   }
1329   bExist = FALSE;
1330   for (FX_STRSIZE i = 0; i < nItems; i++) {
1331     FX_WCHAR wChar = actText.GetAt(i);
1332     if ((wChar > 0x80 && wChar < 0xFFFD) || (wChar <= 0x80 && isprint(wChar))) {
1333       bExist = TRUE;
1334       break;
1335     }
1336   }
1337   if (!bExist) {
1338     return FPDFTEXT_MC_DONE;
1339   }
1340   return FPDFTEXT_MC_DELAY;
1341 }
1342 void CPDF_TextPage::ProcessMarkedContent(PDFTEXT_Obj Obj) {
1343   CPDF_TextObject* pTextObj = Obj.m_pTextObj;
1344   CPDF_ContentMarkData* pMarkData =
1345       (CPDF_ContentMarkData*)pTextObj->m_ContentMark.GetObject();
1346   if (!pMarkData) {
1347     return;
1348   }
1349   int nContentMark = pMarkData->CountItems();
1350   if (nContentMark < 1) {
1351     return;
1352   }
1353   CFX_WideString actText;
1354   CPDF_Dictionary* pDict = NULL;
1355   int n = 0;
1356   for (n = 0; n < nContentMark; n++) {
1357     CPDF_ContentMarkItem& item = pMarkData->GetItem(n);
1358     CFX_ByteString tagStr = (CFX_ByteString)item.GetName();
1359     pDict = ToDictionary(static_cast<CPDF_Object*>(item.GetParam()));
1360     CPDF_String* temp =
1361         ToString(pDict ? pDict->GetElement(FX_BSTRC("ActualText")) : nullptr);
1362     if (temp) {
1363       actText = temp->GetUnicodeText();
1364     }
1365   }
1366   FX_STRSIZE nItems = actText.GetLength();
1367   if (nItems < 1) {
1368     return;
1369   }
1370   CPDF_Font* pFont = pTextObj->GetFont();
1371   CFX_AffineMatrix formMatrix = Obj.m_formMatrix;
1372   CFX_AffineMatrix matrix;
1373   pTextObj->GetTextMatrix(&matrix);
1374   matrix.Concat(formMatrix);
1375   FX_FLOAT fPosX = pTextObj->GetPosX();
1376   FX_FLOAT fPosY = pTextObj->GetPosY();
1377   int nCharInfoIndex = m_TextBuf.GetLength();
1378   CFX_FloatRect charBox;
1379   charBox.top = pTextObj->m_Top;
1380   charBox.left = pTextObj->m_Left;
1381   charBox.right = pTextObj->m_Right;
1382   charBox.bottom = pTextObj->m_Bottom;
1383   for (FX_STRSIZE k = 0; k < nItems; k++) {
1384     FX_WCHAR wChar = actText.GetAt(k);
1385     if (wChar <= 0x80 && !isprint(wChar)) {
1386       wChar = 0x20;
1387     }
1388     if (wChar >= 0xFFFD) {
1389       continue;
1390     }
1391     PAGECHAR_INFO charinfo;
1392     charinfo.m_OriginX = fPosX;
1393     charinfo.m_OriginY = fPosY;
1394     charinfo.m_Index = nCharInfoIndex;
1395     charinfo.m_Unicode = wChar;
1396     charinfo.m_CharCode = pFont->CharCodeFromUnicode(wChar);
1397     charinfo.m_Flag = FPDFTEXT_CHAR_PIECE;
1398     charinfo.m_pTextObj = pTextObj;
1399     charinfo.m_CharBox.top = charBox.top;
1400     charinfo.m_CharBox.left = charBox.left;
1401     charinfo.m_CharBox.right = charBox.right;
1402     charinfo.m_CharBox.bottom = charBox.bottom;
1403     charinfo.m_Matrix.Copy(matrix);
1404     m_TempTextBuf.AppendChar(wChar);
1405     m_TempCharList.Add(charinfo);
1406   }
1407 }
1408 void CPDF_TextPage::FindPreviousTextObject(void) {
1409   if (m_TempCharList.GetSize() < 1 && m_charList.GetSize() < 1) {
1410     return;
1411   }
1412   PAGECHAR_INFO preChar;
1413   if (m_TempCharList.GetSize() >= 1) {
1414     preChar =
1415         *(PAGECHAR_INFO*)m_TempCharList.GetAt(m_TempCharList.GetSize() - 1);
1416   } else {
1417     preChar = *(PAGECHAR_INFO*)m_charList.GetAt(m_charList.GetSize() - 1);
1418   }
1419   if (preChar.m_pTextObj) {
1420     m_pPreTextObj = preChar.m_pTextObj;
1421   }
1422 }
1423 void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) {
1424   CPDF_TextObject* pTextObj = Obj.m_pTextObj;
1425   if (FXSYS_fabs(pTextObj->m_Right - pTextObj->m_Left) < 0.01f) {
1426     return;
1427   }
1428   CFX_AffineMatrix formMatrix = Obj.m_formMatrix;
1429   CPDF_Font* pFont = pTextObj->GetFont();
1430   CFX_AffineMatrix matrix;
1431   pTextObj->GetTextMatrix(&matrix);
1432   matrix.Concat(formMatrix);
1433   int32_t bPreMKC = PreMarkedContent(Obj);
1434   if (FPDFTEXT_MC_DONE == bPreMKC) {
1435     m_pPreTextObj = pTextObj;
1436     m_perMatrix.Copy(formMatrix);
1437     return;
1438   }
1439   int result = 0;
1440   if (m_pPreTextObj) {
1441     result = ProcessInsertObject(pTextObj, formMatrix);
1442     if (2 == result) {
1443       m_CurlineRect =
1444           CFX_FloatRect(Obj.m_pTextObj->m_Left, Obj.m_pTextObj->m_Bottom,
1445                         Obj.m_pTextObj->m_Right, Obj.m_pTextObj->m_Top);
1446     } else {
1447       m_CurlineRect.Union(
1448           CFX_FloatRect(Obj.m_pTextObj->m_Left, Obj.m_pTextObj->m_Bottom,
1449                         Obj.m_pTextObj->m_Right, Obj.m_pTextObj->m_Top));
1450     }
1451     PAGECHAR_INFO generateChar;
1452     if (result == 1) {
1453       if (GenerateCharInfo(TEXT_BLANK_CHAR, generateChar)) {
1454         if (!formMatrix.IsIdentity()) {
1455           generateChar.m_Matrix.Copy(formMatrix);
1456         }
1457         m_TempTextBuf.AppendChar(TEXT_BLANK_CHAR);
1458         m_TempCharList.Add(generateChar);
1459       }
1460     } else if (result == 2) {
1461       CloseTempLine();
1462       if (m_TextBuf.GetSize()) {
1463         if (m_ParseOptions.m_bGetCharCodeOnly) {
1464           m_TextBuf.AppendChar(TEXT_RETURN_CHAR);
1465           m_TextBuf.AppendChar(TEXT_LINEFEED_CHAR);
1466         } else {
1467           if (GenerateCharInfo(TEXT_RETURN_CHAR, generateChar)) {
1468             m_TextBuf.AppendChar(TEXT_RETURN_CHAR);
1469             if (!formMatrix.IsIdentity()) {
1470               generateChar.m_Matrix.Copy(formMatrix);
1471             }
1472             m_charList.Add(generateChar);
1473           }
1474           if (GenerateCharInfo(TEXT_LINEFEED_CHAR, generateChar)) {
1475             m_TextBuf.AppendChar(TEXT_LINEFEED_CHAR);
1476             if (!formMatrix.IsIdentity()) {
1477               generateChar.m_Matrix.Copy(formMatrix);
1478             }
1479             m_charList.Add(generateChar);
1480           }
1481         }
1482       }
1483     } else if (result == 3 && !m_ParseOptions.m_bOutputHyphen) {
1484       int32_t nChars = pTextObj->CountChars();
1485       if (nChars == 1) {
1486         CPDF_TextObjectItem item;
1487         pTextObj->GetCharInfo(0, &item);
1488         CFX_WideString wstrItem =
1489             pTextObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
1490         if (wstrItem.IsEmpty()) {
1491           wstrItem += (FX_WCHAR)item.m_CharCode;
1492         }
1493         FX_WCHAR curChar = wstrItem.GetAt(0);
1494         if (0x2D == curChar || 0xAD == curChar) {
1495           return;
1496         }
1497       }
1498       while (m_TempTextBuf.GetSize() > 0 &&
1499              m_TempTextBuf.GetWideString().GetAt(m_TempTextBuf.GetLength() -
1500                                                  1) == 0x20) {
1501         m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
1502         m_TempCharList.Delete(m_TempCharList.GetSize() - 1);
1503       }
1504       PAGECHAR_INFO* cha =
1505           (PAGECHAR_INFO*)m_TempCharList.GetAt(m_TempCharList.GetSize() - 1);
1506       m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
1507       cha->m_Unicode = 0x2;
1508       cha->m_Flag = FPDFTEXT_CHAR_HYPHEN;
1509       m_TempTextBuf.AppendChar(0xfffe);
1510     }
1511   } else {
1512     m_CurlineRect =
1513         CFX_FloatRect(Obj.m_pTextObj->m_Left, Obj.m_pTextObj->m_Bottom,
1514                       Obj.m_pTextObj->m_Right, Obj.m_pTextObj->m_Top);
1515   }
1516   if (FPDFTEXT_MC_DELAY == bPreMKC) {
1517     ProcessMarkedContent(Obj);
1518     m_pPreTextObj = pTextObj;
1519     m_perMatrix.Copy(formMatrix);
1520     return;
1521   }
1522   m_pPreTextObj = pTextObj;
1523   m_perMatrix.Copy(formMatrix);
1524   int nItems = pTextObj->CountItems();
1525   FX_FLOAT baseSpace = _CalculateBaseSpace(pTextObj, matrix);
1526
1527   const FX_BOOL bR2L = IsRightToLeft(pTextObj, pFont, nItems);
1528   const FX_BOOL bIsBidiAndMirrorInverse =
1529       bR2L && (matrix.a * matrix.d - matrix.b * matrix.c) < 0;
1530   int32_t iBufStartAppend = m_TempTextBuf.GetLength();
1531   int32_t iCharListStartAppend = m_TempCharList.GetSize();
1532
1533   FX_FLOAT spacing = 0;
1534   for (int i = 0; i < nItems; i++) {
1535     CPDF_TextObjectItem item;
1536     PAGECHAR_INFO charinfo;
1537     charinfo.m_OriginX = 0;
1538     charinfo.m_OriginY = 0;
1539     pTextObj->GetItemInfo(i, &item);
1540     if (item.m_CharCode == (FX_DWORD)-1) {
1541       CFX_WideString str = m_TempTextBuf.GetWideString();
1542       if (str.IsEmpty()) {
1543         str = m_TextBuf.GetWideString();
1544       }
1545       if (str.IsEmpty() || str.GetAt(str.GetLength() - 1) == TEXT_BLANK_CHAR) {
1546         continue;
1547       }
1548       FX_FLOAT fontsize_h = pTextObj->m_TextState.GetFontSizeH();
1549       spacing = -fontsize_h * item.m_OriginX / 1000;
1550       continue;
1551     }
1552     FX_FLOAT charSpace = pTextObj->m_TextState.GetObject()->m_CharSpace;
1553     if (charSpace > 0.001) {
1554       spacing += matrix.TransformDistance(charSpace);
1555     } else if (charSpace < -0.001) {
1556       spacing -= matrix.TransformDistance(FXSYS_fabs(charSpace));
1557     }
1558     spacing -= baseSpace;
1559     if (spacing && i > 0) {
1560       int last_width = 0;
1561       FX_FLOAT fontsize_h = pTextObj->m_TextState.GetFontSizeH();
1562       FX_DWORD space_charcode = pFont->CharCodeFromUnicode(' ');
1563       FX_FLOAT threshold = 0;
1564       if (space_charcode != -1) {
1565         threshold = fontsize_h * pFont->GetCharWidthF(space_charcode) / 1000;
1566       }
1567       if (threshold > fontsize_h / 3) {
1568         threshold = 0;
1569       } else {
1570         threshold /= 2;
1571       }
1572       if (threshold == 0) {
1573         threshold = fontsize_h;
1574         int this_width = FXSYS_abs(GetCharWidth(item.m_CharCode, pFont));
1575         threshold = this_width > last_width ? (FX_FLOAT)this_width
1576                                             : (FX_FLOAT)last_width;
1577         threshold = _NormalizeThreshold(threshold);
1578         threshold = fontsize_h * threshold / 1000;
1579       }
1580       if (threshold && (spacing && spacing >= threshold)) {
1581         charinfo.m_Unicode = TEXT_BLANK_CHAR;
1582         charinfo.m_Flag = FPDFTEXT_CHAR_GENERATED;
1583         charinfo.m_pTextObj = pTextObj;
1584         charinfo.m_Index = m_TextBuf.GetLength();
1585         m_TempTextBuf.AppendChar(TEXT_BLANK_CHAR);
1586         charinfo.m_CharCode = -1;
1587         charinfo.m_Matrix.Copy(formMatrix);
1588         matrix.Transform(item.m_OriginX, item.m_OriginY, charinfo.m_OriginX,
1589                          charinfo.m_OriginY);
1590         charinfo.m_CharBox =
1591             CFX_FloatRect(charinfo.m_OriginX, charinfo.m_OriginY,
1592                           charinfo.m_OriginX, charinfo.m_OriginY);
1593         m_TempCharList.Add(charinfo);
1594       }
1595       if (item.m_CharCode == (FX_DWORD)-1) {
1596         continue;
1597       }
1598     }
1599     spacing = 0;
1600     CFX_WideString wstrItem = pFont->UnicodeFromCharCode(item.m_CharCode);
1601     FX_BOOL bNoUnicode = FALSE;
1602     FX_WCHAR wChar = wstrItem.GetAt(0);
1603     if ((wstrItem.IsEmpty() || wChar == 0) && item.m_CharCode) {
1604       if (wstrItem.IsEmpty()) {
1605         wstrItem += (FX_WCHAR)item.m_CharCode;
1606       } else {
1607         wstrItem.SetAt(0, (FX_WCHAR)item.m_CharCode);
1608       }
1609       bNoUnicode = TRUE;
1610     }
1611     charinfo.m_Index = -1;
1612     charinfo.m_CharCode = item.m_CharCode;
1613     if (bNoUnicode) {
1614       charinfo.m_Flag = FPDFTEXT_CHAR_UNUNICODE;
1615     } else {
1616       charinfo.m_Flag = FPDFTEXT_CHAR_NORMAL;
1617     }
1618     charinfo.m_pTextObj = pTextObj;
1619     charinfo.m_OriginX = 0, charinfo.m_OriginY = 0;
1620     matrix.Transform(item.m_OriginX, item.m_OriginY, charinfo.m_OriginX,
1621                      charinfo.m_OriginY);
1622     FX_RECT rect(0, 0, 0, 0);
1623     rect.Intersect(0, 0, 0, 0);
1624     charinfo.m_pTextObj->GetFont()->GetCharBBox(charinfo.m_CharCode, rect);
1625     charinfo.m_CharBox.top =
1626         rect.top * pTextObj->GetFontSize() / 1000 + item.m_OriginY;
1627     charinfo.m_CharBox.left =
1628         rect.left * pTextObj->GetFontSize() / 1000 + item.m_OriginX;
1629     charinfo.m_CharBox.right =
1630         rect.right * pTextObj->GetFontSize() / 1000 + item.m_OriginX;
1631     charinfo.m_CharBox.bottom =
1632         rect.bottom * pTextObj->GetFontSize() / 1000 + item.m_OriginY;
1633     if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) < 0.01f) {
1634       charinfo.m_CharBox.top =
1635           charinfo.m_CharBox.bottom + pTextObj->GetFontSize();
1636     }
1637     if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) < 0.01f) {
1638       charinfo.m_CharBox.right =
1639           charinfo.m_CharBox.left + pTextObj->GetCharWidth(charinfo.m_CharCode);
1640     }
1641     matrix.TransformRect(charinfo.m_CharBox);
1642     charinfo.m_Matrix.Copy(matrix);
1643     if (wstrItem.IsEmpty()) {
1644       charinfo.m_Unicode = 0;
1645       m_TempCharList.Add(charinfo);
1646       m_TempTextBuf.AppendChar(0xfffe);
1647       continue;
1648     } else {
1649       int nTotal = wstrItem.GetLength();
1650       FX_BOOL bDel = FALSE;
1651       const int count = std::min(m_TempCharList.GetSize(), 7);
1652       FX_FLOAT threshold = charinfo.m_Matrix.TransformXDistance(
1653           (FX_FLOAT)TEXT_CHARRATIO_GAPDELTA * pTextObj->GetFontSize());
1654       for (int n = m_TempCharList.GetSize();
1655            n > m_TempCharList.GetSize() - count; n--) {
1656         PAGECHAR_INFO* charinfo1 = (PAGECHAR_INFO*)m_TempCharList.GetAt(n - 1);
1657         if (charinfo1->m_CharCode == charinfo.m_CharCode &&
1658             charinfo1->m_pTextObj->GetFont() ==
1659                 charinfo.m_pTextObj->GetFont() &&
1660             FXSYS_fabs(charinfo1->m_OriginX - charinfo.m_OriginX) < threshold &&
1661             FXSYS_fabs(charinfo1->m_OriginY - charinfo.m_OriginY) < threshold) {
1662           bDel = TRUE;
1663           break;
1664         }
1665       }
1666       if (!bDel) {
1667         for (int nIndex = 0; nIndex < nTotal; nIndex++) {
1668           charinfo.m_Unicode = wstrItem.GetAt(nIndex);
1669           if (charinfo.m_Unicode) {
1670             charinfo.m_Index = m_TextBuf.GetLength();
1671             m_TempTextBuf.AppendChar(charinfo.m_Unicode);
1672           } else {
1673             m_TempTextBuf.AppendChar(0xfffe);
1674           }
1675           m_TempCharList.Add(charinfo);
1676         }
1677       } else if (i == 0) {
1678         CFX_WideString str = m_TempTextBuf.GetWideString();
1679         if (!str.IsEmpty() &&
1680             str.GetAt(str.GetLength() - 1) == TEXT_BLANK_CHAR) {
1681           m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
1682           m_TempCharList.Delete(m_TempCharList.GetSize() - 1);
1683         }
1684       }
1685     }
1686   }
1687   if (bIsBidiAndMirrorInverse) {
1688     SwapTempTextBuf(iCharListStartAppend, iBufStartAppend);
1689   }
1690 }
1691 void CPDF_TextPage::SwapTempTextBuf(int32_t iCharListStartAppend,
1692                                     int32_t iBufStartAppend) {
1693   int32_t i, j;
1694   i = iCharListStartAppend;
1695   j = m_TempCharList.GetSize() - 1;
1696   for (; i < j; i++, j--) {
1697     std::swap(m_TempCharList[i], m_TempCharList[j]);
1698     std::swap(m_TempCharList[i].m_Index, m_TempCharList[j].m_Index);
1699   }
1700   FX_WCHAR* pTempBuffer = m_TempTextBuf.GetBuffer();
1701   i = iBufStartAppend;
1702   j = m_TempTextBuf.GetLength() - 1;
1703   for (; i < j; i++, j--) {
1704     std::swap(pTempBuffer[i], pTempBuffer[j]);
1705   }
1706 }
1707 FX_BOOL CPDF_TextPage::IsRightToLeft(const CPDF_TextObject* pTextObj,
1708                                      const CPDF_Font* pFont,
1709                                      int nItems) const {
1710   nonstd::unique_ptr<CFX_BidiChar> pBidiChar(new CFX_BidiChar);
1711   int32_t nR2L = 0;
1712   int32_t nL2R = 0;
1713   int32_t start = 0, count = 0;
1714   CPDF_TextObjectItem item;
1715   for (int32_t i = 0; i < nItems; i++) {
1716     pTextObj->GetItemInfo(i, &item);
1717     if (item.m_CharCode == (FX_DWORD)-1) {
1718       continue;
1719     }
1720     CFX_WideString wstrItem = pFont->UnicodeFromCharCode(item.m_CharCode);
1721     FX_WCHAR wChar = wstrItem.GetAt(0);
1722     if ((wstrItem.IsEmpty() || wChar == 0) && item.m_CharCode) {
1723       wChar = (FX_WCHAR)item.m_CharCode;
1724     }
1725     if (!wChar) {
1726       continue;
1727     }
1728     if (pBidiChar->AppendChar(wChar)) {
1729       CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
1730       if (ret == CFX_BidiChar::RIGHT) {
1731         nR2L++;
1732       } else if (ret == CFX_BidiChar::LEFT) {
1733         nL2R++;
1734       }
1735     }
1736   }
1737   if (pBidiChar->EndChar()) {
1738     CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
1739     if (ret == CFX_BidiChar::RIGHT) {
1740       nR2L++;
1741     } else if (ret == CFX_BidiChar::LEFT) {
1742       nL2R++;
1743     }
1744   }
1745   return (nR2L > 0 && nR2L >= nL2R);
1746 }
1747 int32_t CPDF_TextPage::GetTextObjectWritingMode(
1748     const CPDF_TextObject* pTextObj) {
1749   int32_t nChars = pTextObj->CountChars();
1750   if (nChars == 1) {
1751     return m_TextlineDir;
1752   }
1753   CPDF_TextObjectItem first, last;
1754   pTextObj->GetCharInfo(0, &first);
1755   pTextObj->GetCharInfo(nChars - 1, &last);
1756   CFX_Matrix textMatrix;
1757   pTextObj->GetTextMatrix(&textMatrix);
1758   textMatrix.TransformPoint(first.m_OriginX, first.m_OriginY);
1759   textMatrix.TransformPoint(last.m_OriginX, last.m_OriginY);
1760   FX_FLOAT dX = FXSYS_fabs(last.m_OriginX - first.m_OriginX);
1761   FX_FLOAT dY = FXSYS_fabs(last.m_OriginY - first.m_OriginY);
1762   if (dX <= 0.0001f && dY <= 0.0001f) {
1763     return -1;
1764   }
1765   CFX_VectorF v;
1766   v.Set(dX, dY);
1767   v.Normalize();
1768   if (v.y <= 0.0872f) {
1769     return v.x <= 0.0872f ? m_TextlineDir : 0;
1770   }
1771   if (v.x <= 0.0872f) {
1772     return 1;
1773   }
1774   return m_TextlineDir;
1775 }
1776 FX_BOOL CPDF_TextPage::IsHyphen(FX_WCHAR curChar) {
1777   CFX_WideString strCurText = m_TempTextBuf.GetWideString();
1778   if (strCurText.GetLength() == 0) {
1779     strCurText = m_TextBuf.GetWideString();
1780   }
1781   FX_STRSIZE nCount = strCurText.GetLength();
1782   int nIndex = nCount - 1;
1783   FX_WCHAR wcTmp = strCurText.GetAt(nIndex);
1784   while (wcTmp == 0x20 && nIndex <= nCount - 1 && nIndex >= 0) {
1785     wcTmp = strCurText.GetAt(--nIndex);
1786   }
1787   if (0x2D == wcTmp || 0xAD == wcTmp) {
1788     if (--nIndex > 0) {
1789       FX_WCHAR preChar = strCurText.GetAt((nIndex));
1790       if (((preChar >= L'A' && preChar <= L'Z') ||
1791            (preChar >= L'a' && preChar <= L'z')) &&
1792           ((curChar >= L'A' && curChar <= L'Z') ||
1793            (curChar >= L'a' && curChar <= L'z'))) {
1794         return TRUE;
1795       }
1796     }
1797     int size = m_TempCharList.GetSize();
1798     PAGECHAR_INFO preChar;
1799     if (size) {
1800       preChar = (PAGECHAR_INFO)m_TempCharList[size - 1];
1801     } else {
1802       size = m_charList.GetSize();
1803       if (size == 0) {
1804         return FALSE;
1805       }
1806       preChar = (PAGECHAR_INFO)m_charList[size - 1];
1807     }
1808     if (FPDFTEXT_CHAR_PIECE == preChar.m_Flag)
1809       if (0xAD == preChar.m_Unicode || 0x2D == preChar.m_Unicode) {
1810         return TRUE;
1811       }
1812   }
1813   return FALSE;
1814 }
1815 int CPDF_TextPage::ProcessInsertObject(const CPDF_TextObject* pObj,
1816                                        const CFX_AffineMatrix& formMatrix) {
1817   FindPreviousTextObject();
1818   FX_BOOL bNewline = FALSE;
1819   int WritingMode = GetTextObjectWritingMode(pObj);
1820   if (WritingMode == -1) {
1821     WritingMode = GetTextObjectWritingMode(m_pPreTextObj);
1822   }
1823   CFX_FloatRect this_rect(pObj->m_Left, pObj->m_Bottom, pObj->m_Right,
1824                           pObj->m_Top);
1825   CFX_FloatRect prev_rect(m_pPreTextObj->m_Left, m_pPreTextObj->m_Bottom,
1826                           m_pPreTextObj->m_Right, m_pPreTextObj->m_Top);
1827   CPDF_TextObjectItem PrevItem, item;
1828   int nItem = m_pPreTextObj->CountItems();
1829   m_pPreTextObj->GetItemInfo(nItem - 1, &PrevItem);
1830   pObj->GetItemInfo(0, &item);
1831   CFX_WideString wstrItem =
1832       pObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
1833   if (wstrItem.IsEmpty()) {
1834     wstrItem += (FX_WCHAR)item.m_CharCode;
1835   }
1836   FX_WCHAR curChar = wstrItem.GetAt(0);
1837   if (WritingMode == 0) {
1838     if (this_rect.Height() > 4.5 && prev_rect.Height() > 4.5) {
1839       FX_FLOAT top =
1840           this_rect.top < prev_rect.top ? this_rect.top : prev_rect.top;
1841       FX_FLOAT bottom = this_rect.bottom > prev_rect.bottom ? this_rect.bottom
1842                                                             : prev_rect.bottom;
1843       if (bottom >= top) {
1844         if (IsHyphen(curChar)) {
1845           return 3;
1846         }
1847         return 2;
1848       }
1849     }
1850   } else if (WritingMode == 1) {
1851     if (this_rect.Width() > pObj->GetFontSize() * 0.1f &&
1852         prev_rect.Width() > m_pPreTextObj->GetFontSize() * 0.1f) {
1853       FX_FLOAT left = this_rect.left > m_CurlineRect.left ? this_rect.left
1854                                                           : m_CurlineRect.left;
1855       FX_FLOAT right = this_rect.right < m_CurlineRect.right
1856                            ? this_rect.right
1857                            : m_CurlineRect.right;
1858       if (right <= left) {
1859         if (IsHyphen(curChar)) {
1860           return 3;
1861         }
1862         return 2;
1863       }
1864     }
1865   }
1866   FX_FLOAT last_pos = PrevItem.m_OriginX;
1867   int nLastWidth = GetCharWidth(PrevItem.m_CharCode, m_pPreTextObj->GetFont());
1868   FX_FLOAT last_width = nLastWidth * m_pPreTextObj->GetFontSize() / 1000;
1869   last_width = FXSYS_fabs(last_width);
1870   int nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont());
1871   FX_FLOAT this_width = nThisWidth * pObj->GetFontSize() / 1000;
1872   this_width = FXSYS_fabs(this_width);
1873   FX_FLOAT threshold =
1874       last_width > this_width ? last_width / 4 : this_width / 4;
1875   CFX_AffineMatrix prev_matrix, prev_reverse;
1876   m_pPreTextObj->GetTextMatrix(&prev_matrix);
1877   prev_matrix.Concat(m_perMatrix);
1878   prev_reverse.SetReverse(prev_matrix);
1879   FX_FLOAT x = pObj->GetPosX();
1880   FX_FLOAT y = pObj->GetPosY();
1881   formMatrix.Transform(x, y);
1882   prev_reverse.Transform(x, y);
1883   if (last_width < this_width) {
1884     threshold = prev_reverse.TransformDistance(threshold);
1885   }
1886   CFX_FloatRect rect1(m_pPreTextObj->m_Left, pObj->m_Bottom,
1887                       m_pPreTextObj->m_Right, pObj->m_Top);
1888   CFX_FloatRect rect2(m_pPreTextObj->m_Left, m_pPreTextObj->m_Bottom,
1889                       m_pPreTextObj->m_Right, m_pPreTextObj->m_Top);
1890   CFX_FloatRect rect3 = rect1;
1891   rect1.Intersect(rect2);
1892   if (WritingMode == 0) {
1893     if ((rect1.IsEmpty() && rect2.Height() > 5 && rect3.Height() > 5) ||
1894         ((y > threshold * 2 || y < threshold * -3) &&
1895          (FXSYS_fabs(y) < 1 ? FXSYS_fabs(x) < FXSYS_fabs(y) : TRUE))) {
1896       bNewline = TRUE;
1897       if (nItem > 1) {
1898         CPDF_TextObjectItem tempItem;
1899         m_pPreTextObj->GetItemInfo(0, &tempItem);
1900         CFX_AffineMatrix m;
1901         m_pPreTextObj->GetTextMatrix(&m);
1902         if (PrevItem.m_OriginX > tempItem.m_OriginX &&
1903             m_DisplayMatrix.a > 0.9 && m_DisplayMatrix.b < 0.1 &&
1904             m_DisplayMatrix.c < 0.1 && m_DisplayMatrix.d < -0.9 && m.b < 0.1 &&
1905             m.c < 0.1) {
1906           CFX_FloatRect re(0, m_pPreTextObj->m_Bottom, 1000,
1907                            m_pPreTextObj->m_Top);
1908           if (re.Contains(pObj->GetPosX(), pObj->GetPosY())) {
1909             bNewline = FALSE;
1910           } else {
1911             CFX_FloatRect re(0, pObj->m_Bottom, 1000, pObj->m_Top);
1912             if (re.Contains(m_pPreTextObj->GetPosX(),
1913                             m_pPreTextObj->GetPosY())) {
1914               bNewline = FALSE;
1915             }
1916           }
1917         }
1918       }
1919     }
1920   }
1921   if (bNewline) {
1922     if (IsHyphen(curChar)) {
1923       return 3;
1924     }
1925     return 2;
1926   }
1927   int32_t nChars = pObj->CountChars();
1928   if (nChars == 1 && (0x2D == curChar || 0xAD == curChar))
1929     if (IsHyphen(curChar)) {
1930       return 3;
1931     }
1932   CFX_WideString PrevStr =
1933       m_pPreTextObj->GetFont()->UnicodeFromCharCode(PrevItem.m_CharCode);
1934   FX_WCHAR preChar = PrevStr.GetAt(PrevStr.GetLength() - 1);
1935   CFX_AffineMatrix matrix;
1936   pObj->GetTextMatrix(&matrix);
1937   matrix.Concat(formMatrix);
1938   threshold = (FX_FLOAT)(nLastWidth > nThisWidth ? nLastWidth : nThisWidth);
1939   threshold = threshold > 400
1940                   ? (threshold < 700
1941                          ? threshold / 4
1942                          : (threshold > 800 ? threshold / 6 : threshold / 5))
1943                   : (threshold / 2);
1944   if (nLastWidth >= nThisWidth) {
1945     threshold *= FXSYS_fabs(m_pPreTextObj->GetFontSize());
1946   } else {
1947     threshold *= FXSYS_fabs(pObj->GetFontSize());
1948     threshold = matrix.TransformDistance(threshold);
1949     threshold = prev_reverse.TransformDistance(threshold);
1950   }
1951   threshold /= 1000;
1952   if ((threshold < 1.4881 && threshold > 1.4879) ||
1953       (threshold < 1.39001 && threshold > 1.38999)) {
1954     threshold *= 1.5;
1955   }
1956   if (FXSYS_fabs(last_pos + last_width - x) > threshold && curChar != L' ' &&
1957       preChar != L' ')
1958     if (curChar != L' ' && preChar != L' ') {
1959       if ((x - last_pos - last_width) > threshold ||
1960           (last_pos - x - last_width) > threshold) {
1961         return 1;
1962       }
1963       if (x < 0 && (last_pos - x - last_width) > threshold) {
1964         return 1;
1965       }
1966       if ((x - last_pos - last_width) > this_width ||
1967           (x - last_pos - this_width) > last_width) {
1968         return 1;
1969       }
1970     }
1971   return 0;
1972 }
1973 FX_BOOL CPDF_TextPage::IsSameTextObject(CPDF_TextObject* pTextObj1,
1974                                         CPDF_TextObject* pTextObj2) {
1975   if (!pTextObj1 || !pTextObj2) {
1976     return FALSE;
1977   }
1978   CFX_FloatRect rcPreObj(pTextObj2->m_Left, pTextObj2->m_Bottom,
1979                          pTextObj2->m_Right, pTextObj2->m_Top);
1980   CFX_FloatRect rcCurObj(pTextObj1->m_Left, pTextObj1->m_Bottom,
1981                          pTextObj1->m_Right, pTextObj1->m_Top);
1982   if (rcPreObj.IsEmpty() && rcCurObj.IsEmpty() &&
1983       !m_ParseOptions.m_bGetCharCodeOnly) {
1984     FX_FLOAT dbXdif = FXSYS_fabs(rcPreObj.left - rcCurObj.left);
1985     int nCount = m_charList.GetSize();
1986     if (nCount >= 2) {
1987       PAGECHAR_INFO perCharTemp = (PAGECHAR_INFO)m_charList[nCount - 2];
1988       FX_FLOAT dbSpace = perCharTemp.m_CharBox.Width();
1989       if (dbXdif > dbSpace) {
1990         return FALSE;
1991       }
1992     }
1993   }
1994   if (!rcPreObj.IsEmpty() || !rcCurObj.IsEmpty()) {
1995     rcPreObj.Intersect(rcCurObj);
1996     if (rcPreObj.IsEmpty()) {
1997       return FALSE;
1998     }
1999     if (FXSYS_fabs(rcPreObj.Width() - rcCurObj.Width()) >
2000         rcCurObj.Width() / 2) {
2001       return FALSE;
2002     }
2003     if (pTextObj2->GetFontSize() != pTextObj1->GetFontSize()) {
2004       return FALSE;
2005     }
2006   }
2007   int nPreCount = pTextObj2->CountItems();
2008   int nCurCount = pTextObj1->CountItems();
2009   if (nPreCount != nCurCount) {
2010     return FALSE;
2011   }
2012   CPDF_TextObjectItem itemPer, itemCur;
2013   for (int i = 0; i < nPreCount; i++) {
2014     pTextObj2->GetItemInfo(i, &itemPer);
2015     pTextObj1->GetItemInfo(i, &itemCur);
2016     if (itemCur.m_CharCode != itemPer.m_CharCode) {
2017       return FALSE;
2018     }
2019   }
2020   if (FXSYS_fabs(pTextObj1->GetPosX() - pTextObj2->GetPosX()) >
2021           GetCharWidth(itemPer.m_CharCode, pTextObj2->GetFont()) *
2022               pTextObj2->GetFontSize() / 1000 * 0.9 ||
2023       FXSYS_fabs(pTextObj1->GetPosY() - pTextObj2->GetPosY()) >
2024           FX_MAX(FX_MAX(rcPreObj.Height(), rcPreObj.Width()),
2025                  pTextObj2->GetFontSize()) /
2026               8) {
2027     return FALSE;
2028   }
2029   return TRUE;
2030 }
2031 FX_BOOL CPDF_TextPage::IsSameAsPreTextObject(CPDF_TextObject* pTextObj,
2032                                              FX_POSITION ObjPos) {
2033   if (!pTextObj) {
2034     return FALSE;
2035   }
2036   int i = 0;
2037   if (!ObjPos) {
2038     ObjPos = m_pPage->GetLastObjectPosition();
2039   }
2040   CPDF_PageObject* pObj = m_pPage->GetPrevObject(ObjPos);
2041   while (i < 5 && ObjPos) {
2042     pObj = m_pPage->GetPrevObject(ObjPos);
2043     if (pObj == pTextObj) {
2044       continue;
2045     }
2046     if (pObj->m_Type != PDFPAGE_TEXT) {
2047       continue;
2048     }
2049     if (IsSameTextObject((CPDF_TextObject*)pObj, pTextObj)) {
2050       return TRUE;
2051     }
2052     i++;
2053   }
2054   return FALSE;
2055 }
2056 FX_BOOL CPDF_TextPage::GenerateCharInfo(FX_WCHAR unicode, PAGECHAR_INFO& info) {
2057   int size = m_TempCharList.GetSize();
2058   PAGECHAR_INFO preChar;
2059   if (size) {
2060     preChar = (PAGECHAR_INFO)m_TempCharList[size - 1];
2061   } else {
2062     size = m_charList.GetSize();
2063     if (size == 0) {
2064       return FALSE;
2065     }
2066     preChar = (PAGECHAR_INFO)m_charList[size - 1];
2067   }
2068   info.m_Index = m_TextBuf.GetLength();
2069   info.m_Unicode = unicode;
2070   info.m_pTextObj = NULL;
2071   info.m_CharCode = -1;
2072   info.m_Flag = FPDFTEXT_CHAR_GENERATED;
2073   int preWidth = 0;
2074   if (preChar.m_pTextObj && preChar.m_CharCode != (FX_DWORD)-1) {
2075     preWidth = GetCharWidth(preChar.m_CharCode, preChar.m_pTextObj->GetFont());
2076   }
2077   FX_FLOAT fs = 0;
2078   if (preChar.m_pTextObj) {
2079     fs = preChar.m_pTextObj->GetFontSize();
2080   } else {
2081     fs = preChar.m_CharBox.Height();
2082   }
2083   if (!fs) {
2084     fs = 1;
2085   }
2086   info.m_OriginX = preChar.m_OriginX + preWidth * (fs) / 1000;
2087   info.m_OriginY = preChar.m_OriginY;
2088   info.m_CharBox = CFX_FloatRect(info.m_OriginX, info.m_OriginY, info.m_OriginX,
2089                                  info.m_OriginY);
2090   return TRUE;
2091 }
2092 FX_BOOL CPDF_TextPage::IsRectIntersect(const CFX_FloatRect& rect1,
2093                                        const CFX_FloatRect& rect2) {
2094   CFX_FloatRect rect = rect1;
2095   rect.Intersect(rect2);
2096   return !rect.IsEmpty();
2097 }
2098 FX_BOOL CPDF_TextPage::IsLetter(FX_WCHAR unicode) {
2099   if (unicode < L'A') {
2100     return FALSE;
2101   }
2102   if (unicode > L'Z' && unicode < L'a') {
2103     return FALSE;
2104   }
2105   if (unicode > L'z') {
2106     return FALSE;
2107   }
2108   return TRUE;
2109 }
2110 CPDF_TextPageFind::CPDF_TextPageFind(const IPDF_TextPage* pTextPage)
2111     : m_pTextPage(pTextPage),
2112       m_flags(0),
2113       m_findNextStart(-1),
2114       m_findPreStart(-1),
2115       m_bMatchCase(FALSE),
2116       m_bMatchWholeWord(FALSE),
2117       m_resStart(0),
2118       m_resEnd(-1),
2119       m_IsFind(FALSE) {
2120   m_strText = m_pTextPage->GetPageText();
2121   int nCount = pTextPage->CountChars();
2122   if (nCount) {
2123     m_CharIndex.Add(0);
2124   }
2125   for (int i = 0; i < nCount; i++) {
2126     FPDF_CHAR_INFO info;
2127     pTextPage->GetCharInfo(i, info);
2128     int indexSize = m_CharIndex.GetSize();
2129     if (info.m_Flag == CHAR_NORMAL || info.m_Flag == CHAR_GENERATED) {
2130       if (indexSize % 2) {
2131         m_CharIndex.Add(1);
2132       } else {
2133         if (indexSize <= 0) {
2134           continue;
2135         }
2136         m_CharIndex.SetAt(indexSize - 1, m_CharIndex.GetAt(indexSize - 1) + 1);
2137       }
2138     } else {
2139       if (indexSize % 2) {
2140         if (indexSize <= 0) {
2141           continue;
2142         }
2143         m_CharIndex.SetAt(indexSize - 1, i + 1);
2144       } else {
2145         m_CharIndex.Add(i + 1);
2146       }
2147     }
2148   }
2149   int indexSize = m_CharIndex.GetSize();
2150   if (indexSize % 2) {
2151     m_CharIndex.RemoveAt(indexSize - 1);
2152   }
2153 }
2154 int CPDF_TextPageFind::GetCharIndex(int index) const {
2155   return m_pTextPage->CharIndexFromTextIndex(index);
2156   int indexSize = m_CharIndex.GetSize();
2157   int count = 0;
2158   for (int i = 0; i < indexSize; i += 2) {
2159     count += m_CharIndex.GetAt(i + 1);
2160     if (count > index) {
2161       return index - count + m_CharIndex.GetAt(i + 1) + m_CharIndex.GetAt(i);
2162     }
2163   }
2164   return -1;
2165 }
2166 FX_BOOL CPDF_TextPageFind::FindFirst(const CFX_WideString& findwhat,
2167                                      int flags,
2168                                      int startPos) {
2169   if (!m_pTextPage) {
2170     return FALSE;
2171   }
2172   if (m_strText.IsEmpty() || m_bMatchCase != (flags & FPDFTEXT_MATCHCASE)) {
2173     m_strText = m_pTextPage->GetPageText();
2174   }
2175   CFX_WideString findwhatStr = findwhat;
2176   m_findWhat = findwhatStr;
2177   m_flags = flags;
2178   m_bMatchCase = flags & FPDFTEXT_MATCHCASE;
2179   if (m_strText.IsEmpty()) {
2180     m_IsFind = FALSE;
2181     return TRUE;
2182   }
2183   FX_STRSIZE len = findwhatStr.GetLength();
2184   if (!m_bMatchCase) {
2185     findwhatStr.MakeLower();
2186     m_strText.MakeLower();
2187   }
2188   m_bMatchWholeWord = flags & FPDFTEXT_MATCHWHOLEWORD;
2189   m_findNextStart = startPos;
2190   if (startPos == -1) {
2191     m_findPreStart = m_strText.GetLength() - 1;
2192   } else {
2193     m_findPreStart = startPos;
2194   }
2195   m_csFindWhatArray.RemoveAll();
2196   int i = 0;
2197   while (i < len) {
2198     if (findwhatStr.GetAt(i) != ' ') {
2199       break;
2200     }
2201     i++;
2202   }
2203   if (i < len) {
2204     ExtractFindWhat(findwhatStr);
2205   } else {
2206     m_csFindWhatArray.Add(findwhatStr);
2207   }
2208   if (m_csFindWhatArray.GetSize() <= 0) {
2209     return FALSE;
2210   }
2211   m_IsFind = TRUE;
2212   m_resStart = 0;
2213   m_resEnd = -1;
2214   return TRUE;
2215 }
2216 FX_BOOL CPDF_TextPageFind::FindNext() {
2217   if (!m_pTextPage) {
2218     return FALSE;
2219   }
2220   m_resArray.RemoveAll();
2221   if (m_findNextStart == -1) {
2222     return FALSE;
2223   }
2224   if (m_strText.IsEmpty()) {
2225     m_IsFind = FALSE;
2226     return m_IsFind;
2227   }
2228   int strLen = m_strText.GetLength();
2229   if (m_findNextStart > strLen - 1) {
2230     m_IsFind = FALSE;
2231     return m_IsFind;
2232   }
2233   int nCount = m_csFindWhatArray.GetSize();
2234   int nResultPos = 0;
2235   int nStartPos = 0;
2236   nStartPos = m_findNextStart;
2237   FX_BOOL bSpaceStart = FALSE;
2238   for (int iWord = 0; iWord < nCount; iWord++) {
2239     CFX_WideString csWord = m_csFindWhatArray[iWord];
2240     if (csWord.IsEmpty()) {
2241       if (iWord == nCount - 1) {
2242         FX_WCHAR strInsert = m_strText.GetAt(nStartPos);
2243         if (strInsert == TEXT_LINEFEED_CHAR || strInsert == TEXT_BLANK_CHAR ||
2244             strInsert == TEXT_RETURN_CHAR || strInsert == 160) {
2245           nResultPos = nStartPos + 1;
2246           break;
2247         }
2248         iWord = -1;
2249       } else if (iWord == 0) {
2250         bSpaceStart = TRUE;
2251       }
2252       continue;
2253     }
2254     int endIndex;
2255     nResultPos = m_strText.Find(csWord.c_str(), nStartPos);
2256     if (nResultPos == -1) {
2257       m_IsFind = FALSE;
2258       return m_IsFind;
2259     }
2260     endIndex = nResultPos + csWord.GetLength() - 1;
2261     if (iWord == 0) {
2262       m_resStart = nResultPos;
2263     }
2264     FX_BOOL bMatch = TRUE;
2265     if (iWord != 0 && !bSpaceStart) {
2266       int PreResEndPos = nStartPos;
2267       int curChar = csWord.GetAt(0);
2268       CFX_WideString lastWord = m_csFindWhatArray[iWord - 1];
2269       int lastChar = lastWord.GetAt(lastWord.GetLength() - 1);
2270       if (nStartPos == nResultPos &&
2271           !(_IsIgnoreSpaceCharacter(lastChar) ||
2272             _IsIgnoreSpaceCharacter(curChar))) {
2273         bMatch = FALSE;
2274       }
2275       for (int d = PreResEndPos; d < nResultPos; d++) {
2276         FX_WCHAR strInsert = m_strText.GetAt(d);
2277         if (strInsert != TEXT_LINEFEED_CHAR && strInsert != TEXT_BLANK_CHAR &&
2278             strInsert != TEXT_RETURN_CHAR && strInsert != 160) {
2279           bMatch = FALSE;
2280           break;
2281         }
2282       }
2283     } else if (bSpaceStart) {
2284       if (nResultPos > 0) {
2285         FX_WCHAR strInsert = m_strText.GetAt(nResultPos - 1);
2286         if (strInsert != TEXT_LINEFEED_CHAR && strInsert != TEXT_BLANK_CHAR &&
2287             strInsert != TEXT_RETURN_CHAR && strInsert != 160) {
2288           bMatch = FALSE;
2289           m_resStart = nResultPos;
2290         } else {
2291           m_resStart = nResultPos - 1;
2292         }
2293       }
2294     }
2295     if (m_bMatchWholeWord && bMatch) {
2296       bMatch = IsMatchWholeWord(m_strText, nResultPos, endIndex);
2297     }
2298     nStartPos = endIndex + 1;
2299     if (!bMatch) {
2300       iWord = -1;
2301       if (bSpaceStart) {
2302         nStartPos = m_resStart + m_csFindWhatArray[1].GetLength();
2303       } else {
2304         nStartPos = m_resStart + m_csFindWhatArray[0].GetLength();
2305       }
2306     }
2307   }
2308   m_resEnd = nResultPos +
2309              m_csFindWhatArray[m_csFindWhatArray.GetSize() - 1].GetLength() - 1;
2310   m_IsFind = TRUE;
2311   int resStart = GetCharIndex(m_resStart);
2312   int resEnd = GetCharIndex(m_resEnd);
2313   m_pTextPage->GetRectArray(resStart, resEnd - resStart + 1, m_resArray);
2314   if (m_flags & FPDFTEXT_CONSECUTIVE) {
2315     m_findNextStart = m_resStart + 1;
2316     m_findPreStart = m_resEnd - 1;
2317   } else {
2318     m_findNextStart = m_resEnd + 1;
2319     m_findPreStart = m_resStart - 1;
2320   }
2321   return m_IsFind;
2322 }
2323 FX_BOOL CPDF_TextPageFind::FindPrev() {
2324   if (!m_pTextPage) {
2325     return FALSE;
2326   }
2327   m_resArray.RemoveAll();
2328   if (m_strText.IsEmpty() || m_findPreStart < 0) {
2329     m_IsFind = FALSE;
2330     return m_IsFind;
2331   }
2332   CPDF_TextPageFind findEngine(m_pTextPage);
2333   FX_BOOL ret = findEngine.FindFirst(m_findWhat, m_flags);
2334   if (!ret) {
2335     m_IsFind = FALSE;
2336     return m_IsFind;
2337   }
2338   int order = -1, MatchedCount = 0;
2339   while (ret) {
2340     ret = findEngine.FindNext();
2341     if (ret) {
2342       int order1 = findEngine.GetCurOrder();
2343       int MatchedCount1 = findEngine.GetMatchedCount();
2344       if (((order1 + MatchedCount1) - 1) > m_findPreStart) {
2345         break;
2346       }
2347       order = order1;
2348       MatchedCount = MatchedCount1;
2349     }
2350   }
2351   if (order == -1) {
2352     m_IsFind = FALSE;
2353     return m_IsFind;
2354   }
2355   m_resStart = m_pTextPage->TextIndexFromCharIndex(order);
2356   m_resEnd = m_pTextPage->TextIndexFromCharIndex(order + MatchedCount - 1);
2357   m_IsFind = TRUE;
2358   m_pTextPage->GetRectArray(order, MatchedCount, m_resArray);
2359   if (m_flags & FPDFTEXT_CONSECUTIVE) {
2360     m_findNextStart = m_resStart + 1;
2361     m_findPreStart = m_resEnd - 1;
2362   } else {
2363     m_findNextStart = m_resEnd + 1;
2364     m_findPreStart = m_resStart - 1;
2365   }
2366   return m_IsFind;
2367 }
2368 void CPDF_TextPageFind::ExtractFindWhat(const CFX_WideString& findwhat) {
2369   if (findwhat.IsEmpty()) {
2370     return;
2371   }
2372   int index = 0;
2373   while (1) {
2374     CFX_WideString csWord = TEXT_EMPTY;
2375     int ret =
2376         ExtractSubString(csWord, findwhat.c_str(), index, TEXT_BLANK_CHAR);
2377     if (csWord.IsEmpty()) {
2378       if (ret) {
2379         m_csFindWhatArray.Add(CFX_WideString(L""));
2380         index++;
2381         continue;
2382       } else {
2383         break;
2384       }
2385     }
2386     int pos = 0;
2387     while (pos < csWord.GetLength()) {
2388       CFX_WideString curStr = csWord.Mid(pos, 1);
2389       FX_WCHAR curChar = csWord.GetAt(pos);
2390       if (_IsIgnoreSpaceCharacter(curChar)) {
2391         if (pos > 0 && curChar == 0x2019) {
2392           pos++;
2393           continue;
2394         }
2395         if (pos > 0) {
2396           CFX_WideString preStr = csWord.Mid(0, pos);
2397           m_csFindWhatArray.Add(preStr);
2398         }
2399         m_csFindWhatArray.Add(curStr);
2400         if (pos == csWord.GetLength() - 1) {
2401           csWord.Empty();
2402           break;
2403         }
2404         csWord = csWord.Right(csWord.GetLength() - pos - 1);
2405         pos = 0;
2406         continue;
2407       }
2408       pos++;
2409     }
2410     if (!csWord.IsEmpty()) {
2411       m_csFindWhatArray.Add(csWord);
2412     }
2413     index++;
2414   }
2415 }
2416 FX_BOOL CPDF_TextPageFind::IsMatchWholeWord(const CFX_WideString& csPageText,
2417                                             int startPos,
2418                                             int endPos) {
2419   int char_left = 0;
2420   int char_right = 0;
2421   int char_count = endPos - startPos + 1;
2422   if (char_count < 1) {
2423     return FALSE;
2424   }
2425   if (char_count == 1 && csPageText.GetAt(startPos) > 255) {
2426     return TRUE;
2427   }
2428   if (startPos - 1 >= 0) {
2429     char_left = csPageText.GetAt(startPos - 1);
2430   }
2431   if (startPos + char_count < csPageText.GetLength()) {
2432     char_right = csPageText.GetAt(startPos + char_count);
2433   }
2434   if ((char_left > 'A' && char_left < 'a') ||
2435       (char_left > 'a' && char_left < 'z') ||
2436       (char_left > 0xfb00 && char_left < 0xfb06) ||
2437       (char_left >= '0' && char_left <= '9') ||
2438       (char_right > 'A' && char_right < 'a') ||
2439       (char_right > 'a' && char_right < 'z') ||
2440       (char_right > 0xfb00 && char_right < 0xfb06) ||
2441       (char_right >= '0' && char_right <= '9')) {
2442     return FALSE;
2443   }
2444   if (!(('A' > char_left || char_left > 'Z') &&
2445         ('a' > char_left || char_left > 'z') &&
2446         ('A' > char_right || char_right > 'Z') &&
2447         ('a' > char_right || char_right > 'z'))) {
2448     return FALSE;
2449   }
2450   if (char_count > 0) {
2451     if (csPageText.GetAt(startPos) >= L'0' &&
2452         csPageText.GetAt(startPos) <= L'9' && char_left >= L'0' &&
2453         char_left <= L'9') {
2454       return FALSE;
2455     }
2456     if (csPageText.GetAt(endPos) >= L'0' && csPageText.GetAt(endPos) <= L'9' &&
2457         char_right >= L'0' && char_right <= L'9') {
2458       return FALSE;
2459     }
2460   }
2461   return TRUE;
2462 }
2463 FX_BOOL CPDF_TextPageFind::ExtractSubString(CFX_WideString& rString,
2464                                             const FX_WCHAR* lpszFullString,
2465                                             int iSubString,
2466                                             FX_WCHAR chSep) {
2467   if (lpszFullString == NULL) {
2468     return FALSE;
2469   }
2470   while (iSubString--) {
2471     lpszFullString = FXSYS_wcschr(lpszFullString, chSep);
2472     if (lpszFullString == NULL) {
2473       rString.Empty();
2474       return FALSE;
2475     }
2476     lpszFullString++;
2477     while (*lpszFullString == chSep) {
2478       lpszFullString++;
2479     }
2480   }
2481   const FX_WCHAR* lpchEnd = FXSYS_wcschr(lpszFullString, chSep);
2482   int nLen = (lpchEnd == NULL) ? (int)FXSYS_wcslen(lpszFullString)
2483                                : (int)(lpchEnd - lpszFullString);
2484   ASSERT(nLen >= 0);
2485   FXSYS_memcpy(rString.GetBuffer(nLen), lpszFullString,
2486                nLen * sizeof(FX_WCHAR));
2487   rString.ReleaseBuffer();
2488   return TRUE;
2489 }
2490 CFX_WideString CPDF_TextPageFind::MakeReverse(const CFX_WideString& str) {
2491   CFX_WideString str2;
2492   str2.Empty();
2493   int nlen = str.GetLength();
2494   for (int i = nlen - 1; i >= 0; i--) {
2495     str2 += str.GetAt(i);
2496   }
2497   return str2;
2498 }
2499 void CPDF_TextPageFind::GetRectArray(CFX_RectArray& rects) const {
2500   rects.Copy(m_resArray);
2501 }
2502 int CPDF_TextPageFind::GetCurOrder() const {
2503   return GetCharIndex(m_resStart);
2504 }
2505 int CPDF_TextPageFind::GetMatchedCount() const {
2506   int resStart = GetCharIndex(m_resStart);
2507   int resEnd = GetCharIndex(m_resEnd);
2508   return resEnd - resStart + 1;
2509 }
2510
2511 CPDF_LinkExtract::CPDF_LinkExtract()
2512     : m_pTextPage(nullptr), m_bIsParsed(false) {
2513 }
2514
2515 CPDF_LinkExtract::~CPDF_LinkExtract() {
2516   DeleteLinkList();
2517 }
2518
2519 FX_BOOL CPDF_LinkExtract::ExtractLinks(const IPDF_TextPage* pTextPage) {
2520   if (!pTextPage || !pTextPage->IsParsed())
2521     return FALSE;
2522
2523   m_pTextPage = (const CPDF_TextPage*)pTextPage;
2524   m_strPageText = m_pTextPage->GetPageText(0, -1);
2525   DeleteLinkList();
2526   if (m_strPageText.IsEmpty()) {
2527     return FALSE;
2528   }
2529   ParseLink();
2530   m_bIsParsed = true;
2531   return TRUE;
2532 }
2533
2534 void CPDF_LinkExtract::DeleteLinkList() {
2535   while (m_LinkList.GetSize()) {
2536     CPDF_LinkExt* linkinfo = NULL;
2537     linkinfo = m_LinkList.GetAt(0);
2538     m_LinkList.RemoveAt(0);
2539     delete linkinfo;
2540   }
2541   m_LinkList.RemoveAll();
2542 }
2543 int CPDF_LinkExtract::CountLinks() const {
2544   if (!m_bIsParsed) {
2545     return -1;
2546   }
2547   return m_LinkList.GetSize();
2548 }
2549 void CPDF_LinkExtract::ParseLink() {
2550   int start = 0, pos = 0;
2551   int TotalChar = m_pTextPage->CountChars();
2552   while (pos < TotalChar) {
2553     FPDF_CHAR_INFO pageChar;
2554     m_pTextPage->GetCharInfo(pos, pageChar);
2555     if (pageChar.m_Flag == CHAR_GENERATED || pageChar.m_Unicode == 0x20 ||
2556         pos == TotalChar - 1) {
2557       int nCount = pos - start;
2558       if (pos == TotalChar - 1) {
2559         nCount++;
2560       }
2561       CFX_WideString strBeCheck;
2562       strBeCheck = m_pTextPage->GetPageText(start, nCount);
2563       if (strBeCheck.GetLength() > 5) {
2564         while (strBeCheck.GetLength() > 0) {
2565           FX_WCHAR ch = strBeCheck.GetAt(strBeCheck.GetLength() - 1);
2566           if (ch == L')' || ch == L',' || ch == L'>' || ch == L'.') {
2567             strBeCheck = strBeCheck.Mid(0, strBeCheck.GetLength() - 1);
2568             nCount--;
2569           } else {
2570             break;
2571           }
2572         }
2573         if (nCount > 5 &&
2574             (CheckWebLink(strBeCheck) || CheckMailLink(strBeCheck))) {
2575           AppendToLinkList(start, nCount, strBeCheck);
2576         }
2577       }
2578       start = ++pos;
2579     } else {
2580       pos++;
2581     }
2582   }
2583 }
2584 FX_BOOL CPDF_LinkExtract::CheckWebLink(CFX_WideString& strBeCheck) {
2585   CFX_WideString str = strBeCheck;
2586   str.MakeLower();
2587   if (str.Find(L"http://www.") != -1) {
2588     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"http://www."));
2589     return TRUE;
2590   }
2591   if (str.Find(L"http://") != -1) {
2592     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"http://"));
2593     return TRUE;
2594   }
2595   if (str.Find(L"https://www.") != -1) {
2596     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"https://www."));
2597     return TRUE;
2598   }
2599   if (str.Find(L"https://") != -1) {
2600     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"https://"));
2601     return TRUE;
2602   }
2603   if (str.Find(L"www.") != -1) {
2604     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"www."));
2605     strBeCheck = L"http://" + strBeCheck;
2606     return TRUE;
2607   }
2608   return FALSE;
2609 }
2610 FX_BOOL CPDF_LinkExtract::CheckMailLink(CFX_WideString& str) {
2611   str.MakeLower();
2612   int aPos = str.Find(L'@');
2613   if (aPos < 1) {
2614     return FALSE;
2615   }
2616   if (str.GetAt(aPos - 1) == L'.' || str.GetAt(aPos - 1) == L'_') {
2617     return FALSE;
2618   }
2619   int i;
2620   for (i = aPos - 1; i >= 0; i--) {
2621     FX_WCHAR ch = str.GetAt(i);
2622     if (ch == L'_' || ch == L'.' || (ch >= L'a' && ch <= L'z') ||
2623         (ch >= L'0' && ch <= L'9')) {
2624       continue;
2625     } else {
2626       if (i == aPos - 1) {
2627         return FALSE;
2628       }
2629       str = str.Right(str.GetLength() - i - 1);
2630       break;
2631     }
2632   }
2633   aPos = str.Find(L'@');
2634   if (aPos < 1) {
2635     return FALSE;
2636   }
2637   CFX_WideString strtemp = L"";
2638   for (i = 0; i < aPos; i++) {
2639     FX_WCHAR wch = str.GetAt(i);
2640     if (wch >= L'a' && wch <= L'z') {
2641       break;
2642     } else {
2643       strtemp = str.Right(str.GetLength() - i + 1);
2644     }
2645   }
2646   if (strtemp != L"") {
2647     str = strtemp;
2648   }
2649   aPos = str.Find(L'@');
2650   if (aPos < 1) {
2651     return FALSE;
2652   }
2653   str.TrimRight(L'.');
2654   strtemp = str;
2655   int ePos = str.Find(L'.');
2656   if (ePos == -1) {
2657     return FALSE;
2658   }
2659   while (ePos != -1) {
2660     strtemp = strtemp.Right(strtemp.GetLength() - ePos - 1);
2661     ePos = strtemp.Find('.');
2662   }
2663   ePos = strtemp.GetLength();
2664   for (i = 0; i < ePos; i++) {
2665     FX_WCHAR wch = str.GetAt(i);
2666     if ((wch >= L'a' && wch <= L'z') || (wch >= L'0' && wch <= L'9')) {
2667       continue;
2668     } else {
2669       str = str.Left(str.GetLength() - ePos + i + 1);
2670       ePos = ePos - i - 1;
2671       break;
2672     }
2673   }
2674   int nLen = str.GetLength();
2675   for (i = aPos + 1; i < nLen - ePos; i++) {
2676     FX_WCHAR wch = str.GetAt(i);
2677     if (wch == L'-' || wch == L'.' || (wch >= L'a' && wch <= L'z') ||
2678         (wch >= L'0' && wch <= L'9')) {
2679       continue;
2680     } else {
2681       return FALSE;
2682     }
2683   }
2684   if (str.Find(L"mailto:") == -1) {
2685     str = L"mailto:" + str;
2686   }
2687   return TRUE;
2688 }
2689
2690 void CPDF_LinkExtract::AppendToLinkList(int start,
2691                                         int count,
2692                                         const CFX_WideString& strUrl) {
2693   CPDF_LinkExt* linkInfo = new CPDF_LinkExt;
2694   linkInfo->m_strUrl = strUrl;
2695   linkInfo->m_Start = start;
2696   linkInfo->m_Count = count;
2697   m_LinkList.Add(linkInfo);
2698 }
2699
2700 CFX_WideString CPDF_LinkExtract::GetURL(int index) const {
2701   if (!m_bIsParsed || index < 0 || index >= m_LinkList.GetSize()) {
2702     return L"";
2703   }
2704   CPDF_LinkExt* link = NULL;
2705   link = m_LinkList.GetAt(index);
2706   if (!link) {
2707     return L"";
2708   }
2709   return link->m_strUrl;
2710 }
2711 void CPDF_LinkExtract::GetBoundedSegment(int index,
2712                                          int& start,
2713                                          int& count) const {
2714   if (!m_bIsParsed || index < 0 || index >= m_LinkList.GetSize()) {
2715     return;
2716   }
2717   CPDF_LinkExt* link = NULL;
2718   link = m_LinkList.GetAt(index);
2719   if (!link) {
2720     return;
2721   }
2722   start = link->m_Start;
2723   count = link->m_Count;
2724 }
2725 void CPDF_LinkExtract::GetRects(int index, CFX_RectArray& rects) const {
2726   if (!m_bIsParsed || index < 0 || index >= m_LinkList.GetSize()) {
2727     return;
2728   }
2729   CPDF_LinkExt* link = NULL;
2730   link = m_LinkList.GetAt(index);
2731   if (!link) {
2732     return;
2733   }
2734   m_pTextPage->GetRectArray(link->m_Start, link->m_Count, rects);
2735 }