Revert "Revert "Add type cast definitions for CPDF_Dictionary.""
[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         (CPDF_String*)(pDict ? pDict->GetElement(FX_BSTRC("ActualText"))
1292                              : NULL);
1293     if (temp) {
1294       bExist = TRUE;
1295       actText = temp->GetUnicodeText();
1296     }
1297   }
1298   if (!bExist) {
1299     return FPDFTEXT_MC_PASS;
1300   }
1301   if (m_pPreTextObj) {
1302     if (CPDF_ContentMarkData* pPreMarkData =
1303             (CPDF_ContentMarkData*)m_pPreTextObj->m_ContentMark.GetObject()) {
1304       if (pPreMarkData->CountItems() == n) {
1305         CPDF_ContentMarkItem& item = pPreMarkData->GetItem(n - 1);
1306         if (pDict == item.GetParam()) {
1307           return FPDFTEXT_MC_DONE;
1308         }
1309       }
1310     }
1311   }
1312   CPDF_Font* pFont = pTextObj->GetFont();
1313   FX_STRSIZE nItems = actText.GetLength();
1314   if (nItems < 1) {
1315     return FPDFTEXT_MC_PASS;
1316   }
1317   bExist = FALSE;
1318   for (FX_STRSIZE i = 0; i < nItems; i++) {
1319     FX_WCHAR wChar = actText.GetAt(i);
1320     if (-1 == pFont->CharCodeFromUnicode(wChar)) {
1321       continue;
1322     } else {
1323       bExist = TRUE;
1324       break;
1325     }
1326   }
1327   if (!bExist) {
1328     return FPDFTEXT_MC_PASS;
1329   }
1330   bExist = FALSE;
1331   for (FX_STRSIZE i = 0; i < nItems; i++) {
1332     FX_WCHAR wChar = actText.GetAt(i);
1333     if ((wChar > 0x80 && wChar < 0xFFFD) || (wChar <= 0x80 && isprint(wChar))) {
1334       bExist = TRUE;
1335       break;
1336     }
1337   }
1338   if (!bExist) {
1339     return FPDFTEXT_MC_DONE;
1340   }
1341   return FPDFTEXT_MC_DELAY;
1342 }
1343 void CPDF_TextPage::ProcessMarkedContent(PDFTEXT_Obj Obj) {
1344   CPDF_TextObject* pTextObj = Obj.m_pTextObj;
1345   CPDF_ContentMarkData* pMarkData =
1346       (CPDF_ContentMarkData*)pTextObj->m_ContentMark.GetObject();
1347   if (!pMarkData) {
1348     return;
1349   }
1350   int nContentMark = pMarkData->CountItems();
1351   if (nContentMark < 1) {
1352     return;
1353   }
1354   CFX_WideString actText;
1355   CPDF_Dictionary* pDict = NULL;
1356   int n = 0;
1357   for (n = 0; n < nContentMark; n++) {
1358     CPDF_ContentMarkItem& item = pMarkData->GetItem(n);
1359     CFX_ByteString tagStr = (CFX_ByteString)item.GetName();
1360     pDict = ToDictionary(static_cast<CPDF_Object*>(item.GetParam()));
1361     CPDF_String* temp =
1362         (CPDF_String*)(pDict ? pDict->GetElement(FX_BSTRC("ActualText"))
1363                              : NULL);
1364     if (temp) {
1365       actText = temp->GetUnicodeText();
1366     }
1367   }
1368   FX_STRSIZE nItems = actText.GetLength();
1369   if (nItems < 1) {
1370     return;
1371   }
1372   CPDF_Font* pFont = pTextObj->GetFont();
1373   CFX_AffineMatrix formMatrix = Obj.m_formMatrix;
1374   CFX_AffineMatrix matrix;
1375   pTextObj->GetTextMatrix(&matrix);
1376   matrix.Concat(formMatrix);
1377   FX_FLOAT fPosX = pTextObj->GetPosX();
1378   FX_FLOAT fPosY = pTextObj->GetPosY();
1379   int nCharInfoIndex = m_TextBuf.GetLength();
1380   CFX_FloatRect charBox;
1381   charBox.top = pTextObj->m_Top;
1382   charBox.left = pTextObj->m_Left;
1383   charBox.right = pTextObj->m_Right;
1384   charBox.bottom = pTextObj->m_Bottom;
1385   for (FX_STRSIZE k = 0; k < nItems; k++) {
1386     FX_WCHAR wChar = actText.GetAt(k);
1387     if (wChar <= 0x80 && !isprint(wChar)) {
1388       wChar = 0x20;
1389     }
1390     if (wChar >= 0xFFFD) {
1391       continue;
1392     }
1393     PAGECHAR_INFO charinfo;
1394     charinfo.m_OriginX = fPosX;
1395     charinfo.m_OriginY = fPosY;
1396     charinfo.m_Index = nCharInfoIndex;
1397     charinfo.m_Unicode = wChar;
1398     charinfo.m_CharCode = pFont->CharCodeFromUnicode(wChar);
1399     charinfo.m_Flag = FPDFTEXT_CHAR_PIECE;
1400     charinfo.m_pTextObj = pTextObj;
1401     charinfo.m_CharBox.top = charBox.top;
1402     charinfo.m_CharBox.left = charBox.left;
1403     charinfo.m_CharBox.right = charBox.right;
1404     charinfo.m_CharBox.bottom = charBox.bottom;
1405     charinfo.m_Matrix.Copy(matrix);
1406     m_TempTextBuf.AppendChar(wChar);
1407     m_TempCharList.Add(charinfo);
1408   }
1409 }
1410 void CPDF_TextPage::FindPreviousTextObject(void) {
1411   if (m_TempCharList.GetSize() < 1 && m_charList.GetSize() < 1) {
1412     return;
1413   }
1414   PAGECHAR_INFO preChar;
1415   if (m_TempCharList.GetSize() >= 1) {
1416     preChar =
1417         *(PAGECHAR_INFO*)m_TempCharList.GetAt(m_TempCharList.GetSize() - 1);
1418   } else {
1419     preChar = *(PAGECHAR_INFO*)m_charList.GetAt(m_charList.GetSize() - 1);
1420   }
1421   if (preChar.m_pTextObj) {
1422     m_pPreTextObj = preChar.m_pTextObj;
1423   }
1424 }
1425 void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) {
1426   CPDF_TextObject* pTextObj = Obj.m_pTextObj;
1427   if (FXSYS_fabs(pTextObj->m_Right - pTextObj->m_Left) < 0.01f) {
1428     return;
1429   }
1430   CFX_AffineMatrix formMatrix = Obj.m_formMatrix;
1431   CPDF_Font* pFont = pTextObj->GetFont();
1432   CFX_AffineMatrix matrix;
1433   pTextObj->GetTextMatrix(&matrix);
1434   matrix.Concat(formMatrix);
1435   int32_t bPreMKC = PreMarkedContent(Obj);
1436   if (FPDFTEXT_MC_DONE == bPreMKC) {
1437     m_pPreTextObj = pTextObj;
1438     m_perMatrix.Copy(formMatrix);
1439     return;
1440   }
1441   int result = 0;
1442   if (m_pPreTextObj) {
1443     result = ProcessInsertObject(pTextObj, formMatrix);
1444     if (2 == result) {
1445       m_CurlineRect =
1446           CFX_FloatRect(Obj.m_pTextObj->m_Left, Obj.m_pTextObj->m_Bottom,
1447                         Obj.m_pTextObj->m_Right, Obj.m_pTextObj->m_Top);
1448     } else {
1449       m_CurlineRect.Union(
1450           CFX_FloatRect(Obj.m_pTextObj->m_Left, Obj.m_pTextObj->m_Bottom,
1451                         Obj.m_pTextObj->m_Right, Obj.m_pTextObj->m_Top));
1452     }
1453     PAGECHAR_INFO generateChar;
1454     if (result == 1) {
1455       if (GenerateCharInfo(TEXT_BLANK_CHAR, generateChar)) {
1456         if (!formMatrix.IsIdentity()) {
1457           generateChar.m_Matrix.Copy(formMatrix);
1458         }
1459         m_TempTextBuf.AppendChar(TEXT_BLANK_CHAR);
1460         m_TempCharList.Add(generateChar);
1461       }
1462     } else if (result == 2) {
1463       CloseTempLine();
1464       if (m_TextBuf.GetSize()) {
1465         if (m_ParseOptions.m_bGetCharCodeOnly) {
1466           m_TextBuf.AppendChar(TEXT_RETURN_CHAR);
1467           m_TextBuf.AppendChar(TEXT_LINEFEED_CHAR);
1468         } else {
1469           if (GenerateCharInfo(TEXT_RETURN_CHAR, generateChar)) {
1470             m_TextBuf.AppendChar(TEXT_RETURN_CHAR);
1471             if (!formMatrix.IsIdentity()) {
1472               generateChar.m_Matrix.Copy(formMatrix);
1473             }
1474             m_charList.Add(generateChar);
1475           }
1476           if (GenerateCharInfo(TEXT_LINEFEED_CHAR, generateChar)) {
1477             m_TextBuf.AppendChar(TEXT_LINEFEED_CHAR);
1478             if (!formMatrix.IsIdentity()) {
1479               generateChar.m_Matrix.Copy(formMatrix);
1480             }
1481             m_charList.Add(generateChar);
1482           }
1483         }
1484       }
1485     } else if (result == 3 && !m_ParseOptions.m_bOutputHyphen) {
1486       int32_t nChars = pTextObj->CountChars();
1487       if (nChars == 1) {
1488         CPDF_TextObjectItem item;
1489         pTextObj->GetCharInfo(0, &item);
1490         CFX_WideString wstrItem =
1491             pTextObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
1492         if (wstrItem.IsEmpty()) {
1493           wstrItem += (FX_WCHAR)item.m_CharCode;
1494         }
1495         FX_WCHAR curChar = wstrItem.GetAt(0);
1496         if (0x2D == curChar || 0xAD == curChar) {
1497           return;
1498         }
1499       }
1500       while (m_TempTextBuf.GetSize() > 0 &&
1501              m_TempTextBuf.GetWideString().GetAt(m_TempTextBuf.GetLength() -
1502                                                  1) == 0x20) {
1503         m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
1504         m_TempCharList.Delete(m_TempCharList.GetSize() - 1);
1505       }
1506       PAGECHAR_INFO* cha =
1507           (PAGECHAR_INFO*)m_TempCharList.GetAt(m_TempCharList.GetSize() - 1);
1508       m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
1509       cha->m_Unicode = 0x2;
1510       cha->m_Flag = FPDFTEXT_CHAR_HYPHEN;
1511       m_TempTextBuf.AppendChar(0xfffe);
1512     }
1513   } else {
1514     m_CurlineRect =
1515         CFX_FloatRect(Obj.m_pTextObj->m_Left, Obj.m_pTextObj->m_Bottom,
1516                       Obj.m_pTextObj->m_Right, Obj.m_pTextObj->m_Top);
1517   }
1518   if (FPDFTEXT_MC_DELAY == bPreMKC) {
1519     ProcessMarkedContent(Obj);
1520     m_pPreTextObj = pTextObj;
1521     m_perMatrix.Copy(formMatrix);
1522     return;
1523   }
1524   m_pPreTextObj = pTextObj;
1525   m_perMatrix.Copy(formMatrix);
1526   int nItems = pTextObj->CountItems();
1527   FX_FLOAT baseSpace = _CalculateBaseSpace(pTextObj, matrix);
1528
1529   const FX_BOOL bR2L = IsRightToLeft(pTextObj, pFont, nItems);
1530   const FX_BOOL bIsBidiAndMirrorInverse =
1531       bR2L && (matrix.a * matrix.d - matrix.b * matrix.c) < 0;
1532   int32_t iBufStartAppend = m_TempTextBuf.GetLength();
1533   int32_t iCharListStartAppend = m_TempCharList.GetSize();
1534
1535   FX_FLOAT spacing = 0;
1536   for (int i = 0; i < nItems; i++) {
1537     CPDF_TextObjectItem item;
1538     PAGECHAR_INFO charinfo;
1539     charinfo.m_OriginX = 0;
1540     charinfo.m_OriginY = 0;
1541     pTextObj->GetItemInfo(i, &item);
1542     if (item.m_CharCode == (FX_DWORD)-1) {
1543       CFX_WideString str = m_TempTextBuf.GetWideString();
1544       if (str.IsEmpty()) {
1545         str = m_TextBuf.GetWideString();
1546       }
1547       if (str.IsEmpty() || str.GetAt(str.GetLength() - 1) == TEXT_BLANK_CHAR) {
1548         continue;
1549       }
1550       FX_FLOAT fontsize_h = pTextObj->m_TextState.GetFontSizeH();
1551       spacing = -fontsize_h * item.m_OriginX / 1000;
1552       continue;
1553     }
1554     FX_FLOAT charSpace = pTextObj->m_TextState.GetObject()->m_CharSpace;
1555     if (charSpace > 0.001) {
1556       spacing += matrix.TransformDistance(charSpace);
1557     } else if (charSpace < -0.001) {
1558       spacing -= matrix.TransformDistance(FXSYS_fabs(charSpace));
1559     }
1560     spacing -= baseSpace;
1561     if (spacing && i > 0) {
1562       int last_width = 0;
1563       FX_FLOAT fontsize_h = pTextObj->m_TextState.GetFontSizeH();
1564       FX_DWORD space_charcode = pFont->CharCodeFromUnicode(' ');
1565       FX_FLOAT threshold = 0;
1566       if (space_charcode != -1) {
1567         threshold = fontsize_h * pFont->GetCharWidthF(space_charcode) / 1000;
1568       }
1569       if (threshold > fontsize_h / 3) {
1570         threshold = 0;
1571       } else {
1572         threshold /= 2;
1573       }
1574       if (threshold == 0) {
1575         threshold = fontsize_h;
1576         int this_width = FXSYS_abs(GetCharWidth(item.m_CharCode, pFont));
1577         threshold = this_width > last_width ? (FX_FLOAT)this_width
1578                                             : (FX_FLOAT)last_width;
1579         threshold = _NormalizeThreshold(threshold);
1580         threshold = fontsize_h * threshold / 1000;
1581       }
1582       if (threshold && (spacing && spacing >= threshold)) {
1583         charinfo.m_Unicode = TEXT_BLANK_CHAR;
1584         charinfo.m_Flag = FPDFTEXT_CHAR_GENERATED;
1585         charinfo.m_pTextObj = pTextObj;
1586         charinfo.m_Index = m_TextBuf.GetLength();
1587         m_TempTextBuf.AppendChar(TEXT_BLANK_CHAR);
1588         charinfo.m_CharCode = -1;
1589         charinfo.m_Matrix.Copy(formMatrix);
1590         matrix.Transform(item.m_OriginX, item.m_OriginY, charinfo.m_OriginX,
1591                          charinfo.m_OriginY);
1592         charinfo.m_CharBox =
1593             CFX_FloatRect(charinfo.m_OriginX, charinfo.m_OriginY,
1594                           charinfo.m_OriginX, charinfo.m_OriginY);
1595         m_TempCharList.Add(charinfo);
1596       }
1597       if (item.m_CharCode == (FX_DWORD)-1) {
1598         continue;
1599       }
1600     }
1601     spacing = 0;
1602     CFX_WideString wstrItem = pFont->UnicodeFromCharCode(item.m_CharCode);
1603     FX_BOOL bNoUnicode = FALSE;
1604     FX_WCHAR wChar = wstrItem.GetAt(0);
1605     if ((wstrItem.IsEmpty() || wChar == 0) && item.m_CharCode) {
1606       if (wstrItem.IsEmpty()) {
1607         wstrItem += (FX_WCHAR)item.m_CharCode;
1608       } else {
1609         wstrItem.SetAt(0, (FX_WCHAR)item.m_CharCode);
1610       }
1611       bNoUnicode = TRUE;
1612     }
1613     charinfo.m_Index = -1;
1614     charinfo.m_CharCode = item.m_CharCode;
1615     if (bNoUnicode) {
1616       charinfo.m_Flag = FPDFTEXT_CHAR_UNUNICODE;
1617     } else {
1618       charinfo.m_Flag = FPDFTEXT_CHAR_NORMAL;
1619     }
1620     charinfo.m_pTextObj = pTextObj;
1621     charinfo.m_OriginX = 0, charinfo.m_OriginY = 0;
1622     matrix.Transform(item.m_OriginX, item.m_OriginY, charinfo.m_OriginX,
1623                      charinfo.m_OriginY);
1624     FX_RECT rect(0, 0, 0, 0);
1625     rect.Intersect(0, 0, 0, 0);
1626     charinfo.m_pTextObj->GetFont()->GetCharBBox(charinfo.m_CharCode, rect);
1627     charinfo.m_CharBox.top =
1628         rect.top * pTextObj->GetFontSize() / 1000 + item.m_OriginY;
1629     charinfo.m_CharBox.left =
1630         rect.left * pTextObj->GetFontSize() / 1000 + item.m_OriginX;
1631     charinfo.m_CharBox.right =
1632         rect.right * pTextObj->GetFontSize() / 1000 + item.m_OriginX;
1633     charinfo.m_CharBox.bottom =
1634         rect.bottom * pTextObj->GetFontSize() / 1000 + item.m_OriginY;
1635     if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) < 0.01f) {
1636       charinfo.m_CharBox.top =
1637           charinfo.m_CharBox.bottom + pTextObj->GetFontSize();
1638     }
1639     if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) < 0.01f) {
1640       charinfo.m_CharBox.right =
1641           charinfo.m_CharBox.left + pTextObj->GetCharWidth(charinfo.m_CharCode);
1642     }
1643     matrix.TransformRect(charinfo.m_CharBox);
1644     charinfo.m_Matrix.Copy(matrix);
1645     if (wstrItem.IsEmpty()) {
1646       charinfo.m_Unicode = 0;
1647       m_TempCharList.Add(charinfo);
1648       m_TempTextBuf.AppendChar(0xfffe);
1649       continue;
1650     } else {
1651       int nTotal = wstrItem.GetLength();
1652       FX_BOOL bDel = FALSE;
1653       const int count = std::min(m_TempCharList.GetSize(), 7);
1654       FX_FLOAT threshold = charinfo.m_Matrix.TransformXDistance(
1655           (FX_FLOAT)TEXT_CHARRATIO_GAPDELTA * pTextObj->GetFontSize());
1656       for (int n = m_TempCharList.GetSize();
1657            n > m_TempCharList.GetSize() - count; n--) {
1658         PAGECHAR_INFO* charinfo1 = (PAGECHAR_INFO*)m_TempCharList.GetAt(n - 1);
1659         if (charinfo1->m_CharCode == charinfo.m_CharCode &&
1660             charinfo1->m_pTextObj->GetFont() ==
1661                 charinfo.m_pTextObj->GetFont() &&
1662             FXSYS_fabs(charinfo1->m_OriginX - charinfo.m_OriginX) < threshold &&
1663             FXSYS_fabs(charinfo1->m_OriginY - charinfo.m_OriginY) < threshold) {
1664           bDel = TRUE;
1665           break;
1666         }
1667       }
1668       if (!bDel) {
1669         for (int nIndex = 0; nIndex < nTotal; nIndex++) {
1670           charinfo.m_Unicode = wstrItem.GetAt(nIndex);
1671           if (charinfo.m_Unicode) {
1672             charinfo.m_Index = m_TextBuf.GetLength();
1673             m_TempTextBuf.AppendChar(charinfo.m_Unicode);
1674           } else {
1675             m_TempTextBuf.AppendChar(0xfffe);
1676           }
1677           m_TempCharList.Add(charinfo);
1678         }
1679       } else if (i == 0) {
1680         CFX_WideString str = m_TempTextBuf.GetWideString();
1681         if (!str.IsEmpty() &&
1682             str.GetAt(str.GetLength() - 1) == TEXT_BLANK_CHAR) {
1683           m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
1684           m_TempCharList.Delete(m_TempCharList.GetSize() - 1);
1685         }
1686       }
1687     }
1688   }
1689   if (bIsBidiAndMirrorInverse) {
1690     SwapTempTextBuf(iCharListStartAppend, iBufStartAppend);
1691   }
1692 }
1693 void CPDF_TextPage::SwapTempTextBuf(int32_t iCharListStartAppend,
1694                                     int32_t iBufStartAppend) {
1695   int32_t i, j;
1696   i = iCharListStartAppend;
1697   j = m_TempCharList.GetSize() - 1;
1698   for (; i < j; i++, j--) {
1699     std::swap(m_TempCharList[i], m_TempCharList[j]);
1700     std::swap(m_TempCharList[i].m_Index, m_TempCharList[j].m_Index);
1701   }
1702   FX_WCHAR* pTempBuffer = m_TempTextBuf.GetBuffer();
1703   i = iBufStartAppend;
1704   j = m_TempTextBuf.GetLength() - 1;
1705   for (; i < j; i++, j--) {
1706     std::swap(pTempBuffer[i], pTempBuffer[j]);
1707   }
1708 }
1709 FX_BOOL CPDF_TextPage::IsRightToLeft(const CPDF_TextObject* pTextObj,
1710                                      const CPDF_Font* pFont,
1711                                      int nItems) const {
1712   nonstd::unique_ptr<CFX_BidiChar> pBidiChar(new CFX_BidiChar);
1713   int32_t nR2L = 0;
1714   int32_t nL2R = 0;
1715   int32_t start = 0, count = 0;
1716   CPDF_TextObjectItem item;
1717   for (int32_t i = 0; i < nItems; i++) {
1718     pTextObj->GetItemInfo(i, &item);
1719     if (item.m_CharCode == (FX_DWORD)-1) {
1720       continue;
1721     }
1722     CFX_WideString wstrItem = pFont->UnicodeFromCharCode(item.m_CharCode);
1723     FX_WCHAR wChar = wstrItem.GetAt(0);
1724     if ((wstrItem.IsEmpty() || wChar == 0) && item.m_CharCode) {
1725       wChar = (FX_WCHAR)item.m_CharCode;
1726     }
1727     if (!wChar) {
1728       continue;
1729     }
1730     if (pBidiChar->AppendChar(wChar)) {
1731       CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
1732       if (ret == CFX_BidiChar::RIGHT) {
1733         nR2L++;
1734       } else if (ret == CFX_BidiChar::LEFT) {
1735         nL2R++;
1736       }
1737     }
1738   }
1739   if (pBidiChar->EndChar()) {
1740     CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
1741     if (ret == CFX_BidiChar::RIGHT) {
1742       nR2L++;
1743     } else if (ret == CFX_BidiChar::LEFT) {
1744       nL2R++;
1745     }
1746   }
1747   return (nR2L > 0 && nR2L >= nL2R);
1748 }
1749 int32_t CPDF_TextPage::GetTextObjectWritingMode(
1750     const CPDF_TextObject* pTextObj) {
1751   int32_t nChars = pTextObj->CountChars();
1752   if (nChars == 1) {
1753     return m_TextlineDir;
1754   }
1755   CPDF_TextObjectItem first, last;
1756   pTextObj->GetCharInfo(0, &first);
1757   pTextObj->GetCharInfo(nChars - 1, &last);
1758   CFX_Matrix textMatrix;
1759   pTextObj->GetTextMatrix(&textMatrix);
1760   textMatrix.TransformPoint(first.m_OriginX, first.m_OriginY);
1761   textMatrix.TransformPoint(last.m_OriginX, last.m_OriginY);
1762   FX_FLOAT dX = FXSYS_fabs(last.m_OriginX - first.m_OriginX);
1763   FX_FLOAT dY = FXSYS_fabs(last.m_OriginY - first.m_OriginY);
1764   if (dX <= 0.0001f && dY <= 0.0001f) {
1765     return -1;
1766   }
1767   CFX_VectorF v;
1768   v.Set(dX, dY);
1769   v.Normalize();
1770   if (v.y <= 0.0872f) {
1771     return v.x <= 0.0872f ? m_TextlineDir : 0;
1772   }
1773   if (v.x <= 0.0872f) {
1774     return 1;
1775   }
1776   return m_TextlineDir;
1777 }
1778 FX_BOOL CPDF_TextPage::IsHyphen(FX_WCHAR curChar) {
1779   CFX_WideString strCurText = m_TempTextBuf.GetWideString();
1780   if (strCurText.GetLength() == 0) {
1781     strCurText = m_TextBuf.GetWideString();
1782   }
1783   FX_STRSIZE nCount = strCurText.GetLength();
1784   int nIndex = nCount - 1;
1785   FX_WCHAR wcTmp = strCurText.GetAt(nIndex);
1786   while (wcTmp == 0x20 && nIndex <= nCount - 1 && nIndex >= 0) {
1787     wcTmp = strCurText.GetAt(--nIndex);
1788   }
1789   if (0x2D == wcTmp || 0xAD == wcTmp) {
1790     if (--nIndex > 0) {
1791       FX_WCHAR preChar = strCurText.GetAt((nIndex));
1792       if (((preChar >= L'A' && preChar <= L'Z') ||
1793            (preChar >= L'a' && preChar <= L'z')) &&
1794           ((curChar >= L'A' && curChar <= L'Z') ||
1795            (curChar >= L'a' && curChar <= L'z'))) {
1796         return TRUE;
1797       }
1798     }
1799     int size = m_TempCharList.GetSize();
1800     PAGECHAR_INFO preChar;
1801     if (size) {
1802       preChar = (PAGECHAR_INFO)m_TempCharList[size - 1];
1803     } else {
1804       size = m_charList.GetSize();
1805       if (size == 0) {
1806         return FALSE;
1807       }
1808       preChar = (PAGECHAR_INFO)m_charList[size - 1];
1809     }
1810     if (FPDFTEXT_CHAR_PIECE == preChar.m_Flag)
1811       if (0xAD == preChar.m_Unicode || 0x2D == preChar.m_Unicode) {
1812         return TRUE;
1813       }
1814   }
1815   return FALSE;
1816 }
1817 int CPDF_TextPage::ProcessInsertObject(const CPDF_TextObject* pObj,
1818                                        const CFX_AffineMatrix& formMatrix) {
1819   FindPreviousTextObject();
1820   FX_BOOL bNewline = FALSE;
1821   int WritingMode = GetTextObjectWritingMode(pObj);
1822   if (WritingMode == -1) {
1823     WritingMode = GetTextObjectWritingMode(m_pPreTextObj);
1824   }
1825   CFX_FloatRect this_rect(pObj->m_Left, pObj->m_Bottom, pObj->m_Right,
1826                           pObj->m_Top);
1827   CFX_FloatRect prev_rect(m_pPreTextObj->m_Left, m_pPreTextObj->m_Bottom,
1828                           m_pPreTextObj->m_Right, m_pPreTextObj->m_Top);
1829   CPDF_TextObjectItem PrevItem, item;
1830   int nItem = m_pPreTextObj->CountItems();
1831   m_pPreTextObj->GetItemInfo(nItem - 1, &PrevItem);
1832   pObj->GetItemInfo(0, &item);
1833   CFX_WideString wstrItem =
1834       pObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
1835   if (wstrItem.IsEmpty()) {
1836     wstrItem += (FX_WCHAR)item.m_CharCode;
1837   }
1838   FX_WCHAR curChar = wstrItem.GetAt(0);
1839   if (WritingMode == 0) {
1840     if (this_rect.Height() > 4.5 && prev_rect.Height() > 4.5) {
1841       FX_FLOAT top =
1842           this_rect.top < prev_rect.top ? this_rect.top : prev_rect.top;
1843       FX_FLOAT bottom = this_rect.bottom > prev_rect.bottom ? this_rect.bottom
1844                                                             : prev_rect.bottom;
1845       if (bottom >= top) {
1846         if (IsHyphen(curChar)) {
1847           return 3;
1848         }
1849         return 2;
1850       }
1851     }
1852   } else if (WritingMode == 1) {
1853     if (this_rect.Width() > pObj->GetFontSize() * 0.1f &&
1854         prev_rect.Width() > m_pPreTextObj->GetFontSize() * 0.1f) {
1855       FX_FLOAT left = this_rect.left > m_CurlineRect.left ? this_rect.left
1856                                                           : m_CurlineRect.left;
1857       FX_FLOAT right = this_rect.right < m_CurlineRect.right
1858                            ? this_rect.right
1859                            : m_CurlineRect.right;
1860       if (right <= left) {
1861         if (IsHyphen(curChar)) {
1862           return 3;
1863         }
1864         return 2;
1865       }
1866     }
1867   }
1868   FX_FLOAT last_pos = PrevItem.m_OriginX;
1869   int nLastWidth = GetCharWidth(PrevItem.m_CharCode, m_pPreTextObj->GetFont());
1870   FX_FLOAT last_width = nLastWidth * m_pPreTextObj->GetFontSize() / 1000;
1871   last_width = FXSYS_fabs(last_width);
1872   int nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont());
1873   FX_FLOAT this_width = nThisWidth * pObj->GetFontSize() / 1000;
1874   this_width = FXSYS_fabs(this_width);
1875   FX_FLOAT threshold =
1876       last_width > this_width ? last_width / 4 : this_width / 4;
1877   CFX_AffineMatrix prev_matrix, prev_reverse;
1878   m_pPreTextObj->GetTextMatrix(&prev_matrix);
1879   prev_matrix.Concat(m_perMatrix);
1880   prev_reverse.SetReverse(prev_matrix);
1881   FX_FLOAT x = pObj->GetPosX();
1882   FX_FLOAT y = pObj->GetPosY();
1883   formMatrix.Transform(x, y);
1884   prev_reverse.Transform(x, y);
1885   if (last_width < this_width) {
1886     threshold = prev_reverse.TransformDistance(threshold);
1887   }
1888   CFX_FloatRect rect1(m_pPreTextObj->m_Left, pObj->m_Bottom,
1889                       m_pPreTextObj->m_Right, pObj->m_Top);
1890   CFX_FloatRect rect2(m_pPreTextObj->m_Left, m_pPreTextObj->m_Bottom,
1891                       m_pPreTextObj->m_Right, m_pPreTextObj->m_Top);
1892   CFX_FloatRect rect3 = rect1;
1893   rect1.Intersect(rect2);
1894   if (WritingMode == 0) {
1895     if ((rect1.IsEmpty() && rect2.Height() > 5 && rect3.Height() > 5) ||
1896         ((y > threshold * 2 || y < threshold * -3) &&
1897          (FXSYS_fabs(y) < 1 ? FXSYS_fabs(x) < FXSYS_fabs(y) : TRUE))) {
1898       bNewline = TRUE;
1899       if (nItem > 1) {
1900         CPDF_TextObjectItem tempItem;
1901         m_pPreTextObj->GetItemInfo(0, &tempItem);
1902         CFX_AffineMatrix m;
1903         m_pPreTextObj->GetTextMatrix(&m);
1904         if (PrevItem.m_OriginX > tempItem.m_OriginX &&
1905             m_DisplayMatrix.a > 0.9 && m_DisplayMatrix.b < 0.1 &&
1906             m_DisplayMatrix.c < 0.1 && m_DisplayMatrix.d < -0.9 && m.b < 0.1 &&
1907             m.c < 0.1) {
1908           CFX_FloatRect re(0, m_pPreTextObj->m_Bottom, 1000,
1909                            m_pPreTextObj->m_Top);
1910           if (re.Contains(pObj->GetPosX(), pObj->GetPosY())) {
1911             bNewline = FALSE;
1912           } else {
1913             CFX_FloatRect re(0, pObj->m_Bottom, 1000, pObj->m_Top);
1914             if (re.Contains(m_pPreTextObj->GetPosX(),
1915                             m_pPreTextObj->GetPosY())) {
1916               bNewline = FALSE;
1917             }
1918           }
1919         }
1920       }
1921     }
1922   }
1923   if (bNewline) {
1924     if (IsHyphen(curChar)) {
1925       return 3;
1926     }
1927     return 2;
1928   }
1929   int32_t nChars = pObj->CountChars();
1930   if (nChars == 1 && (0x2D == curChar || 0xAD == curChar))
1931     if (IsHyphen(curChar)) {
1932       return 3;
1933     }
1934   CFX_WideString PrevStr =
1935       m_pPreTextObj->GetFont()->UnicodeFromCharCode(PrevItem.m_CharCode);
1936   FX_WCHAR preChar = PrevStr.GetAt(PrevStr.GetLength() - 1);
1937   CFX_AffineMatrix matrix;
1938   pObj->GetTextMatrix(&matrix);
1939   matrix.Concat(formMatrix);
1940   threshold = (FX_FLOAT)(nLastWidth > nThisWidth ? nLastWidth : nThisWidth);
1941   threshold = threshold > 400
1942                   ? (threshold < 700
1943                          ? threshold / 4
1944                          : (threshold > 800 ? threshold / 6 : threshold / 5))
1945                   : (threshold / 2);
1946   if (nLastWidth >= nThisWidth) {
1947     threshold *= FXSYS_fabs(m_pPreTextObj->GetFontSize());
1948   } else {
1949     threshold *= FXSYS_fabs(pObj->GetFontSize());
1950     threshold = matrix.TransformDistance(threshold);
1951     threshold = prev_reverse.TransformDistance(threshold);
1952   }
1953   threshold /= 1000;
1954   if ((threshold < 1.4881 && threshold > 1.4879) ||
1955       (threshold < 1.39001 && threshold > 1.38999)) {
1956     threshold *= 1.5;
1957   }
1958   if (FXSYS_fabs(last_pos + last_width - x) > threshold && curChar != L' ' &&
1959       preChar != L' ')
1960     if (curChar != L' ' && preChar != L' ') {
1961       if ((x - last_pos - last_width) > threshold ||
1962           (last_pos - x - last_width) > threshold) {
1963         return 1;
1964       }
1965       if (x < 0 && (last_pos - x - last_width) > threshold) {
1966         return 1;
1967       }
1968       if ((x - last_pos - last_width) > this_width ||
1969           (x - last_pos - this_width) > last_width) {
1970         return 1;
1971       }
1972     }
1973   return 0;
1974 }
1975 FX_BOOL CPDF_TextPage::IsSameTextObject(CPDF_TextObject* pTextObj1,
1976                                         CPDF_TextObject* pTextObj2) {
1977   if (!pTextObj1 || !pTextObj2) {
1978     return FALSE;
1979   }
1980   CFX_FloatRect rcPreObj(pTextObj2->m_Left, pTextObj2->m_Bottom,
1981                          pTextObj2->m_Right, pTextObj2->m_Top);
1982   CFX_FloatRect rcCurObj(pTextObj1->m_Left, pTextObj1->m_Bottom,
1983                          pTextObj1->m_Right, pTextObj1->m_Top);
1984   if (rcPreObj.IsEmpty() && rcCurObj.IsEmpty() &&
1985       !m_ParseOptions.m_bGetCharCodeOnly) {
1986     FX_FLOAT dbXdif = FXSYS_fabs(rcPreObj.left - rcCurObj.left);
1987     int nCount = m_charList.GetSize();
1988     if (nCount >= 2) {
1989       PAGECHAR_INFO perCharTemp = (PAGECHAR_INFO)m_charList[nCount - 2];
1990       FX_FLOAT dbSpace = perCharTemp.m_CharBox.Width();
1991       if (dbXdif > dbSpace) {
1992         return FALSE;
1993       }
1994     }
1995   }
1996   if (!rcPreObj.IsEmpty() || !rcCurObj.IsEmpty()) {
1997     rcPreObj.Intersect(rcCurObj);
1998     if (rcPreObj.IsEmpty()) {
1999       return FALSE;
2000     }
2001     if (FXSYS_fabs(rcPreObj.Width() - rcCurObj.Width()) >
2002         rcCurObj.Width() / 2) {
2003       return FALSE;
2004     }
2005     if (pTextObj2->GetFontSize() != pTextObj1->GetFontSize()) {
2006       return FALSE;
2007     }
2008   }
2009   int nPreCount = pTextObj2->CountItems();
2010   int nCurCount = pTextObj1->CountItems();
2011   if (nPreCount != nCurCount) {
2012     return FALSE;
2013   }
2014   CPDF_TextObjectItem itemPer, itemCur;
2015   for (int i = 0; i < nPreCount; i++) {
2016     pTextObj2->GetItemInfo(i, &itemPer);
2017     pTextObj1->GetItemInfo(i, &itemCur);
2018     if (itemCur.m_CharCode != itemPer.m_CharCode) {
2019       return FALSE;
2020     }
2021   }
2022   if (FXSYS_fabs(pTextObj1->GetPosX() - pTextObj2->GetPosX()) >
2023           GetCharWidth(itemPer.m_CharCode, pTextObj2->GetFont()) *
2024               pTextObj2->GetFontSize() / 1000 * 0.9 ||
2025       FXSYS_fabs(pTextObj1->GetPosY() - pTextObj2->GetPosY()) >
2026           FX_MAX(FX_MAX(rcPreObj.Height(), rcPreObj.Width()),
2027                  pTextObj2->GetFontSize()) /
2028               8) {
2029     return FALSE;
2030   }
2031   return TRUE;
2032 }
2033 FX_BOOL CPDF_TextPage::IsSameAsPreTextObject(CPDF_TextObject* pTextObj,
2034                                              FX_POSITION ObjPos) {
2035   if (!pTextObj) {
2036     return FALSE;
2037   }
2038   int i = 0;
2039   if (!ObjPos) {
2040     ObjPos = m_pPage->GetLastObjectPosition();
2041   }
2042   CPDF_PageObject* pObj = m_pPage->GetPrevObject(ObjPos);
2043   while (i < 5 && ObjPos) {
2044     pObj = m_pPage->GetPrevObject(ObjPos);
2045     if (pObj == pTextObj) {
2046       continue;
2047     }
2048     if (pObj->m_Type != PDFPAGE_TEXT) {
2049       continue;
2050     }
2051     if (IsSameTextObject((CPDF_TextObject*)pObj, pTextObj)) {
2052       return TRUE;
2053     }
2054     i++;
2055   }
2056   return FALSE;
2057 }
2058 FX_BOOL CPDF_TextPage::GenerateCharInfo(FX_WCHAR unicode, PAGECHAR_INFO& info) {
2059   int size = m_TempCharList.GetSize();
2060   PAGECHAR_INFO preChar;
2061   if (size) {
2062     preChar = (PAGECHAR_INFO)m_TempCharList[size - 1];
2063   } else {
2064     size = m_charList.GetSize();
2065     if (size == 0) {
2066       return FALSE;
2067     }
2068     preChar = (PAGECHAR_INFO)m_charList[size - 1];
2069   }
2070   info.m_Index = m_TextBuf.GetLength();
2071   info.m_Unicode = unicode;
2072   info.m_pTextObj = NULL;
2073   info.m_CharCode = -1;
2074   info.m_Flag = FPDFTEXT_CHAR_GENERATED;
2075   int preWidth = 0;
2076   if (preChar.m_pTextObj && preChar.m_CharCode != (FX_DWORD)-1) {
2077     preWidth = GetCharWidth(preChar.m_CharCode, preChar.m_pTextObj->GetFont());
2078   }
2079   FX_FLOAT fs = 0;
2080   if (preChar.m_pTextObj) {
2081     fs = preChar.m_pTextObj->GetFontSize();
2082   } else {
2083     fs = preChar.m_CharBox.Height();
2084   }
2085   if (!fs) {
2086     fs = 1;
2087   }
2088   info.m_OriginX = preChar.m_OriginX + preWidth * (fs) / 1000;
2089   info.m_OriginY = preChar.m_OriginY;
2090   info.m_CharBox = CFX_FloatRect(info.m_OriginX, info.m_OriginY, info.m_OriginX,
2091                                  info.m_OriginY);
2092   return TRUE;
2093 }
2094 FX_BOOL CPDF_TextPage::IsRectIntersect(const CFX_FloatRect& rect1,
2095                                        const CFX_FloatRect& rect2) {
2096   CFX_FloatRect rect = rect1;
2097   rect.Intersect(rect2);
2098   return !rect.IsEmpty();
2099 }
2100 FX_BOOL CPDF_TextPage::IsLetter(FX_WCHAR unicode) {
2101   if (unicode < L'A') {
2102     return FALSE;
2103   }
2104   if (unicode > L'Z' && unicode < L'a') {
2105     return FALSE;
2106   }
2107   if (unicode > L'z') {
2108     return FALSE;
2109   }
2110   return TRUE;
2111 }
2112 CPDF_TextPageFind::CPDF_TextPageFind(const IPDF_TextPage* pTextPage)
2113     : m_pTextPage(pTextPage),
2114       m_flags(0),
2115       m_findNextStart(-1),
2116       m_findPreStart(-1),
2117       m_bMatchCase(FALSE),
2118       m_bMatchWholeWord(FALSE),
2119       m_resStart(0),
2120       m_resEnd(-1),
2121       m_IsFind(FALSE) {
2122   m_strText = m_pTextPage->GetPageText();
2123   int nCount = pTextPage->CountChars();
2124   if (nCount) {
2125     m_CharIndex.Add(0);
2126   }
2127   for (int i = 0; i < nCount; i++) {
2128     FPDF_CHAR_INFO info;
2129     pTextPage->GetCharInfo(i, info);
2130     int indexSize = m_CharIndex.GetSize();
2131     if (info.m_Flag == CHAR_NORMAL || info.m_Flag == CHAR_GENERATED) {
2132       if (indexSize % 2) {
2133         m_CharIndex.Add(1);
2134       } else {
2135         if (indexSize <= 0) {
2136           continue;
2137         }
2138         m_CharIndex.SetAt(indexSize - 1, m_CharIndex.GetAt(indexSize - 1) + 1);
2139       }
2140     } else {
2141       if (indexSize % 2) {
2142         if (indexSize <= 0) {
2143           continue;
2144         }
2145         m_CharIndex.SetAt(indexSize - 1, i + 1);
2146       } else {
2147         m_CharIndex.Add(i + 1);
2148       }
2149     }
2150   }
2151   int indexSize = m_CharIndex.GetSize();
2152   if (indexSize % 2) {
2153     m_CharIndex.RemoveAt(indexSize - 1);
2154   }
2155 }
2156 int CPDF_TextPageFind::GetCharIndex(int index) const {
2157   return m_pTextPage->CharIndexFromTextIndex(index);
2158   int indexSize = m_CharIndex.GetSize();
2159   int count = 0;
2160   for (int i = 0; i < indexSize; i += 2) {
2161     count += m_CharIndex.GetAt(i + 1);
2162     if (count > index) {
2163       return index - count + m_CharIndex.GetAt(i + 1) + m_CharIndex.GetAt(i);
2164     }
2165   }
2166   return -1;
2167 }
2168 FX_BOOL CPDF_TextPageFind::FindFirst(const CFX_WideString& findwhat,
2169                                      int flags,
2170                                      int startPos) {
2171   if (!m_pTextPage) {
2172     return FALSE;
2173   }
2174   if (m_strText.IsEmpty() || m_bMatchCase != (flags & FPDFTEXT_MATCHCASE)) {
2175     m_strText = m_pTextPage->GetPageText();
2176   }
2177   CFX_WideString findwhatStr = findwhat;
2178   m_findWhat = findwhatStr;
2179   m_flags = flags;
2180   m_bMatchCase = flags & FPDFTEXT_MATCHCASE;
2181   if (m_strText.IsEmpty()) {
2182     m_IsFind = FALSE;
2183     return TRUE;
2184   }
2185   FX_STRSIZE len = findwhatStr.GetLength();
2186   if (!m_bMatchCase) {
2187     findwhatStr.MakeLower();
2188     m_strText.MakeLower();
2189   }
2190   m_bMatchWholeWord = flags & FPDFTEXT_MATCHWHOLEWORD;
2191   m_findNextStart = startPos;
2192   if (startPos == -1) {
2193     m_findPreStart = m_strText.GetLength() - 1;
2194   } else {
2195     m_findPreStart = startPos;
2196   }
2197   m_csFindWhatArray.RemoveAll();
2198   int i = 0;
2199   while (i < len) {
2200     if (findwhatStr.GetAt(i) != ' ') {
2201       break;
2202     }
2203     i++;
2204   }
2205   if (i < len) {
2206     ExtractFindWhat(findwhatStr);
2207   } else {
2208     m_csFindWhatArray.Add(findwhatStr);
2209   }
2210   if (m_csFindWhatArray.GetSize() <= 0) {
2211     return FALSE;
2212   }
2213   m_IsFind = TRUE;
2214   m_resStart = 0;
2215   m_resEnd = -1;
2216   return TRUE;
2217 }
2218 FX_BOOL CPDF_TextPageFind::FindNext() {
2219   if (!m_pTextPage) {
2220     return FALSE;
2221   }
2222   m_resArray.RemoveAll();
2223   if (m_findNextStart == -1) {
2224     return FALSE;
2225   }
2226   if (m_strText.IsEmpty()) {
2227     m_IsFind = FALSE;
2228     return m_IsFind;
2229   }
2230   int strLen = m_strText.GetLength();
2231   if (m_findNextStart > strLen - 1) {
2232     m_IsFind = FALSE;
2233     return m_IsFind;
2234   }
2235   int nCount = m_csFindWhatArray.GetSize();
2236   int nResultPos = 0;
2237   int nStartPos = 0;
2238   nStartPos = m_findNextStart;
2239   FX_BOOL bSpaceStart = FALSE;
2240   for (int iWord = 0; iWord < nCount; iWord++) {
2241     CFX_WideString csWord = m_csFindWhatArray[iWord];
2242     if (csWord.IsEmpty()) {
2243       if (iWord == nCount - 1) {
2244         FX_WCHAR strInsert = m_strText.GetAt(nStartPos);
2245         if (strInsert == TEXT_LINEFEED_CHAR || strInsert == TEXT_BLANK_CHAR ||
2246             strInsert == TEXT_RETURN_CHAR || strInsert == 160) {
2247           nResultPos = nStartPos + 1;
2248           break;
2249         }
2250         iWord = -1;
2251       } else if (iWord == 0) {
2252         bSpaceStart = TRUE;
2253       }
2254       continue;
2255     }
2256     int endIndex;
2257     nResultPos = m_strText.Find(csWord.c_str(), nStartPos);
2258     if (nResultPos == -1) {
2259       m_IsFind = FALSE;
2260       return m_IsFind;
2261     }
2262     endIndex = nResultPos + csWord.GetLength() - 1;
2263     if (iWord == 0) {
2264       m_resStart = nResultPos;
2265     }
2266     FX_BOOL bMatch = TRUE;
2267     if (iWord != 0 && !bSpaceStart) {
2268       int PreResEndPos = nStartPos;
2269       int curChar = csWord.GetAt(0);
2270       CFX_WideString lastWord = m_csFindWhatArray[iWord - 1];
2271       int lastChar = lastWord.GetAt(lastWord.GetLength() - 1);
2272       if (nStartPos == nResultPos &&
2273           !(_IsIgnoreSpaceCharacter(lastChar) ||
2274             _IsIgnoreSpaceCharacter(curChar))) {
2275         bMatch = FALSE;
2276       }
2277       for (int d = PreResEndPos; d < nResultPos; d++) {
2278         FX_WCHAR strInsert = m_strText.GetAt(d);
2279         if (strInsert != TEXT_LINEFEED_CHAR && strInsert != TEXT_BLANK_CHAR &&
2280             strInsert != TEXT_RETURN_CHAR && strInsert != 160) {
2281           bMatch = FALSE;
2282           break;
2283         }
2284       }
2285     } else if (bSpaceStart) {
2286       if (nResultPos > 0) {
2287         FX_WCHAR strInsert = m_strText.GetAt(nResultPos - 1);
2288         if (strInsert != TEXT_LINEFEED_CHAR && strInsert != TEXT_BLANK_CHAR &&
2289             strInsert != TEXT_RETURN_CHAR && strInsert != 160) {
2290           bMatch = FALSE;
2291           m_resStart = nResultPos;
2292         } else {
2293           m_resStart = nResultPos - 1;
2294         }
2295       }
2296     }
2297     if (m_bMatchWholeWord && bMatch) {
2298       bMatch = IsMatchWholeWord(m_strText, nResultPos, endIndex);
2299     }
2300     nStartPos = endIndex + 1;
2301     if (!bMatch) {
2302       iWord = -1;
2303       if (bSpaceStart) {
2304         nStartPos = m_resStart + m_csFindWhatArray[1].GetLength();
2305       } else {
2306         nStartPos = m_resStart + m_csFindWhatArray[0].GetLength();
2307       }
2308     }
2309   }
2310   m_resEnd = nResultPos +
2311              m_csFindWhatArray[m_csFindWhatArray.GetSize() - 1].GetLength() - 1;
2312   m_IsFind = TRUE;
2313   int resStart = GetCharIndex(m_resStart);
2314   int resEnd = GetCharIndex(m_resEnd);
2315   m_pTextPage->GetRectArray(resStart, resEnd - resStart + 1, m_resArray);
2316   if (m_flags & FPDFTEXT_CONSECUTIVE) {
2317     m_findNextStart = m_resStart + 1;
2318     m_findPreStart = m_resEnd - 1;
2319   } else {
2320     m_findNextStart = m_resEnd + 1;
2321     m_findPreStart = m_resStart - 1;
2322   }
2323   return m_IsFind;
2324 }
2325 FX_BOOL CPDF_TextPageFind::FindPrev() {
2326   if (!m_pTextPage) {
2327     return FALSE;
2328   }
2329   m_resArray.RemoveAll();
2330   if (m_strText.IsEmpty() || m_findPreStart < 0) {
2331     m_IsFind = FALSE;
2332     return m_IsFind;
2333   }
2334   CPDF_TextPageFind findEngine(m_pTextPage);
2335   FX_BOOL ret = findEngine.FindFirst(m_findWhat, m_flags);
2336   if (!ret) {
2337     m_IsFind = FALSE;
2338     return m_IsFind;
2339   }
2340   int order = -1, MatchedCount = 0;
2341   while (ret) {
2342     ret = findEngine.FindNext();
2343     if (ret) {
2344       int order1 = findEngine.GetCurOrder();
2345       int MatchedCount1 = findEngine.GetMatchedCount();
2346       if (((order1 + MatchedCount1) - 1) > m_findPreStart) {
2347         break;
2348       }
2349       order = order1;
2350       MatchedCount = MatchedCount1;
2351     }
2352   }
2353   if (order == -1) {
2354     m_IsFind = FALSE;
2355     return m_IsFind;
2356   }
2357   m_resStart = m_pTextPage->TextIndexFromCharIndex(order);
2358   m_resEnd = m_pTextPage->TextIndexFromCharIndex(order + MatchedCount - 1);
2359   m_IsFind = TRUE;
2360   m_pTextPage->GetRectArray(order, MatchedCount, m_resArray);
2361   if (m_flags & FPDFTEXT_CONSECUTIVE) {
2362     m_findNextStart = m_resStart + 1;
2363     m_findPreStart = m_resEnd - 1;
2364   } else {
2365     m_findNextStart = m_resEnd + 1;
2366     m_findPreStart = m_resStart - 1;
2367   }
2368   return m_IsFind;
2369 }
2370 void CPDF_TextPageFind::ExtractFindWhat(const CFX_WideString& findwhat) {
2371   if (findwhat.IsEmpty()) {
2372     return;
2373   }
2374   int index = 0;
2375   while (1) {
2376     CFX_WideString csWord = TEXT_EMPTY;
2377     int ret =
2378         ExtractSubString(csWord, findwhat.c_str(), index, TEXT_BLANK_CHAR);
2379     if (csWord.IsEmpty()) {
2380       if (ret) {
2381         m_csFindWhatArray.Add(CFX_WideString(L""));
2382         index++;
2383         continue;
2384       } else {
2385         break;
2386       }
2387     }
2388     int pos = 0;
2389     while (pos < csWord.GetLength()) {
2390       CFX_WideString curStr = csWord.Mid(pos, 1);
2391       FX_WCHAR curChar = csWord.GetAt(pos);
2392       if (_IsIgnoreSpaceCharacter(curChar)) {
2393         if (pos > 0 && curChar == 0x2019) {
2394           pos++;
2395           continue;
2396         }
2397         if (pos > 0) {
2398           CFX_WideString preStr = csWord.Mid(0, pos);
2399           m_csFindWhatArray.Add(preStr);
2400         }
2401         m_csFindWhatArray.Add(curStr);
2402         if (pos == csWord.GetLength() - 1) {
2403           csWord.Empty();
2404           break;
2405         }
2406         csWord = csWord.Right(csWord.GetLength() - pos - 1);
2407         pos = 0;
2408         continue;
2409       }
2410       pos++;
2411     }
2412     if (!csWord.IsEmpty()) {
2413       m_csFindWhatArray.Add(csWord);
2414     }
2415     index++;
2416   }
2417 }
2418 FX_BOOL CPDF_TextPageFind::IsMatchWholeWord(const CFX_WideString& csPageText,
2419                                             int startPos,
2420                                             int endPos) {
2421   int char_left = 0;
2422   int char_right = 0;
2423   int char_count = endPos - startPos + 1;
2424   if (char_count < 1) {
2425     return FALSE;
2426   }
2427   if (char_count == 1 && csPageText.GetAt(startPos) > 255) {
2428     return TRUE;
2429   }
2430   if (startPos - 1 >= 0) {
2431     char_left = csPageText.GetAt(startPos - 1);
2432   }
2433   if (startPos + char_count < csPageText.GetLength()) {
2434     char_right = csPageText.GetAt(startPos + char_count);
2435   }
2436   if ((char_left > 'A' && char_left < 'a') ||
2437       (char_left > 'a' && char_left < 'z') ||
2438       (char_left > 0xfb00 && char_left < 0xfb06) ||
2439       (char_left >= '0' && char_left <= '9') ||
2440       (char_right > 'A' && char_right < 'a') ||
2441       (char_right > 'a' && char_right < 'z') ||
2442       (char_right > 0xfb00 && char_right < 0xfb06) ||
2443       (char_right >= '0' && char_right <= '9')) {
2444     return FALSE;
2445   }
2446   if (!(('A' > char_left || char_left > 'Z') &&
2447         ('a' > char_left || char_left > 'z') &&
2448         ('A' > char_right || char_right > 'Z') &&
2449         ('a' > char_right || char_right > 'z'))) {
2450     return FALSE;
2451   }
2452   if (char_count > 0) {
2453     if (csPageText.GetAt(startPos) >= L'0' &&
2454         csPageText.GetAt(startPos) <= L'9' && char_left >= L'0' &&
2455         char_left <= L'9') {
2456       return FALSE;
2457     }
2458     if (csPageText.GetAt(endPos) >= L'0' && csPageText.GetAt(endPos) <= L'9' &&
2459         char_right >= L'0' && char_right <= L'9') {
2460       return FALSE;
2461     }
2462   }
2463   return TRUE;
2464 }
2465 FX_BOOL CPDF_TextPageFind::ExtractSubString(CFX_WideString& rString,
2466                                             const FX_WCHAR* lpszFullString,
2467                                             int iSubString,
2468                                             FX_WCHAR chSep) {
2469   if (lpszFullString == NULL) {
2470     return FALSE;
2471   }
2472   while (iSubString--) {
2473     lpszFullString = FXSYS_wcschr(lpszFullString, chSep);
2474     if (lpszFullString == NULL) {
2475       rString.Empty();
2476       return FALSE;
2477     }
2478     lpszFullString++;
2479     while (*lpszFullString == chSep) {
2480       lpszFullString++;
2481     }
2482   }
2483   const FX_WCHAR* lpchEnd = FXSYS_wcschr(lpszFullString, chSep);
2484   int nLen = (lpchEnd == NULL) ? (int)FXSYS_wcslen(lpszFullString)
2485                                : (int)(lpchEnd - lpszFullString);
2486   ASSERT(nLen >= 0);
2487   FXSYS_memcpy(rString.GetBuffer(nLen), lpszFullString,
2488                nLen * sizeof(FX_WCHAR));
2489   rString.ReleaseBuffer();
2490   return TRUE;
2491 }
2492 CFX_WideString CPDF_TextPageFind::MakeReverse(const CFX_WideString& str) {
2493   CFX_WideString str2;
2494   str2.Empty();
2495   int nlen = str.GetLength();
2496   for (int i = nlen - 1; i >= 0; i--) {
2497     str2 += str.GetAt(i);
2498   }
2499   return str2;
2500 }
2501 void CPDF_TextPageFind::GetRectArray(CFX_RectArray& rects) const {
2502   rects.Copy(m_resArray);
2503 }
2504 int CPDF_TextPageFind::GetCurOrder() const {
2505   return GetCharIndex(m_resStart);
2506 }
2507 int CPDF_TextPageFind::GetMatchedCount() const {
2508   int resStart = GetCharIndex(m_resStart);
2509   int resEnd = GetCharIndex(m_resEnd);
2510   return resEnd - resStart + 1;
2511 }
2512
2513 CPDF_LinkExtract::CPDF_LinkExtract()
2514     : m_pTextPage(nullptr), m_bIsParsed(false) {
2515 }
2516
2517 CPDF_LinkExtract::~CPDF_LinkExtract() {
2518   DeleteLinkList();
2519 }
2520
2521 FX_BOOL CPDF_LinkExtract::ExtractLinks(const IPDF_TextPage* pTextPage) {
2522   if (!pTextPage || !pTextPage->IsParsed())
2523     return FALSE;
2524
2525   m_pTextPage = (const CPDF_TextPage*)pTextPage;
2526   m_strPageText = m_pTextPage->GetPageText(0, -1);
2527   DeleteLinkList();
2528   if (m_strPageText.IsEmpty()) {
2529     return FALSE;
2530   }
2531   ParseLink();
2532   m_bIsParsed = true;
2533   return TRUE;
2534 }
2535
2536 void CPDF_LinkExtract::DeleteLinkList() {
2537   while (m_LinkList.GetSize()) {
2538     CPDF_LinkExt* linkinfo = NULL;
2539     linkinfo = m_LinkList.GetAt(0);
2540     m_LinkList.RemoveAt(0);
2541     delete linkinfo;
2542   }
2543   m_LinkList.RemoveAll();
2544 }
2545 int CPDF_LinkExtract::CountLinks() const {
2546   if (!m_bIsParsed) {
2547     return -1;
2548   }
2549   return m_LinkList.GetSize();
2550 }
2551 void CPDF_LinkExtract::ParseLink() {
2552   int start = 0, pos = 0;
2553   int TotalChar = m_pTextPage->CountChars();
2554   while (pos < TotalChar) {
2555     FPDF_CHAR_INFO pageChar;
2556     m_pTextPage->GetCharInfo(pos, pageChar);
2557     if (pageChar.m_Flag == CHAR_GENERATED || pageChar.m_Unicode == 0x20 ||
2558         pos == TotalChar - 1) {
2559       int nCount = pos - start;
2560       if (pos == TotalChar - 1) {
2561         nCount++;
2562       }
2563       CFX_WideString strBeCheck;
2564       strBeCheck = m_pTextPage->GetPageText(start, nCount);
2565       if (strBeCheck.GetLength() > 5) {
2566         while (strBeCheck.GetLength() > 0) {
2567           FX_WCHAR ch = strBeCheck.GetAt(strBeCheck.GetLength() - 1);
2568           if (ch == L')' || ch == L',' || ch == L'>' || ch == L'.') {
2569             strBeCheck = strBeCheck.Mid(0, strBeCheck.GetLength() - 1);
2570             nCount--;
2571           } else {
2572             break;
2573           }
2574         }
2575         if (nCount > 5 &&
2576             (CheckWebLink(strBeCheck) || CheckMailLink(strBeCheck))) {
2577           AppendToLinkList(start, nCount, strBeCheck);
2578         }
2579       }
2580       start = ++pos;
2581     } else {
2582       pos++;
2583     }
2584   }
2585 }
2586 FX_BOOL CPDF_LinkExtract::CheckWebLink(CFX_WideString& strBeCheck) {
2587   CFX_WideString str = strBeCheck;
2588   str.MakeLower();
2589   if (str.Find(L"http://www.") != -1) {
2590     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"http://www."));
2591     return TRUE;
2592   }
2593   if (str.Find(L"http://") != -1) {
2594     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"http://"));
2595     return TRUE;
2596   }
2597   if (str.Find(L"https://www.") != -1) {
2598     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"https://www."));
2599     return TRUE;
2600   }
2601   if (str.Find(L"https://") != -1) {
2602     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"https://"));
2603     return TRUE;
2604   }
2605   if (str.Find(L"www.") != -1) {
2606     strBeCheck = strBeCheck.Right(str.GetLength() - str.Find(L"www."));
2607     strBeCheck = L"http://" + strBeCheck;
2608     return TRUE;
2609   }
2610   return FALSE;
2611 }
2612 FX_BOOL CPDF_LinkExtract::CheckMailLink(CFX_WideString& str) {
2613   str.MakeLower();
2614   int aPos = str.Find(L'@');
2615   if (aPos < 1) {
2616     return FALSE;
2617   }
2618   if (str.GetAt(aPos - 1) == L'.' || str.GetAt(aPos - 1) == L'_') {
2619     return FALSE;
2620   }
2621   int i;
2622   for (i = aPos - 1; i >= 0; i--) {
2623     FX_WCHAR ch = str.GetAt(i);
2624     if (ch == L'_' || ch == L'.' || (ch >= L'a' && ch <= L'z') ||
2625         (ch >= L'0' && ch <= L'9')) {
2626       continue;
2627     } else {
2628       if (i == aPos - 1) {
2629         return FALSE;
2630       }
2631       str = str.Right(str.GetLength() - i - 1);
2632       break;
2633     }
2634   }
2635   aPos = str.Find(L'@');
2636   if (aPos < 1) {
2637     return FALSE;
2638   }
2639   CFX_WideString strtemp = L"";
2640   for (i = 0; i < aPos; i++) {
2641     FX_WCHAR wch = str.GetAt(i);
2642     if (wch >= L'a' && wch <= L'z') {
2643       break;
2644     } else {
2645       strtemp = str.Right(str.GetLength() - i + 1);
2646     }
2647   }
2648   if (strtemp != L"") {
2649     str = strtemp;
2650   }
2651   aPos = str.Find(L'@');
2652   if (aPos < 1) {
2653     return FALSE;
2654   }
2655   str.TrimRight(L'.');
2656   strtemp = str;
2657   int ePos = str.Find(L'.');
2658   if (ePos == -1) {
2659     return FALSE;
2660   }
2661   while (ePos != -1) {
2662     strtemp = strtemp.Right(strtemp.GetLength() - ePos - 1);
2663     ePos = strtemp.Find('.');
2664   }
2665   ePos = strtemp.GetLength();
2666   for (i = 0; i < ePos; i++) {
2667     FX_WCHAR wch = str.GetAt(i);
2668     if ((wch >= L'a' && wch <= L'z') || (wch >= L'0' && wch <= L'9')) {
2669       continue;
2670     } else {
2671       str = str.Left(str.GetLength() - ePos + i + 1);
2672       ePos = ePos - i - 1;
2673       break;
2674     }
2675   }
2676   int nLen = str.GetLength();
2677   for (i = aPos + 1; i < nLen - ePos; i++) {
2678     FX_WCHAR wch = str.GetAt(i);
2679     if (wch == L'-' || wch == L'.' || (wch >= L'a' && wch <= L'z') ||
2680         (wch >= L'0' && wch <= L'9')) {
2681       continue;
2682     } else {
2683       return FALSE;
2684     }
2685   }
2686   if (str.Find(L"mailto:") == -1) {
2687     str = L"mailto:" + str;
2688   }
2689   return TRUE;
2690 }
2691
2692 void CPDF_LinkExtract::AppendToLinkList(int start,
2693                                         int count,
2694                                         const CFX_WideString& strUrl) {
2695   CPDF_LinkExt* linkInfo = new CPDF_LinkExt;
2696   linkInfo->m_strUrl = strUrl;
2697   linkInfo->m_Start = start;
2698   linkInfo->m_Count = count;
2699   m_LinkList.Add(linkInfo);
2700 }
2701
2702 CFX_WideString CPDF_LinkExtract::GetURL(int index) const {
2703   if (!m_bIsParsed || index < 0 || index >= m_LinkList.GetSize()) {
2704     return L"";
2705   }
2706   CPDF_LinkExt* link = NULL;
2707   link = m_LinkList.GetAt(index);
2708   if (!link) {
2709     return L"";
2710   }
2711   return link->m_strUrl;
2712 }
2713 void CPDF_LinkExtract::GetBoundedSegment(int index,
2714                                          int& start,
2715                                          int& count) const {
2716   if (!m_bIsParsed || index < 0 || index >= m_LinkList.GetSize()) {
2717     return;
2718   }
2719   CPDF_LinkExt* link = NULL;
2720   link = m_LinkList.GetAt(index);
2721   if (!link) {
2722     return;
2723   }
2724   start = link->m_Start;
2725   count = link->m_Count;
2726 }
2727 void CPDF_LinkExtract::GetRects(int index, CFX_RectArray& rects) const {
2728   if (!m_bIsParsed || index < 0 || index >= m_LinkList.GetSize()) {
2729     return;
2730   }
2731   CPDF_LinkExt* link = NULL;
2732   link = m_LinkList.GetAt(index);
2733   if (!link) {
2734     return;
2735   }
2736   m_pTextPage->GetRectArray(link->m_Start, link->m_Count, rects);
2737 }