Add type cast definitions for CPDF_String.
[pdfium.git] / core / src / fpdftext / fpdf_text.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 "../../../third_party/base/nonstd_unique_ptr.h"
8 #include "../../include/fpdfapi/fpdf_page.h"
9 #include "../../include/fpdfapi/fpdf_pageobj.h"
10 #include "../../include/fpdfapi/fpdf_resource.h"
11 #include "../../include/fpdftext/fpdf_text.h"
12 #include "../../include/fxcrt/fx_bidi.h"
13 #include "../../include/fxcrt/fx_ucd.h"
14 #include "text_int.h"
15 #include "txtproc.h"
16
17 CFX_ByteString CharFromUnicodeAlt(FX_WCHAR unicode,
18                                   int destcp,
19                                   const FX_CHAR* defchar) {
20   if (destcp == 0) {
21     if (unicode < 0x80) {
22       return CFX_ByteString((char)unicode);
23     }
24     const FX_CHAR* altstr = FCS_GetAltStr(unicode);
25     return CFX_ByteString(altstr ? altstr : defchar);
26   }
27   char buf[10];
28   int iDef = 0;
29   int ret = FXSYS_WideCharToMultiByte(destcp, 0, (wchar_t*)&unicode, 1, buf, 10,
30                                       NULL, &iDef);
31   if (ret && !iDef) {
32     return CFX_ByteString(buf, ret);
33   }
34   const FX_CHAR* altstr = FCS_GetAltStr(unicode);
35   return CFX_ByteString(altstr ? altstr : defchar);
36 }
37 CTextPage::CTextPage() {}
38 CTextPage::~CTextPage() {
39   int i;
40   for (i = 0; i < m_BaseLines.GetSize(); i++) {
41     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
42     delete pBaseLine;
43   }
44   for (i = 0; i < m_TextColumns.GetSize(); i++) {
45     CTextColumn* pTextColumn = (CTextColumn*)m_TextColumns.GetAt(i);
46     delete pTextColumn;
47   }
48 }
49 void CTextPage::ProcessObject(CPDF_PageObject* pObject) {
50   if (pObject->m_Type != PDFPAGE_TEXT) {
51     return;
52   }
53   CPDF_TextObject* pText = (CPDF_TextObject*)pObject;
54   CPDF_Font* pFont = pText->m_TextState.GetFont();
55   int count = pText->CountItems();
56   FX_FLOAT* pPosArray = FX_Alloc2D(FX_FLOAT, count, 2);
57   pText->CalcCharPos(pPosArray);
58
59   FX_FLOAT fontsize_h = pText->m_TextState.GetFontSizeH();
60   FX_FLOAT fontsize_v = pText->m_TextState.GetFontSizeV();
61   FX_DWORD space_charcode = pFont->CharCodeFromUnicode(' ');
62   FX_FLOAT spacew = 0;
63   if (space_charcode != -1) {
64     spacew = fontsize_h * pFont->GetCharWidthF(space_charcode) / 1000;
65   }
66   if (spacew == 0) {
67     spacew = fontsize_h / 4;
68   }
69   if (pText->m_TextState.GetBaselineAngle() != 0) {
70     int cc = 0;
71     CFX_AffineMatrix matrix;
72     pText->GetTextMatrix(&matrix);
73     for (int i = 0; i < pText->m_nChars; i++) {
74       FX_DWORD charcode = pText->m_nChars == 1
75                               ? (FX_DWORD)(uintptr_t)pText->m_pCharCodes
76                               : pText->m_pCharCodes[i];
77       if (charcode == (FX_DWORD)-1) {
78         continue;
79       }
80       FX_RECT char_box;
81       pFont->GetCharBBox(charcode, char_box);
82       FX_FLOAT char_left =
83           pPosArray ? pPosArray[cc * 2]
84                     : char_box.left * pText->m_TextState.GetFontSize() / 1000;
85       FX_FLOAT char_right =
86           pPosArray ? pPosArray[cc * 2 + 1]
87                     : char_box.right * pText->m_TextState.GetFontSize() / 1000;
88       FX_FLOAT char_top =
89           char_box.top * pText->m_TextState.GetFontSize() / 1000;
90       FX_FLOAT char_bottom =
91           char_box.bottom * pText->m_TextState.GetFontSize() / 1000;
92       cc++;
93       FX_FLOAT char_origx, char_origy;
94       matrix.Transform(char_left, 0, char_origx, char_origy);
95       matrix.TransformRect(char_left, char_right, char_top, char_bottom);
96       CFX_ByteString str;
97       pFont->AppendChar(str, charcode);
98       InsertTextBox(NULL, char_origy, char_left, char_right, char_top,
99                     char_bottom, spacew, fontsize_v, str, pFont);
100     }
101     FX_Free(pPosArray);
102     return;
103   }
104   FX_FLOAT ratio_h = fontsize_h / pText->m_TextState.GetFontSize();
105   for (int ii = 0; ii < count * 2; ii++) {
106     pPosArray[ii] *= ratio_h;
107   }
108   FX_FLOAT baseline = pText->m_PosY;
109   CTextBaseLine* pBaseLine = NULL;
110   FX_FLOAT topy = pText->m_Top;
111   FX_FLOAT bottomy = pText->m_Bottom;
112   FX_FLOAT leftx = pText->m_Left;
113   int cc = 0;
114   CFX_ByteString segment;
115   int space_count = 0;
116   FX_FLOAT last_left = 0, last_right = 0, segment_left = 0, segment_right = 0;
117   for (int i = 0; i < pText->m_nChars; i++) {
118     FX_DWORD charcode = pText->m_nChars == 1
119                             ? (FX_DWORD)(uintptr_t)pText->m_pCharCodes
120                             : pText->m_pCharCodes[i];
121     if (charcode == (FX_DWORD)-1) {
122       continue;
123     }
124     FX_FLOAT char_left = pPosArray[cc * 2];
125     FX_FLOAT char_right = pPosArray[cc * 2 + 1];
126     cc++;
127     if (char_left < last_left || (char_left - last_right) > spacew / 2) {
128       pBaseLine = InsertTextBox(pBaseLine, baseline, leftx + segment_left,
129                                 leftx + segment_right, topy, bottomy, spacew,
130                                 fontsize_v, segment, pFont);
131       segment_left = char_left;
132       segment = "";
133     }
134     if (space_count > 1) {
135       pBaseLine = InsertTextBox(pBaseLine, baseline, leftx + segment_left,
136                                 leftx + segment_right, topy, bottomy, spacew,
137                                 fontsize_v, segment, pFont);
138       segment = "";
139     } else if (space_count == 1) {
140       pFont->AppendChar(segment, ' ');
141     }
142     if (segment.GetLength() == 0) {
143       segment_left = char_left;
144     }
145     segment_right = char_right;
146     pFont->AppendChar(segment, charcode);
147     space_count = 0;
148     last_left = char_left;
149     last_right = char_right;
150   }
151   if (segment.GetLength())
152     pBaseLine = InsertTextBox(pBaseLine, baseline, leftx + segment_left,
153                               leftx + segment_right, topy, bottomy, spacew,
154                               fontsize_v, segment, pFont);
155   FX_Free(pPosArray);
156 }
157 CTextBaseLine* CTextPage::InsertTextBox(CTextBaseLine* pBaseLine,
158                                         FX_FLOAT basey,
159                                         FX_FLOAT leftx,
160                                         FX_FLOAT rightx,
161                                         FX_FLOAT topy,
162                                         FX_FLOAT bottomy,
163                                         FX_FLOAT spacew,
164                                         FX_FLOAT fontsize_v,
165                                         CFX_ByteString& str,
166                                         CPDF_Font* pFont) {
167   if (str.GetLength() == 0) {
168     return NULL;
169   }
170   if (pBaseLine == NULL) {
171     int i;
172     for (i = 0; i < m_BaseLines.GetSize(); i++) {
173       CTextBaseLine* pExistLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
174       if (pExistLine->m_BaseLine == basey) {
175         pBaseLine = pExistLine;
176         break;
177       }
178       if (pExistLine->m_BaseLine < basey) {
179         break;
180       }
181     }
182     if (pBaseLine == NULL) {
183       pBaseLine = new CTextBaseLine;
184       pBaseLine->m_BaseLine = basey;
185       m_BaseLines.InsertAt(i, pBaseLine);
186     }
187   }
188   CFX_WideString text;
189   const FX_CHAR* pStr = str;
190   int len = str.GetLength(), offset = 0;
191   while (offset < len) {
192     FX_DWORD ch = pFont->GetNextChar(pStr, len, offset);
193     CFX_WideString unicode_str = pFont->UnicodeFromCharCode(ch);
194     if (unicode_str.IsEmpty()) {
195       text += (FX_WCHAR)ch;
196     } else {
197       text += unicode_str;
198     }
199   }
200   pBaseLine->InsertTextBox(leftx, rightx, topy, bottomy, spacew, fontsize_v,
201                            text);
202   return pBaseLine;
203 }
204 void CTextPage::WriteOutput(CFX_WideStringArray& lines, int iMinWidth) {
205   FX_FLOAT lastheight = -1;
206   FX_FLOAT lastbaseline = -1;
207   FX_FLOAT MinLeftX = 1000000;
208   FX_FLOAT MaxRightX = 0;
209   int i;
210   for (i = 0; i < m_BaseLines.GetSize(); i++) {
211     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
212     FX_FLOAT leftx, rightx;
213     if (pBaseLine->GetWidth(leftx, rightx)) {
214       if (leftx < MinLeftX) {
215         MinLeftX = leftx;
216       }
217       if (rightx > MaxRightX) {
218         MaxRightX = rightx;
219       }
220     }
221   }
222   for (i = 0; i < m_BaseLines.GetSize(); i++) {
223     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
224     pBaseLine->MergeBoxes();
225   }
226   for (i = 1; i < m_BaseLines.GetSize(); i++) {
227     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
228     CTextBaseLine* pPrevLine = (CTextBaseLine*)m_BaseLines.GetAt(i - 1);
229     if (pBaseLine->CanMerge(pPrevLine)) {
230       pPrevLine->Merge(pBaseLine);
231       delete pBaseLine;
232       m_BaseLines.RemoveAt(i);
233       i--;
234     }
235   }
236   if (m_bAutoWidth) {
237     int* widths = FX_Alloc(int, m_BaseLines.GetSize());
238     for (i = 0; i < m_BaseLines.GetSize(); i++) {
239       widths[i] = 0;
240       CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
241       int TotalChars = 0;
242       FX_FLOAT TotalWidth = 0;
243       int minchars;
244       pBaseLine->CountChars(TotalChars, TotalWidth, minchars);
245       if (TotalChars) {
246         FX_FLOAT charwidth = TotalWidth / TotalChars;
247         widths[i] = (int)((MaxRightX - MinLeftX) / charwidth);
248       }
249       if (widths[i] > 1000) {
250         widths[i] = 1000;
251       }
252       if (widths[i] < minchars) {
253         widths[i] = minchars;
254       }
255     }
256     int AvgWidth = 0, widthcount = 0;
257     for (i = 0; i < m_BaseLines.GetSize(); i++)
258       if (widths[i]) {
259         AvgWidth += widths[i];
260         widthcount++;
261       }
262     AvgWidth = int((FX_FLOAT)AvgWidth / widthcount + 0.5);
263     int MaxWidth = 0;
264     for (i = 0; i < m_BaseLines.GetSize(); i++)
265       if (MaxWidth < widths[i]) {
266         MaxWidth = widths[i];
267       }
268     if (MaxWidth > AvgWidth * 6 / 5) {
269       MaxWidth = AvgWidth * 6 / 5;
270     }
271     FX_Free(widths);
272     if (iMinWidth < MaxWidth) {
273       iMinWidth = MaxWidth;
274     }
275   }
276   for (i = 0; i < m_BaseLines.GetSize(); i++) {
277     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
278     pBaseLine->MergeBoxes();
279   }
280   if (m_bKeepColumn) {
281     FindColumns();
282   }
283   for (i = 0; i < m_BaseLines.GetSize(); i++) {
284     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
285     if (lastheight >= 0) {
286       FX_FLOAT dy = lastbaseline - pBaseLine->m_BaseLine;
287       if (dy >= (pBaseLine->m_MaxFontSizeV) * 1.5 || dy >= lastheight * 1.5) {
288         lines.Add(L"");
289       }
290     }
291     lastheight = pBaseLine->m_MaxFontSizeV;
292     lastbaseline = pBaseLine->m_BaseLine;
293     CFX_WideString str;
294     pBaseLine->WriteOutput(str, MinLeftX, MaxRightX - MinLeftX, iMinWidth);
295     lines.Add(str);
296   }
297 }
298 void NormalizeCompositeChar(FX_WCHAR wChar, CFX_WideString& sDest) {
299   wChar = FX_GetMirrorChar(wChar, TRUE, FALSE);
300   FX_WCHAR* pDst = NULL;
301   FX_STRSIZE nCount = FX_Unicode_GetNormalization(wChar, pDst);
302   if (nCount < 1) {
303     sDest += wChar;
304     return;
305   }
306   pDst = new FX_WCHAR[nCount];
307   FX_Unicode_GetNormalization(wChar, pDst);
308   for (int nIndex = 0; nIndex < nCount; nIndex++) {
309     sDest += pDst[nIndex];
310   }
311   delete[] pDst;
312 }
313 void NormalizeString(CFX_WideString& str) {
314   if (str.GetLength() <= 0) {
315     return;
316   }
317   CFX_WideString sBuffer;
318   nonstd::unique_ptr<CFX_BidiChar> pBidiChar(new CFX_BidiChar);
319   CFX_WordArray order;
320   FX_BOOL bR2L = FALSE;
321   int32_t start = 0, count = 0, i = 0;
322   int nR2L = 0, nL2R = 0;
323   for (i = 0; i < str.GetLength(); i++) {
324     if (pBidiChar->AppendChar(str.GetAt(i))) {
325       CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
326       order.Add(start);
327       order.Add(count);
328       order.Add(ret);
329       if (!bR2L) {
330         if (ret == CFX_BidiChar::RIGHT) {
331           nR2L++;
332         } else if (ret == CFX_BidiChar::LEFT) {
333           nL2R++;
334         }
335       }
336     }
337   }
338   if (pBidiChar->EndChar()) {
339     CFX_BidiChar::Direction ret = pBidiChar->GetBidiInfo(&start, &count);
340     order.Add(start);
341     order.Add(count);
342     order.Add(ret);
343     if (!bR2L) {
344       if (ret == CFX_BidiChar::RIGHT) {
345         nR2L++;
346       } else if (ret == CFX_BidiChar::LEFT) {
347         nL2R++;
348       }
349     }
350   }
351   if (nR2L > 0 && nR2L >= nL2R) {
352     bR2L = TRUE;
353   }
354   if (bR2L) {
355     int count = order.GetSize();
356     for (int j = count - 1; j > 0; j -= 3) {
357       int ret = order.GetAt(j);
358       int start = order.GetAt(j - 2);
359       int count1 = order.GetAt(j - 1);
360       if (ret == 2 || ret == 0) {
361         for (int i = start + count1 - 1; i >= start; i--) {
362           NormalizeCompositeChar(str[i], sBuffer);
363         }
364       } else {
365         i = j;
366         FX_BOOL bSymbol = FALSE;
367         while (i > 0 && order.GetAt(i) != 2) {
368           bSymbol = !order.GetAt(i);
369           i -= 3;
370         }
371         int end = start + count1;
372         int n = 0;
373         if (bSymbol) {
374           n = i + 6;
375         } else {
376           n = i + 3;
377         }
378         if (n >= j) {
379           for (int m = start; m < end; m++) {
380             sBuffer += str[m];
381           }
382         } else {
383           i = j;
384           j = n;
385           for (; n <= i; n += 3) {
386             int start = order.GetAt(n - 2);
387             int count1 = order.GetAt(n - 1);
388             int end = start + count1;
389             for (int m = start; m < end; m++) {
390               sBuffer += str[m];
391             }
392           }
393         }
394       }
395     }
396   } else {
397     int count = order.GetSize();
398     FX_BOOL bL2R = FALSE;
399     for (int j = 0; j < count; j += 3) {
400       int ret = order.GetAt(j + 2);
401       int start = order.GetAt(j);
402       int count1 = order.GetAt(j + 1);
403       if (ret == 2 || (j == 0 && ret == 0 && !bL2R)) {
404         int i = j + 3;
405         while (bR2L && i < count) {
406           if (order.GetAt(i + 2) == 1) {
407             break;
408           } else {
409             i += 3;
410           }
411         }
412         if (i == 3) {
413           j = -3;
414           bL2R = TRUE;
415           continue;
416         }
417         int end = str.GetLength() - 1;
418         if (i < count) {
419           end = order.GetAt(i) - 1;
420         }
421         j = i - 3;
422         for (int n = end; n >= start; n--) {
423           NormalizeCompositeChar(str[i], sBuffer);
424         }
425       } else {
426         int end = start + count1;
427         for (int i = start; i < end; i++) {
428           sBuffer += str[i];
429         }
430       }
431     }
432   }
433   str.Empty();
434   str += sBuffer;
435 }
436 static FX_BOOL IsNumber(CFX_WideString& str) {
437   for (int i = 0; i < str.GetLength(); i++) {
438     FX_WCHAR ch = str[i];
439     if ((ch < '0' || ch > '9') && ch != '-' && ch != '+' && ch != '.' &&
440         ch != ' ') {
441       return FALSE;
442     }
443   }
444   return TRUE;
445 }
446 void CTextPage::FindColumns() {
447   int i;
448   for (i = 0; i < m_BaseLines.GetSize(); i++) {
449     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
450     for (int j = 0; j < pBaseLine->m_TextList.GetSize(); j++) {
451       CTextBox* pTextBox = (CTextBox*)pBaseLine->m_TextList.GetAt(j);
452       CTextColumn* pColumn = FindColumn(pTextBox->m_Right);
453       if (pColumn == NULL) {
454         pColumn = new CTextColumn;
455         pColumn->m_Count = 1;
456         pColumn->m_AvgPos = pTextBox->m_Right;
457         pColumn->m_TextPos = -1;
458         m_TextColumns.Add(pColumn);
459       } else {
460         pColumn->m_AvgPos =
461             (pColumn->m_Count * pColumn->m_AvgPos + pTextBox->m_Right) /
462             (pColumn->m_Count + 1);
463         pColumn->m_Count++;
464       }
465     }
466   }
467   int mincount = m_BaseLines.GetSize() / 4;
468   for (i = 0; i < m_TextColumns.GetSize(); i++) {
469     CTextColumn* pTextColumn = (CTextColumn*)m_TextColumns.GetAt(i);
470     if (pTextColumn->m_Count >= mincount) {
471       continue;
472     }
473     delete pTextColumn;
474     m_TextColumns.RemoveAt(i);
475     i--;
476   }
477   for (i = 0; i < m_BaseLines.GetSize(); i++) {
478     CTextBaseLine* pBaseLine = (CTextBaseLine*)m_BaseLines.GetAt(i);
479     for (int j = 0; j < pBaseLine->m_TextList.GetSize(); j++) {
480       CTextBox* pTextBox = (CTextBox*)pBaseLine->m_TextList.GetAt(j);
481       if (IsNumber(pTextBox->m_Text)) {
482         pTextBox->m_pColumn = FindColumn(pTextBox->m_Right);
483       }
484     }
485   }
486 }
487 CTextColumn* CTextPage::FindColumn(FX_FLOAT xpos) {
488   for (int i = 0; i < m_TextColumns.GetSize(); i++) {
489     CTextColumn* pColumn = (CTextColumn*)m_TextColumns.GetAt(i);
490     if (pColumn->m_AvgPos < xpos + 1 && pColumn->m_AvgPos > xpos - 1) {
491       return pColumn;
492     }
493   }
494   return NULL;
495 }
496 void CTextPage::BreakSpace(CPDF_TextObject* pTextObj) {}
497 CTextBaseLine::CTextBaseLine() {
498   m_Top = -100000;
499   m_Bottom = 100000;
500   m_MaxFontSizeV = 0;
501 }
502 CTextBaseLine::~CTextBaseLine() {
503   for (int i = 0; i < m_TextList.GetSize(); i++) {
504     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
505     delete pText;
506   }
507 }
508 void CTextBaseLine::InsertTextBox(FX_FLOAT leftx,
509                                   FX_FLOAT rightx,
510                                   FX_FLOAT topy,
511                                   FX_FLOAT bottomy,
512                                   FX_FLOAT spacew,
513                                   FX_FLOAT fontsize_v,
514                                   const CFX_WideString& text) {
515   if (m_Top < topy) {
516     m_Top = topy;
517   }
518   if (m_Bottom > bottomy) {
519     m_Bottom = bottomy;
520   }
521   if (m_MaxFontSizeV < fontsize_v) {
522     m_MaxFontSizeV = fontsize_v;
523   }
524   int i;
525   for (i = 0; i < m_TextList.GetSize(); i++) {
526     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
527     if (pText->m_Left > leftx) {
528       break;
529     }
530   }
531   CTextBox* pText = new CTextBox;
532   pText->m_Text = text;
533   pText->m_Left = leftx;
534   pText->m_Right = rightx;
535   pText->m_Top = topy;
536   pText->m_Bottom = bottomy;
537   pText->m_SpaceWidth = spacew;
538   pText->m_FontSizeV = fontsize_v;
539   pText->m_pColumn = NULL;
540   m_TextList.InsertAt(i, pText);
541 }
542 FX_BOOL GetIntersection(FX_FLOAT low1,
543                         FX_FLOAT high1,
544                         FX_FLOAT low2,
545                         FX_FLOAT high2,
546                         FX_FLOAT& interlow,
547                         FX_FLOAT& interhigh);
548 FX_BOOL CTextBaseLine::CanMerge(CTextBaseLine* pOther) {
549   FX_FLOAT inter_top, inter_bottom;
550   if (!GetIntersection(m_Bottom, m_Top, pOther->m_Bottom, pOther->m_Top,
551                        inter_bottom, inter_top)) {
552     return FALSE;
553   }
554   FX_FLOAT inter_h = inter_top - inter_bottom;
555   if (inter_h < (m_Top - m_Bottom) / 2 &&
556       inter_h < (pOther->m_Top - pOther->m_Bottom) / 2) {
557     return FALSE;
558   }
559   FX_FLOAT dy = (FX_FLOAT)FXSYS_fabs(m_BaseLine - pOther->m_BaseLine);
560   for (int i = 0; i < m_TextList.GetSize(); i++) {
561     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
562     for (int j = 0; j < pOther->m_TextList.GetSize(); j++) {
563       CTextBox* pOtherText = (CTextBox*)pOther->m_TextList.GetAt(j);
564       FX_FLOAT inter_left, inter_right;
565       if (!GetIntersection(pText->m_Left, pText->m_Right, pOtherText->m_Left,
566                            pOtherText->m_Right, inter_left, inter_right)) {
567         continue;
568       }
569       FX_FLOAT inter_w = inter_right - inter_left;
570       if (inter_w < pText->m_SpaceWidth / 2 &&
571           inter_w < pOtherText->m_SpaceWidth / 2) {
572         continue;
573       }
574       if (dy >= (pText->m_Bottom - pText->m_Top) / 2 ||
575           dy >= (pOtherText->m_Bottom - pOtherText->m_Top) / 2) {
576         return FALSE;
577       }
578     }
579   }
580   return TRUE;
581 }
582 void CTextBaseLine::Merge(CTextBaseLine* pOther) {
583   for (int i = 0; i < pOther->m_TextList.GetSize(); i++) {
584     CTextBox* pText = (CTextBox*)pOther->m_TextList.GetAt(i);
585     InsertTextBox(pText->m_Left, pText->m_Right, pText->m_Top, pText->m_Bottom,
586                   pText->m_SpaceWidth, pText->m_FontSizeV, pText->m_Text);
587   }
588 }
589 FX_BOOL CTextBaseLine::GetWidth(FX_FLOAT& leftx, FX_FLOAT& rightx) {
590   int i;
591   for (i = 0; i < m_TextList.GetSize(); i++) {
592     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
593     if (pText->m_Text != L" ") {
594       break;
595     }
596   }
597   if (i == m_TextList.GetSize()) {
598     return FALSE;
599   }
600   CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
601   leftx = pText->m_Left;
602   for (i = m_TextList.GetSize() - 1; i >= 0; i--) {
603     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
604     if (pText->m_Text != L" ") {
605       break;
606     }
607   }
608   pText = (CTextBox*)m_TextList.GetAt(i);
609   rightx = pText->m_Right;
610   return TRUE;
611 }
612 void CTextBaseLine::MergeBoxes() {
613   int i = 0;
614   while (1) {
615     if (i >= m_TextList.GetSize() - 1) {
616       break;
617     }
618     CTextBox* pThisText = (CTextBox*)m_TextList.GetAt(i);
619     CTextBox* pNextText = (CTextBox*)m_TextList.GetAt(i + 1);
620     FX_FLOAT dx = pNextText->m_Left - pThisText->m_Right;
621     FX_FLOAT spacew = (pThisText->m_SpaceWidth == 0.0)
622                           ? pNextText->m_SpaceWidth
623                           : pThisText->m_SpaceWidth;
624     if (spacew > 0.0 && dx < spacew * 2) {
625       pThisText->m_Right = pNextText->m_Right;
626       if (dx > spacew * 1.5) {
627         pThisText->m_Text += L"  ";
628       } else if (dx > spacew / 3) {
629         pThisText->m_Text += L' ';
630       }
631       pThisText->m_Text += pNextText->m_Text;
632       pThisText->m_SpaceWidth =
633           pNextText->m_SpaceWidth == 0.0 ? spacew : pNextText->m_SpaceWidth;
634       m_TextList.RemoveAt(i + 1);
635       delete pNextText;
636     } else {
637       i++;
638     }
639   }
640 }
641 void CTextBaseLine::WriteOutput(CFX_WideString& str,
642                                 FX_FLOAT leftx,
643                                 FX_FLOAT pagewidth,
644                                 int iTextWidth) {
645   int lastpos = -1;
646   for (int i = 0; i < m_TextList.GetSize(); i++) {
647     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
648     int xpos;
649     if (pText->m_pColumn) {
650       xpos =
651           (int)((pText->m_pColumn->m_AvgPos - leftx) * iTextWidth / pagewidth +
652                 0.5);
653       xpos -= pText->m_Text.GetLength();
654     } else {
655       xpos = (int)((pText->m_Left - leftx) * iTextWidth / pagewidth + 0.5);
656     }
657     if (xpos <= lastpos) {
658       xpos = lastpos + 1;
659     }
660     for (int j = lastpos + 1; j < xpos; j++) {
661       str += ' ';
662     }
663     CFX_WideString sSrc(pText->m_Text);
664     NormalizeString(sSrc);
665     str += sSrc;
666     str += ' ';
667     lastpos = xpos + pText->m_Text.GetLength();
668   }
669 }
670 void CTextBaseLine::CountChars(int& count, FX_FLOAT& width, int& minchars) {
671   minchars = 0;
672   for (int i = 0; i < m_TextList.GetSize(); i++) {
673     CTextBox* pText = (CTextBox*)m_TextList.GetAt(i);
674     if (pText->m_Right - pText->m_Left < 0.002) {
675       continue;
676     }
677     count += pText->m_Text.GetLength();
678     width += pText->m_Right - pText->m_Left;
679     minchars += pText->m_Text.GetLength() + 1;
680   }
681 }
682 #define PI 3.1415926535897932384626433832795
683 static void CheckRotate(CPDF_Page& page, CFX_FloatRect& page_bbox) {
684   int total_count = 0, rotated_count[3] = {0, 0, 0};
685   FX_POSITION pos = page.GetFirstObjectPosition();
686   while (pos) {
687     CPDF_PageObject* pObj = page.GetNextObject(pos);
688     if (pObj->m_Type != PDFPAGE_TEXT) {
689       continue;
690     }
691     total_count++;
692     CPDF_TextObject* pText = (CPDF_TextObject*)pObj;
693     FX_FLOAT angle = pText->m_TextState.GetBaselineAngle();
694     if (angle == 0.0) {
695       continue;
696     }
697     int degree = (int)(angle * 180 / PI + 0.5);
698     if (degree % 90) {
699       continue;
700     }
701     if (degree < 0) {
702       degree += 360;
703     }
704     int index = degree / 90 % 3 - 1;
705     if (index < 0) {
706       continue;
707     }
708     rotated_count[index]++;
709   }
710   if (total_count == 0) {
711     return;
712   }
713   CFX_AffineMatrix matrix;
714   if (rotated_count[0] > total_count * 2 / 3) {
715     matrix.Set(0, -1, 1, 0, 0, page.GetPageHeight());
716   } else if (rotated_count[1] > total_count * 2 / 3) {
717     matrix.Set(-1, 0, 0, -1, page.GetPageWidth(), page.GetPageHeight());
718   } else if (rotated_count[2] > total_count * 2 / 3) {
719     matrix.Set(0, 1, -1, 0, page.GetPageWidth(), 0);
720   } else {
721     return;
722   }
723   page.Transform(matrix);
724   page_bbox.Transform(&matrix);
725 }
726 void PDF_GetPageText_Unicode(CFX_WideStringArray& lines,
727                              CPDF_Document* pDoc,
728                              CPDF_Dictionary* pPage,
729                              int iMinWidth,
730                              FX_DWORD flags) {
731   lines.RemoveAll();
732   if (pPage == NULL) {
733     return;
734   }
735   CPDF_Page page;
736   page.Load(pDoc, pPage);
737   CPDF_ParseOptions options;
738   options.m_bTextOnly = TRUE;
739   options.m_bSeparateForm = FALSE;
740   page.ParseContent(&options);
741   CFX_FloatRect page_bbox = page.GetPageBBox();
742   if (flags & PDF2TXT_AUTO_ROTATE) {
743     CheckRotate(page, page_bbox);
744   }
745   CTextPage texts;
746   texts.m_bAutoWidth = flags & PDF2TXT_AUTO_WIDTH;
747   texts.m_bKeepColumn = flags & PDF2TXT_KEEP_COLUMN;
748   texts.m_bBreakSpace = TRUE;
749   FX_POSITION pos = page.GetFirstObjectPosition();
750   while (pos) {
751     CPDF_PageObject* pObject = page.GetNextObject(pos);
752     if (!(flags & PDF2TXT_INCLUDE_INVISIBLE)) {
753       CFX_FloatRect rect(pObject->m_Left, pObject->m_Bottom, pObject->m_Right,
754                          pObject->m_Top);
755       if (!page_bbox.Contains(rect)) {
756         continue;
757       }
758     }
759     texts.ProcessObject(pObject);
760   }
761   texts.WriteOutput(lines, iMinWidth);
762 }
763 void PDF_GetPageText(CFX_ByteStringArray& lines,
764                      CPDF_Document* pDoc,
765                      CPDF_Dictionary* pPage,
766                      int iMinWidth,
767                      FX_DWORD flags) {
768   lines.RemoveAll();
769   CFX_WideStringArray wlines;
770   PDF_GetPageText_Unicode(wlines, pDoc, pPage, iMinWidth, flags);
771   for (int i = 0; i < wlines.GetSize(); i++) {
772     CFX_WideString wstr = wlines[i];
773     CFX_ByteString str;
774     for (int c = 0; c < wstr.GetLength(); c++) {
775       str += CharFromUnicodeAlt(wstr[c], FXSYS_GetACP(), "?");
776     }
777     lines.Add(str);
778   }
779 }
780 void PDF_GetTextStream_Unicode(CFX_WideTextBuf& buffer,
781                                CPDF_Document* pDoc,
782                                CPDF_Dictionary* pPage,
783                                FX_DWORD flags) {
784   buffer.EstimateSize(0, 10240);
785   CPDF_Page page;
786   page.Load(pDoc, pPage);
787   CPDF_ParseOptions options;
788   options.m_bTextOnly = TRUE;
789   options.m_bSeparateForm = FALSE;
790   page.ParseContent(&options);
791   GetTextStream_Unicode(buffer, &page, TRUE, NULL);
792 }