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