1 /*
2 *
3 * © 2016 and later: Unicode, Inc. and others.
4 * License & terms of use: http://www.unicode.org/copyright.html
5 *
6 * (C) Copyright IBM Corp. 1998-2007 - All Rights Reserved
7 *
8 */
9
10 #include "unicode/utypes.h"
11 #include "unicode/uchar.h"
12 #include "unicode/ubidi.h"
13 #include "unicode/ustring.h"
14
15 #include "layout/LETypes.h"
16
17 #include "layout/loengine.h"
18 #include "layout/playout.h"
19 #include "layout/plruns.h"
20
21 #include "pflow.h"
22
23 #include "arraymem.h"
24 #include "ucreader.h"
25
26 /*
27 * Move the line below out of this comment
28 * to add a locale run to the pl_paragraphs
29 * that are created.
30 #define TEST_LOCALE "zh_TW"
31 */
32
33 #define MARGIN 10
34 #define LINE_GROW 32
35 #define PARA_GROW 8
36
37 #define CH_LF 0x000A
38 #define CH_CR 0x000D
39 #define CH_LSEP 0x2028
40 #define CH_PSEP 0x2029
41
42 struct pf_object
43 {
44 pl_paragraph **fParagraphLayout;
45
46 le_int32 fParagraphCount;
47 le_int32 fParagraphMax;
48 le_int32 fParagraphGrow;
49
50 le_int32 fLineCount;
51 le_int32 fLinesMax;
52 le_int32 fLinesGrow;
53
54 pl_line **fLines;
55
56 LEUnicode *fChars;
57
58 le_int32 fLineHeight;
59 le_int32 fAscent;
60 le_int32 fWidth;
61 le_int32 fHeight;
62 UBiDiLevel fParagraphLevel;
63 };
64
65 typedef struct pf_object pf_object;
66
67
skipLineEnd(LEUnicode * ptr)68 static LEUnicode *skipLineEnd(LEUnicode *ptr)
69 {
70 if (ptr[0] == CH_CR && ptr[1] == CH_LF) {
71 ptr += 1;
72 }
73
74 return ptr + 1;
75 }
76
findFontRun(const pl_fontRuns * fontRuns,le_int32 offset)77 static le_int32 findFontRun(const pl_fontRuns *fontRuns, le_int32 offset)
78 {
79 le_int32 runCount = pl_getFontRunCount(fontRuns);
80 le_int32 run;
81
82 for (run = 0; run < runCount; run += 1) {
83 if (pl_getFontRunLimit(fontRuns, run) > offset) {
84 return run;
85 }
86 }
87
88 return -1;
89 }
90
subsetFontRuns(const pl_fontRuns * fontRuns,le_int32 start,le_int32 limit,pl_fontRuns * sub)91 static void subsetFontRuns(const pl_fontRuns *fontRuns, le_int32 start, le_int32 limit, pl_fontRuns *sub)
92 {
93 le_int32 startRun = findFontRun(fontRuns, start);
94 le_int32 endRun = findFontRun(fontRuns, limit - 1);
95 le_int32 run;
96
97 pl_resetFontRuns(sub);
98
99 for (run = startRun; run <= endRun; run += 1) {
100 const le_font *runFont = pl_getFontRunFont(fontRuns, run);
101 le_int32 runLimit = pl_getFontRunLimit(fontRuns, run) - start;
102
103 if (run == endRun) {
104 runLimit = limit - start;
105 }
106
107 pl_addFontRun(sub, runFont, runLimit);
108 }
109 }
110
pf_create(const LEUnicode chars[],le_int32 charCount,const pl_fontRuns * fontRuns,LEErrorCode * status)111 pf_flow *pf_create(const LEUnicode chars[], le_int32 charCount, const pl_fontRuns *fontRuns, LEErrorCode *status)
112 {
113 pf_object *flow;
114 le_int32 ascent = 0;
115 le_int32 descent = 0;
116 le_int32 leading = 0;
117 pl_localeRuns *locales = NULL;
118 pl_fontRuns *fr;
119 LEUnicode *pStart;
120 static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000};
121
122 if (LE_FAILURE(*status)) {
123 return NULL;
124 }
125
126 flow = NEW_ARRAY(pf_object, 1);
127
128 flow->fParagraphLayout = NULL;
129 flow->fParagraphCount = 0;
130 flow->fParagraphMax = PARA_GROW;
131 flow->fParagraphGrow = PARA_GROW;
132 flow->fLineCount = 0;
133 flow->fLinesMax = LINE_GROW;
134 flow->fLinesGrow = LINE_GROW;
135 flow->fLines = NULL;
136 flow->fChars = NULL;
137 flow->fLineHeight = -1;
138 flow->fAscent = -1;
139 flow->fWidth = -1;
140 flow->fHeight = -1;
141 flow->fParagraphLevel = UBIDI_DEFAULT_LTR;
142
143 fr = pl_openEmptyFontRuns(0);
144
145 #ifdef TEST_LOCALE
146 locales = pl_openEmptyLocaleRuns(0);
147 #endif
148
149 flow->fLines = NEW_ARRAY(pl_line *, flow->fLinesMax);
150 flow->fParagraphLayout = NEW_ARRAY(pl_paragraph *, flow->fParagraphMax);
151
152 flow->fChars = NEW_ARRAY(LEUnicode, charCount + 1);
153 LE_ARRAY_COPY(flow->fChars, chars, charCount);
154 flow->fChars[charCount] = 0;
155
156 pStart = &flow->fChars[0];
157
158 while (*pStart != 0) {
159 LEUnicode *pEnd = u_strpbrk(pStart, separators);
160 le_int32 pAscent, pDescent, pLeading;
161 pl_paragraph *paragraphLayout = NULL;
162
163 if (pEnd == NULL) {
164 pEnd = &flow->fChars[charCount];
165 }
166
167 if (pEnd != pStart) {
168 subsetFontRuns(fontRuns, pStart - flow->fChars, pEnd - flow->fChars, fr);
169
170 #ifdef TEST_LOCALE
171 pl_resetLocaleRuns(locales);
172 pl_addLocaleRun(locales, TEST_LOCALE, pEnd - pStart);
173 #endif
174
175 paragraphLayout = pl_create(pStart, pEnd - pStart, fr, NULL, NULL, locales, flow->fParagraphLevel, FALSE, status);
176
177 if (LE_FAILURE(*status)) {
178 break; /* return? something else? */
179 }
180
181 if (flow->fParagraphLevel == UBIDI_DEFAULT_LTR) {
182 flow->fParagraphLevel = pl_getParagraphLevel(paragraphLayout);
183 }
184
185 pAscent = pl_getAscent(paragraphLayout);
186 pDescent = pl_getDescent(paragraphLayout);
187 pLeading = pl_getLeading(paragraphLayout);
188
189 if (pAscent > ascent) {
190 ascent = pAscent;
191 }
192
193 if (pDescent > descent) {
194 descent = pDescent;
195 }
196
197 if (pLeading > leading) {
198 leading = pLeading;
199 }
200 }
201
202 if (flow->fParagraphCount >= flow->fParagraphMax) {
203 flow->fParagraphLayout = (pl_paragraph **) GROW_ARRAY(flow->fParagraphLayout, flow->fParagraphMax + flow->fParagraphGrow);
204 flow->fParagraphMax += flow->fParagraphGrow;
205 }
206
207 flow->fParagraphLayout[flow->fParagraphCount++] = paragraphLayout;
208
209 if (*pEnd == 0) {
210 break;
211 }
212
213 pStart = skipLineEnd(pEnd);
214 }
215
216 flow->fLineHeight = ascent + descent + leading;
217 flow->fAscent = ascent;
218
219 pl_closeLocaleRuns(locales);
220 pl_closeFontRuns(fr);
221
222 return (pf_flow *) flow;
223 }
224
pf_close(pf_flow * flow)225 void pf_close(pf_flow *flow)
226 {
227 pf_object *obj = (pf_object *) flow;
228 le_int32 i;
229
230 for (i = 0; i < obj->fLineCount; i += 1) {
231 DELETE_ARRAY(obj->fLines[i]);
232 }
233
234 DELETE_ARRAY(obj->fLines);
235
236 for (i = 0; i < obj->fParagraphCount; i += 1) {
237 pl_close(obj->fParagraphLayout[i]);
238 }
239
240 DELETE_ARRAY(obj->fParagraphLayout);
241
242 DELETE_ARRAY(obj->fChars);
243
244 DELETE_ARRAY(obj);
245 }
246
247
pf_getAscent(pf_flow * flow)248 le_int32 pf_getAscent(pf_flow *flow)
249 {
250 pf_object *obj = (pf_object *) flow;
251
252 return obj->fAscent;
253 }
254
pf_getLineHeight(pf_flow * flow)255 le_int32 pf_getLineHeight(pf_flow *flow)
256 {
257 pf_object *obj = (pf_object *) flow;
258
259 return obj->fLineHeight;
260 }
261
pf_getLineCount(pf_flow * flow)262 le_int32 pf_getLineCount(pf_flow *flow)
263 {
264 pf_object *obj = (pf_object *) flow;
265
266 return obj->fLineCount;
267 }
268
addLine(pf_object * obj,pl_line * line)269 static void addLine(pf_object *obj, pl_line *line)
270 {
271 if (obj->fLineCount >= obj->fLinesMax) {
272 obj->fLines = (pl_line **) GROW_ARRAY(obj->fLines, obj->fLinesMax + obj->fLinesGrow);
273 obj->fLinesMax += obj->fLinesGrow;
274 }
275
276 obj->fLines[obj->fLineCount++] = line;
277 }
278
pf_breakLines(pf_flow * flow,le_int32 width,le_int32 height)279 void pf_breakLines(pf_flow *flow, le_int32 width, le_int32 height)
280 {
281 pf_object *obj = (pf_object *) flow;
282 le_int32 li, p;
283 float lineWidth;
284 pl_line *line;
285
286 obj->fHeight = height;
287
288 /* don't re-break if the width hasn't changed */
289 if (obj->fWidth == width) {
290 return;
291 }
292
293 obj->fWidth = width;
294
295 lineWidth = (float) (width - 2 * MARGIN);
296
297 /* Free the old Lines... */
298 for (li = 0; li < obj->fLineCount; li += 1) {
299 pl_closeLine(obj->fLines[li]);
300 }
301
302 obj->fLineCount = 0;
303
304 for (p = 0; p < obj->fParagraphCount; p += 1) {
305 pl_paragraph *paragraphLayout = obj->fParagraphLayout[p];
306
307 if (paragraphLayout != NULL) {
308 pl_reflow(paragraphLayout);
309 while ((line = pl_nextLine(paragraphLayout, lineWidth)) != NULL) {
310 addLine(obj, line);
311 }
312 } else {
313 addLine(obj, NULL);
314 }
315 }
316 }
317
pf_draw(pf_flow * flow,rs_surface * surface,le_int32 firstLine,le_int32 lastLine)318 void pf_draw(pf_flow *flow, rs_surface *surface, le_int32 firstLine, le_int32 lastLine)
319 {
320 pf_object *obj = (pf_object *) flow;
321 le_int32 li, x, y;
322
323 x = MARGIN;
324 y = obj->fAscent;
325
326 for (li = firstLine; li <= lastLine; li += 1) {
327 const pl_line *line = obj->fLines[li];
328
329 if (line != NULL) {
330 le_int32 runCount = pl_countLineRuns(line);
331 le_int32 run;
332
333 if (obj->fParagraphLevel == UBIDI_RTL) {
334 le_int32 lastX = pl_getLineWidth(line);
335
336 x = (obj->fWidth - lastX - MARGIN);
337 }
338
339
340 for (run = 0; run < runCount; run += 1) {
341 const pl_visualRun *visualRun = pl_getLineVisualRun(line, run);
342 le_int32 glyphCount = pl_getVisualRunGlyphCount(visualRun);
343 const le_font *font = pl_getVisualRunFont(visualRun);
344 const LEGlyphID *glyphs = pl_getVisualRunGlyphs(visualRun);
345 const float *positions = pl_getVisualRunPositions(visualRun);
346
347 rs_drawGlyphs(surface, font, glyphs, glyphCount, positions, x, y, obj->fWidth, obj->fHeight);
348 }
349 }
350
351 y += obj->fLineHeight;
352 }
353 }
354
pf_factory(const char * fileName,const le_font * font,gs_guiSupport * guiSupport)355 pf_flow *pf_factory(const char *fileName, const le_font *font, gs_guiSupport *guiSupport)
356 {
357 LEErrorCode status = LE_NO_ERROR;
358 le_int32 charCount;
359 const UChar *text = uc_readFile(fileName, guiSupport, &charCount);
360 pl_fontRuns *fontRuns;
361 pf_flow *result = NULL;
362
363 if (text == NULL) {
364 return NULL;
365 }
366
367 fontRuns = pl_openEmptyFontRuns(0);
368
369 pl_addFontRun(fontRuns, font, charCount);
370
371 result = pf_create(text, charCount, fontRuns, &status);
372
373 if (LE_FAILURE(status)) {
374 pf_close(result);
375 result = NULL;
376 }
377
378 pl_closeFontRuns(fontRuns);
379
380 DELETE_ARRAY(text);
381
382 return result;
383 }
384
385