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