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