1/*
2 *******************************************************************************
3 *
4 *   Copyright (C) 2016 and later: Unicode, Inc. and others.
5 *   License & terms of use: http://www.unicode.org/copyright.html#License
6 *
7 *******************************************************************************
8 *******************************************************************************
9 *
10 *   Copyright (C) 1999-2015, International Business Machines
11 *   Corporation and others.  All Rights Reserved.
12 *
13 *******************************************************************************
14 *   file name:  Paragraph.cpp
15 *
16 *   created on: 09/06/2000
17 *   created by: Eric R. Mader
18 */
19
20#include "unicode/utypes.h"
21#include "unicode/uchar.h"
22#include "unicode/ubidi.h"
23#include "unicode/ustring.h"
24
25#include "layout/ParagraphLayout.h"
26
27#include "RenderingSurface.h"
28
29#include "paragraph.h"
30#include "UnicodeReader.h"
31
32#define MARGIN 10
33#define LINE_GROW 32
34#define PARA_GROW 8
35
36#define CH_LF 0x000A
37#define CH_CR 0x000D
38#define CH_LSEP 0x2028
39#define CH_PSEP 0x2029
40
41static LEUnicode *skipLineEnd(LEUnicode *ptr)
42{
43    if (ptr[0] == CH_CR && ptr[1] == CH_LF) {
44        ptr += 1;
45    }
46
47    return ptr + 1;
48}
49
50static le_int32 findRun(const RunArray *runArray, le_int32 offset)
51{
52    le_int32 runCount = runArray->getCount();
53
54    for (le_int32 run = 0; run < runCount; run += 1) {
55        if (runArray->getLimit(run) > offset) {
56            return run;
57        }
58    }
59
60    return -1;
61}
62
63static void subsetFontRuns(const FontRuns *fontRuns, le_int32 start, le_int32 limit, FontRuns *sub)
64{
65    le_int32 startRun = findRun(fontRuns, start);
66    le_int32 endRun   = findRun(fontRuns, limit - 1);
67
68    sub->reset();
69
70    for (le_int32 run = startRun; run <= endRun; run += 1) {
71        const LEFontInstance *runFont = fontRuns->getFont(run);
72        le_int32 runLimit = fontRuns->getLimit(run) - start;
73
74        if (run == endRun) {
75            runLimit = limit - start;
76        }
77
78        sub->add(runFont, runLimit);
79    }
80}
81
82Paragraph::Paragraph(const LEUnicode chars[], int32_t charCount, const FontRuns *fontRuns, LEErrorCode &status)
83  : fParagraphLayout(NULL), fParagraphCount(0), fParagraphMax(PARA_GROW), fParagraphGrow(PARA_GROW),
84    fLineCount(0), fLinesMax(LINE_GROW), fLinesGrow(LINE_GROW), fLines(NULL), fChars(NULL),
85    fLineHeight(-1), fAscent(-1), fWidth(-1), fHeight(-1), fParagraphLevel(UBIDI_DEFAULT_LTR)
86{
87    static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000};
88
89	if (LE_FAILURE(status)) {
90		return;
91	}
92
93    le_int32 ascent  = 0;
94    le_int32 descent = 0;
95    le_int32 leading = 0;
96
97	LocaleRuns *locales = NULL;
98    FontRuns fr(0);
99
100    fLines = LE_NEW_ARRAY(const ParagraphLayout::Line *, fLinesMax);
101    fParagraphLayout = LE_NEW_ARRAY(ParagraphLayout *, fParagraphMax);
102
103    fChars = LE_NEW_ARRAY(LEUnicode, charCount + 1);
104    LE_ARRAY_COPY(fChars, chars, charCount);
105    fChars[charCount] = 0;
106
107    LEUnicode *pStart = &fChars[0];
108
109    while (*pStart != 0) {
110        LEUnicode *pEnd = u_strpbrk(pStart, separators);
111        le_int32 pAscent, pDescent, pLeading;
112        ParagraphLayout *paragraphLayout = NULL;
113
114        if (pEnd == NULL) {
115            pEnd = &fChars[charCount];
116        }
117
118        if (pEnd != pStart) {
119            subsetFontRuns(fontRuns, pStart - fChars, pEnd - fChars, &fr);
120
121            paragraphLayout = new ParagraphLayout(pStart, pEnd - pStart, &fr, NULL, NULL, locales, fParagraphLevel, FALSE, status);
122
123            if (LE_FAILURE(status)) {
124                delete paragraphLayout;
125                break; // return? something else?
126            }
127
128            if (fParagraphLevel == UBIDI_DEFAULT_LTR) {
129                fParagraphLevel = paragraphLayout->getParagraphLevel();
130            }
131
132            pAscent  = paragraphLayout->getAscent();
133            pDescent = paragraphLayout->getDescent();
134            pLeading = paragraphLayout->getLeading();
135
136            if (pAscent > ascent) {
137                ascent = pAscent;
138            }
139
140            if (pDescent > descent) {
141                descent = pDescent;
142            }
143
144            if (pLeading > leading) {
145                leading = pLeading;
146            }
147        }
148
149        if (fParagraphCount >= fParagraphMax) {
150            fParagraphLayout = (ParagraphLayout **) LE_GROW_ARRAY(fParagraphLayout, fParagraphMax + fParagraphGrow);
151            fParagraphMax += fParagraphGrow;
152        }
153
154        fParagraphLayout[fParagraphCount++] = paragraphLayout;
155
156        if (*pEnd == 0) {
157            break;
158        }
159
160        pStart = skipLineEnd(pEnd);
161    }
162
163    fLineHeight = ascent + descent + leading;
164    fAscent     = ascent;
165}
166
167Paragraph::~Paragraph()
168{
169    for (le_int32 line = 0; line < fLineCount; line += 1) {
170        delete /*(LineInfo *)*/ fLines[line];
171    }
172
173    for (le_int32 paragraph = 0; paragraph < fParagraphCount; paragraph += 1) {
174        delete fParagraphLayout[paragraph];
175    }
176
177    LE_DELETE_ARRAY(fLines);
178    LE_DELETE_ARRAY(fParagraphLayout);
179    LE_DELETE_ARRAY(fChars);
180}
181
182void Paragraph::addLine(const ParagraphLayout::Line *line)
183{
184    if (fLineCount >= fLinesMax) {
185        fLines = (const ParagraphLayout::Line **) LE_GROW_ARRAY(fLines, fLinesMax + fLinesGrow);
186        fLinesMax += fLinesGrow;
187    }
188
189    fLines[fLineCount++] = line;
190}
191
192void Paragraph::breakLines(le_int32 width, le_int32 height)
193{
194    fHeight = height;
195
196    // don't re-break if the width hasn't changed
197    if (fWidth == width) {
198        return;
199    }
200
201    fWidth  = width;
202
203    float lineWidth = (float) (width - 2 * MARGIN);
204    const ParagraphLayout::Line *line;
205
206    // Free the old LineInfo's...
207    for (le_int32 li = 0; li < fLineCount; li += 1) {
208        delete fLines[li];
209    }
210
211    fLineCount = 0;
212
213    for (le_int32 p = 0; p < fParagraphCount; p += 1) {
214        ParagraphLayout *paragraphLayout = fParagraphLayout[p];
215
216        if (paragraphLayout != NULL) {
217            paragraphLayout->reflow();
218            while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
219                addLine(line);
220            }
221        } else {
222            addLine(NULL);
223        }
224    }
225}
226
227void Paragraph::draw(RenderingSurface *surface, le_int32 firstLine, le_int32 lastLine)
228{
229    le_int32 li, x, y;
230
231    x = MARGIN;
232    y = fAscent;
233
234    for (li = firstLine; li <= lastLine; li += 1) {
235        const ParagraphLayout::Line *line = fLines[li];
236
237        if (line != NULL) {
238            le_int32 runCount = line->countRuns();
239            le_int32 run;
240
241		    if (fParagraphLevel == UBIDI_RTL) {
242			    le_int32 lastX = line->getWidth();
243
244			    x = (fWidth - lastX - MARGIN);
245		    }
246
247
248            for (run = 0; run < runCount; run += 1) {
249                const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
250                le_int32 glyphCount = visualRun->getGlyphCount();
251                const LEFontInstance *font = visualRun->getFont();
252                const LEGlyphID *glyphs = visualRun->getGlyphs();
253                const float *positions = visualRun->getPositions();
254
255                surface->drawGlyphs(font, glyphs, glyphCount, positions, x, y, fWidth, fHeight);
256            }
257        }
258
259        y += fLineHeight;
260    }
261}
262
263Paragraph *Paragraph::paragraphFactory(const char *fileName, const LEFontInstance *font, GUISupport *guiSupport)
264{
265    LEErrorCode status  = LE_NO_ERROR;
266    le_int32 charCount;
267    const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount);
268    Paragraph *result = NULL;
269
270    if (text == NULL) {
271        return NULL;
272    }
273
274    FontRuns  fontRuns(0);
275
276    fontRuns.add(font, charCount);
277
278    result = new Paragraph(text, charCount, &fontRuns, status);
279
280	if (LE_FAILURE(status)) {
281		delete result;
282		result = NULL;
283	}
284
285    LE_DELETE_ARRAY(text);
286
287    return result;
288}
289
290