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