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 <algorithm>
8
9#include "reflowedtextpage.h"
10
11IPDF_TextPage*	IPDF_TextPage::CreateReflowTextPage(IPDF_ReflowedPage* pRefPage)
12{
13    return new CRF_TextPage(pRefPage);
14}
15CRF_TextPage::CRF_TextPage(IPDF_ReflowedPage* pRefPage)
16{
17    m_pRefPage = (CPDF_ReflowedPage*)(pRefPage);
18    m_pDataList = NULL;
19    m_CountBSArray = NULL;
20}
21CRF_TextPage::~CRF_TextPage()
22{
23    if(m_pDataList) {
24        delete m_pDataList;
25        m_pDataList = NULL;
26    }
27    if(m_CountBSArray) {
28        delete m_CountBSArray;
29        m_CountBSArray = NULL;
30    }
31}
32FX_BOOL CRF_TextPage::ParseTextPage()
33{
34    if(!m_pRefPage) {
35        return FALSE;
36    }
37    int count = m_pRefPage->m_pReflowed->GetSize();
38    m_pDataList = new CRF_CharDataPtrArray(std::min(count, 500));
39    for(int i = 0; i < count; i++) {
40        CRF_Data* pData = (*(m_pRefPage->m_pReflowed))[i];
41        if(pData->GetType() == CRF_Data::Text) {
42            m_pDataList->Add((CRF_CharData*)pData);
43        }
44    }
45    m_CountBSArray = new CFX_CountBSINT32Array(20);
46    return TRUE;
47}
48FX_BOOL	CRF_TextPage::IsParsered() const
49{
50    if(m_pDataList) {
51        return TRUE;
52    }
53    return FALSE;
54}
55int CRF_TextPage::CharIndexFromTextIndex(int TextIndex) const
56{
57    return TextIndex;
58}
59int CRF_TextPage::TextIndexFromCharIndex(int CharIndex) const
60{
61    return CharIndex;
62}
63
64int	CRF_TextPage::CountChars() const
65{
66    if (NULL == m_pDataList) {
67        return -1;
68    }
69    return m_pDataList->GetSize();
70}
71void CRF_TextPage::GetCharInfo(int index, FPDF_CHAR_INFO & info) const
72{
73    if(index >= CountChars() || index < 0 || !m_pDataList) {
74        return;
75    }
76    CRF_CharData* pData = (*m_pDataList)[index];
77    FX_FLOAT ReltiveCorddDs = pData->m_pCharState->m_fDescent;
78    FX_FLOAT ReltiveCorddAs = pData->m_pCharState->m_fAscent;
79    info.m_Flag		= CHAR_NORMAL;
80    info.m_pTextObj	= pData->m_pCharState->m_pTextObj;
81    info.m_OriginX	= pData->m_PosX;
82    info.m_OriginY	= pData->m_PosY - ReltiveCorddDs;
83    info.m_FontSize	= pData->m_pCharState->m_fFontSize;
84    CFX_FloatRect FloatRectTmp(pData->m_PosX, pData->m_PosY, pData->m_PosX + pData->m_Width, pData->m_PosY + ReltiveCorddAs - ReltiveCorddDs);
85    info.m_CharBox	= FloatRectTmp;
86    CFX_WideString str = pData->m_pCharState->m_pFont->UnicodeFromCharCode(pData->m_CharCode);
87    if(!str.IsEmpty()) {
88        info.m_Unicode	= str.GetAt(0);
89    } else {
90        info.m_Unicode = -1;
91    }
92    info.m_Charcode = (FX_WCHAR)pData->m_CharCode;
93    info.m_Matrix = CFX_Matrix(1, 0, 0, 1, 0, 0);
94}
95extern FX_BOOL GetIntersection(FX_FLOAT low1, FX_FLOAT high1, FX_FLOAT low2, FX_FLOAT high2, FX_FLOAT& interlow, FX_FLOAT& interhigh);
96inline FX_BOOL _IsInsameline(const CFX_FloatRect& rectA, const CFX_FloatRect& rectB)
97{
98    if((rectA.top >= rectB.bottom && rectB.top >= rectA.bottom)) {
99        return TRUE;
100    } else {
101        return FALSE;
102    }
103}
104inline FX_BOOL _IsIntersect(const CFX_FloatRect& rectA, const CFX_FloatRect& rectB)
105{
106    FX_FLOAT interlow = .0f, interhigh = .0f;
107    if(GetIntersection(rectA.bottom, rectA.top, rectB.bottom, rectB.top, interlow, interhigh)) {
108        if(GetIntersection(rectA.left, rectA.right, rectB.left, rectB.right, interlow, interhigh)) {
109            return TRUE;
110        } else {
111            return FALSE;
112        }
113    }
114    return FALSE;
115}
116void CRF_TextPage::GetRectArray(int start, int nCount, CFX_RectArray& rectArray) const
117{
118    int indexlen = start + nCount;
119    FPDF_CHAR_INFO info;
120    FX_BOOL bstart = TRUE;
121    CFX_FloatRect recttmp;
122    int i;
123    for(i = start; i < indexlen; i++) {
124        GetCharInfo(i, info);
125        if(bstart) {
126            recttmp = info.m_CharBox;
127            bstart = FALSE;
128        } else if(_IsInsameline(recttmp, info.m_CharBox)) {
129            recttmp.right = info.m_CharBox.right;
130            if(info.m_CharBox.top > recttmp.top) {
131                recttmp.top = info.m_CharBox.top;
132            }
133            if(info.m_CharBox.bottom < recttmp.bottom) {
134                recttmp.bottom = info.m_CharBox.bottom;
135            }
136        } else {
137            rectArray.Add(recttmp);
138            recttmp = info.m_CharBox;
139        }
140    }
141    rectArray.Add(recttmp);
142}
143inline FX_FLOAT _GetDistance(CFX_FloatRect floatRect, CPDF_Point point)
144{
145    if(floatRect.right < point.x && floatRect.bottom > point.y) {
146        return FXSYS_sqrt(FXSYS_pow(point.x - floatRect.right, 2) + FXSYS_pow(floatRect.bottom - point.y, 2));
147    }
148    if (floatRect.right < point.x && floatRect.top < point.y) {
149        return FXSYS_sqrt(FXSYS_pow(point.x - floatRect.right, 2) + FXSYS_pow(point.y - floatRect.top, 2));
150    }
151    if(floatRect.left > point.x && floatRect.bottom > point.y) {
152        return FXSYS_sqrt(FXSYS_pow(floatRect.bottom - point.y, 2) + FXSYS_pow(floatRect.left - point.x, 2));
153    }
154    if((floatRect.right > point.x || FXSYS_fabs(floatRect.right - point.x) <= 0.0001f) &&
155            (floatRect.left < point.x || FXSYS_fabs(floatRect.left - point.x) <= 0.0001f) && floatRect.bottom > point.y) {
156        return FXSYS_fabs(floatRect.bottom - point.y);
157    }
158    if(floatRect.left > point.x && (floatRect.bottom < point.y || FXSYS_fabs(floatRect.bottom - point.y) <= 0.0001f) &&
159            (floatRect.top > point.y || FXSYS_fabs(floatRect.top - point.y) <= 0.0001f)) {
160        return FXSYS_fabs(floatRect.left - point.x);
161    }
162    if(floatRect.left > point.x && floatRect.top < point.y) {
163        return FXSYS_sqrt(FXSYS_pow(floatRect.left - point.x, 2) + FXSYS_pow(point.y - floatRect.top, 2));
164    }
165    if ((floatRect.left < point.x || FXSYS_fabs(floatRect.left - point.x) <= 0.0001f) &&
166            (floatRect.right > point.x || FXSYS_fabs(floatRect.right - point.x) <= 0.0001f) && floatRect.top < point.y) {
167        return FXSYS_fabs(point.y - floatRect.top);
168    }
169    if(floatRect.right < point.x && (floatRect.top > point.y || FXSYS_fabs(floatRect.top - point.y) <= 0.0001f) &&
170            (floatRect.bottom < point.y || FXSYS_fabs(floatRect.bottom - point.y) <= 0.0001f)) {
171        return point.x - floatRect.right;
172    }
173    return .0f;
174}
175int CRF_TextPage::GetIndexAtPos(CPDF_Point point, FX_FLOAT xTorelance, FX_FLOAT yTorelance) const
176{
177    int index = -1, i = 0, j = 0;
178    FPDF_CHAR_INFO info;
179    CFX_FloatRect rectTmp;
180    FX_FLOAT MinDistance = 1000, DistanceTmp = 0;
181    FX_FLOAT rect_bottom = point.x - xTorelance;
182    CFX_FloatRect TorelanceRect(rect_bottom <= 0 ? 0 : rect_bottom, point.y - yTorelance, point.x + xTorelance, point.y + yTorelance);
183    int count = CountChars();
184    for(i = 0; i < count; i++) {
185        GetCharInfo(i, info);
186        rectTmp = info.m_CharBox;
187        if(rectTmp.Contains(point.x, point.y)) {
188            index = i;
189            break;
190        } else if(_IsIntersect(rectTmp, TorelanceRect)) {
191            DistanceTmp = _GetDistance(rectTmp, point);
192            if(DistanceTmp < MinDistance) {
193                MinDistance = DistanceTmp;
194                index = i;
195            }
196        }
197    }
198    return index;
199}
200int CRF_TextPage::GetIndexAtPos(FX_FLOAT x, FX_FLOAT y, FX_FLOAT xTorelance, FX_FLOAT yTorelance) const
201{
202    int index = 0;
203    CPDF_Point point(x, y);
204    if((index = GetIndexAtPos(point, xTorelance, yTorelance)) < 0) {
205        return -1;
206    } else {
207        return index;
208    }
209}
210int CRF_TextPage::GetOrderByDirection(int index, int direction) const
211{
212    return -1;
213}
214CFX_WideString CRF_TextPage::GetTextByRect(CFX_FloatRect rect) const
215{
216    int count;
217    FPDF_CHAR_INFO info;
218    CFX_WideString str;
219    CFX_FloatRect  Recttmp;
220    FX_BOOL bstart = TRUE;
221    count = CountChars();
222    if(rect.IsEmpty()) {
223        return L"";
224    }
225    for(int i = 0; i < count; i++) {
226        GetCharInfo(i, info);
227        if(_IsIntersect(rect, info.m_CharBox)) {
228            if(bstart) {
229                Recttmp = info.m_CharBox;
230                str += info.m_Unicode;
231                bstart = FALSE;
232            } else if(_IsInsameline(Recttmp, info.m_CharBox)) {
233                str += info.m_Unicode;
234            } else {
235                str += L"\r\n";
236                Recttmp = info.m_CharBox;
237                str += info.m_Unicode;
238            }
239        }
240    }
241    if(str.IsEmpty()) {
242        return L"";
243    } else {
244        return str;
245    }
246}
247void CRF_TextPage::GetRectsArrayByRect(CFX_FloatRect rect, CFX_RectArray& resRectArray) const
248{
249    int count, i;
250    FX_BOOL bstart = TRUE;
251    FPDF_CHAR_INFO info;
252    CFX_FloatRect recttmp;
253    count = CountChars();
254    for(i = 0; i < count; i++) {
255        GetCharInfo(i, info);
256        if(_IsIntersect(rect, info.m_CharBox)) {
257            if(bstart) {
258                recttmp = info.m_CharBox;
259                bstart = FALSE;
260            } else if(_IsInsameline(recttmp, info.m_CharBox)) {
261                recttmp.right = info.m_CharBox.right;
262                if(info.m_CharBox.top > recttmp.top) {
263                    recttmp.top = info.m_CharBox.top;
264                }
265                if(info.m_CharBox.bottom < recttmp.bottom) {
266                    recttmp.bottom = info.m_CharBox.bottom;
267                }
268            } else {
269                resRectArray.Add(recttmp);
270                recttmp = info.m_CharBox;
271            }
272        }
273    }
274    resRectArray.Add(recttmp);
275}
276int CRF_TextPage::CountRects(int start, int nCount)
277{
278    m_rectArray.RemoveAll();
279    GetRectArray(start, nCount, m_rectArray);
280    return m_rectArray.GetSize();
281}
282void CRF_TextPage::GetRect(int rectIndex, FX_FLOAT& left, FX_FLOAT& top, FX_FLOAT& right, FX_FLOAT &bottom) const
283{
284    if(m_rectArray.GetSize() <= rectIndex) {
285        return;
286    }
287    left   = m_rectArray[rectIndex].left;
288    top    = m_rectArray[rectIndex].top;
289    right  = m_rectArray[rectIndex].right;
290    bottom = m_rectArray[rectIndex].bottom;
291}
292FX_BOOL CRF_TextPage::GetBaselineRotate(int rectIndex, int& Rotate)
293{
294    Rotate = 0;
295    return TRUE;
296}
297FX_BOOL CRF_TextPage::GetBaselineRotate(CFX_FloatRect rect, int& Rotate)
298{
299    Rotate = 0;
300    return TRUE;
301}
302int CRF_TextPage::CountBoundedSegments(FX_FLOAT left, FX_FLOAT top, FX_FLOAT right, FX_FLOAT bottom, FX_BOOL bContains)
303{
304    if (!m_CountBSArray) {
305        return -1;
306    }
307    m_CountBSArray->RemoveAll();
308    CFX_FloatRect floatrect(left, bottom, right, top);
309    int totalcount, i, j = 0, counttmp = 0;
310    FX_BOOL bstart = TRUE;
311    FPDF_CHAR_INFO info;
312    CFX_FloatRect recttmp;
313    totalcount = CountChars();
314    for(i = 0; i < totalcount; i++) {
315        GetCharInfo(i, info);
316        if(_IsIntersect(floatrect, info.m_CharBox)) {
317            if(bstart) {
318                m_CountBSArray->Add(i);
319                counttmp = 1;
320                recttmp = info.m_CharBox;
321                bstart = FALSE;
322            } else if(_IsInsameline(recttmp, info.m_CharBox)) {
323                recttmp.right = info.m_CharBox.right;
324                if(info.m_CharBox.top > recttmp.top) {
325                    recttmp.top = info.m_CharBox.top;
326                }
327                if(info.m_CharBox.bottom < recttmp.bottom) {
328                    recttmp.bottom = info.m_CharBox.bottom;
329                }
330                counttmp ++;
331            } else {
332                m_CountBSArray->Add(counttmp);
333                m_CountBSArray->Add(i);
334                counttmp = 1;
335                j++;
336                recttmp = info.m_CharBox;
337            }
338        }
339    }
340    m_CountBSArray->Add(counttmp);
341    j++;
342    return j;
343}
344void CRF_TextPage::GetBoundedSegment(int index, int& start, int& count) const
345{
346    if (!m_CountBSArray) {
347        return;
348    }
349    if(m_CountBSArray->GetSize() <= index * 2) {
350        start = 0;
351        count = 0;
352        return;
353    }
354    start = *(int *)m_CountBSArray->GetAt(index * 2);
355    count = *(int *)m_CountBSArray->GetAt(index * 2 + 1);
356}
357
358int CRF_TextPage::GetWordBreak(int index, int direction) const
359{
360    return -1;
361}
362CFX_WideString CRF_TextPage::GetPageText(int start, int nCount ) const
363{
364    if(nCount == -1) {
365        nCount = CountChars();
366        start = 0;
367    } else if(nCount < 1) {
368        return L"";
369    } else if(start >= CountChars()) {
370        return L"";
371    }
372    int i, index = start + nCount;
373    FPDF_CHAR_INFO info;
374    CFX_WideString str;
375    CFX_FloatRect recttmp;
376    FX_BOOL bstart = TRUE;
377    for(i = start; i < index; i++) {
378        GetCharInfo(i, info);
379        if(bstart) {
380            recttmp = info.m_CharBox;
381            str += info.m_Unicode;
382            bstart = FALSE;
383        } else if (_IsInsameline(recttmp, info.m_CharBox)) {
384            str += info.m_Unicode;
385        } else {
386            str += L"\r\n";
387            recttmp = info.m_CharBox;
388            str += info.m_Unicode;
389        }
390    }
391    if(str.IsEmpty()) {
392        return L"";
393    }
394    return str;
395}
396