1 /*
2  *******************************************************************************
3  *
4  *   © 2016 and later: Unicode, Inc. and others.
5  *   License & terms of use: http://www.unicode.org/copyright.html
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 
skipLineEnd(LEUnicode * ptr)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 
findRun(const RunArray * runArray,le_int32 offset)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 
subsetFontRuns(const FontRuns * fontRuns,le_int32 start,le_int32 limit,FontRuns * sub)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 
Paragraph(const LEUnicode chars[],int32_t charCount,const FontRuns * fontRuns,LEErrorCode & status)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 
~Paragraph()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 
addLine(const ParagraphLayout::Line * line)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 
breakLines(le_int32 width,le_int32 height)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 
draw(RenderingSurface * surface,le_int32 firstLine,le_int32 lastLine)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 
paragraphFactory(const char * fileName,const LEFontInstance * font,GUISupport * guiSupport)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 
290