Imported Upstream version 58.1
[platform/upstream/icu.git] / source / samples / layout / paragraph.cpp
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
41 static 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
50 static 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
63 static 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
82 Paragraph::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
167 Paragraph::~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
182 void 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
192 void 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
227 void 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
263 Paragraph *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