pflow.c revision c73f511526464f8e56c242df80552e9b0d94ae3d
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 39struct 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 62typedef struct pf_object pf_object; 63 64 65static 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 74static 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 88static 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 108pf_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 222void 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 245le_int32 pf_getAscent(pf_flow *flow) 246{ 247 pf_object *obj = (pf_object *) flow; 248 249 return obj->fAscent; 250} 251 252le_int32 pf_getLineHeight(pf_flow *flow) 253{ 254 pf_object *obj = (pf_object *) flow; 255 256 return obj->fLineHeight; 257} 258 259le_int32 pf_getLineCount(pf_flow *flow) 260{ 261 pf_object *obj = (pf_object *) flow; 262 263 return obj->fLineCount; 264} 265 266static 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 276void 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 315void 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 352pf_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