| /* |
| * |
| * © 2016 and later: Unicode, Inc. and others. |
| * License & terms of use: http://www.unicode.org/copyright.html |
| * |
| * (C) Copyright IBM Corp. 1998-2007 - All Rights Reserved |
| * |
| */ |
| |
| #include "unicode/utypes.h" |
| #include "unicode/uchar.h" |
| #include "unicode/ubidi.h" |
| #include "unicode/ustring.h" |
| |
| #include "layout/LETypes.h" |
| |
| #include "layout/loengine.h" |
| #include "layout/playout.h" |
| #include "layout/plruns.h" |
| |
| #include "pflow.h" |
| |
| #include "arraymem.h" |
| #include "ucreader.h" |
| |
| /* |
| * Move the line below out of this comment |
| * to add a locale run to the pl_paragraphs |
| * that are created. |
| #define TEST_LOCALE "zh_TW" |
| */ |
| |
| #define MARGIN 10 |
| #define LINE_GROW 32 |
| #define PARA_GROW 8 |
| |
| #define CH_LF 0x000A |
| #define CH_CR 0x000D |
| #define CH_LSEP 0x2028 |
| #define CH_PSEP 0x2029 |
| |
| struct pf_object |
| { |
| pl_paragraph **fParagraphLayout; |
| |
| le_int32 fParagraphCount; |
| le_int32 fParagraphMax; |
| le_int32 fParagraphGrow; |
| |
| le_int32 fLineCount; |
| le_int32 fLinesMax; |
| le_int32 fLinesGrow; |
| |
| pl_line **fLines; |
| |
| LEUnicode *fChars; |
| |
| le_int32 fLineHeight; |
| le_int32 fAscent; |
| le_int32 fWidth; |
| le_int32 fHeight; |
| UBiDiLevel fParagraphLevel; |
| }; |
| |
| typedef struct pf_object pf_object; |
| |
| |
| static LEUnicode *skipLineEnd(LEUnicode *ptr) |
| { |
| if (ptr[0] == CH_CR && ptr[1] == CH_LF) { |
| ptr += 1; |
| } |
| |
| return ptr + 1; |
| } |
| |
| static le_int32 findFontRun(const pl_fontRuns *fontRuns, le_int32 offset) |
| { |
| le_int32 runCount = pl_getFontRunCount(fontRuns); |
| le_int32 run; |
| |
| for (run = 0; run < runCount; run += 1) { |
| if (pl_getFontRunLimit(fontRuns, run) > offset) { |
| return run; |
| } |
| } |
| |
| return -1; |
| } |
| |
| static void subsetFontRuns(const pl_fontRuns *fontRuns, le_int32 start, le_int32 limit, pl_fontRuns *sub) |
| { |
| le_int32 startRun = findFontRun(fontRuns, start); |
| le_int32 endRun = findFontRun(fontRuns, limit - 1); |
| le_int32 run; |
| |
| pl_resetFontRuns(sub); |
| |
| for (run = startRun; run <= endRun; run += 1) { |
| const le_font *runFont = pl_getFontRunFont(fontRuns, run); |
| le_int32 runLimit = pl_getFontRunLimit(fontRuns, run) - start; |
| |
| if (run == endRun) { |
| runLimit = limit - start; |
| } |
| |
| pl_addFontRun(sub, runFont, runLimit); |
| } |
| } |
| |
| pf_flow *pf_create(const LEUnicode chars[], le_int32 charCount, const pl_fontRuns *fontRuns, LEErrorCode *status) |
| { |
| pf_object *flow; |
| le_int32 ascent = 0; |
| le_int32 descent = 0; |
| le_int32 leading = 0; |
| pl_localeRuns *locales = NULL; |
| pl_fontRuns *fr; |
| LEUnicode *pStart; |
| static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000}; |
| |
| if (LE_FAILURE(*status)) { |
| return NULL; |
| } |
| |
| flow = NEW_ARRAY(pf_object, 1); |
| |
| flow->fParagraphLayout = NULL; |
| flow->fParagraphCount = 0; |
| flow->fParagraphMax = PARA_GROW; |
| flow->fParagraphGrow = PARA_GROW; |
| flow->fLineCount = 0; |
| flow->fLinesMax = LINE_GROW; |
| flow->fLinesGrow = LINE_GROW; |
| flow->fLines = NULL; |
| flow->fChars = NULL; |
| flow->fLineHeight = -1; |
| flow->fAscent = -1; |
| flow->fWidth = -1; |
| flow->fHeight = -1; |
| flow->fParagraphLevel = UBIDI_DEFAULT_LTR; |
| |
| fr = pl_openEmptyFontRuns(0); |
| |
| #ifdef TEST_LOCALE |
| locales = pl_openEmptyLocaleRuns(0); |
| #endif |
| |
| flow->fLines = NEW_ARRAY(pl_line *, flow->fLinesMax); |
| flow->fParagraphLayout = NEW_ARRAY(pl_paragraph *, flow->fParagraphMax); |
| |
| flow->fChars = NEW_ARRAY(LEUnicode, charCount + 1); |
| LE_ARRAY_COPY(flow->fChars, chars, charCount); |
| flow->fChars[charCount] = 0; |
| |
| pStart = &flow->fChars[0]; |
| |
| while (*pStart != 0) { |
| LEUnicode *pEnd = u_strpbrk(pStart, separators); |
| le_int32 pAscent, pDescent, pLeading; |
| pl_paragraph *paragraphLayout = NULL; |
| |
| if (pEnd == NULL) { |
| pEnd = &flow->fChars[charCount]; |
| } |
| |
| if (pEnd != pStart) { |
| subsetFontRuns(fontRuns, pStart - flow->fChars, pEnd - flow->fChars, fr); |
| |
| #ifdef TEST_LOCALE |
| pl_resetLocaleRuns(locales); |
| pl_addLocaleRun(locales, TEST_LOCALE, pEnd - pStart); |
| #endif |
| |
| paragraphLayout = pl_create(pStart, pEnd - pStart, fr, NULL, NULL, locales, flow->fParagraphLevel, FALSE, status); |
| |
| if (LE_FAILURE(*status)) { |
| break; /* return? something else? */ |
| } |
| |
| if (flow->fParagraphLevel == UBIDI_DEFAULT_LTR) { |
| flow->fParagraphLevel = pl_getParagraphLevel(paragraphLayout); |
| } |
| |
| pAscent = pl_getAscent(paragraphLayout); |
| pDescent = pl_getDescent(paragraphLayout); |
| pLeading = pl_getLeading(paragraphLayout); |
| |
| if (pAscent > ascent) { |
| ascent = pAscent; |
| } |
| |
| if (pDescent > descent) { |
| descent = pDescent; |
| } |
| |
| if (pLeading > leading) { |
| leading = pLeading; |
| } |
| } |
| |
| if (flow->fParagraphCount >= flow->fParagraphMax) { |
| flow->fParagraphLayout = (pl_paragraph **) GROW_ARRAY(flow->fParagraphLayout, flow->fParagraphMax + flow->fParagraphGrow); |
| flow->fParagraphMax += flow->fParagraphGrow; |
| } |
| |
| flow->fParagraphLayout[flow->fParagraphCount++] = paragraphLayout; |
| |
| if (*pEnd == 0) { |
| break; |
| } |
| |
| pStart = skipLineEnd(pEnd); |
| } |
| |
| flow->fLineHeight = ascent + descent + leading; |
| flow->fAscent = ascent; |
| |
| pl_closeLocaleRuns(locales); |
| pl_closeFontRuns(fr); |
| |
| return (pf_flow *) flow; |
| } |
| |
| void pf_close(pf_flow *flow) |
| { |
| pf_object *obj = (pf_object *) flow; |
| le_int32 i; |
| |
| for (i = 0; i < obj->fLineCount; i += 1) { |
| DELETE_ARRAY(obj->fLines[i]); |
| } |
| |
| DELETE_ARRAY(obj->fLines); |
| |
| for (i = 0; i < obj->fParagraphCount; i += 1) { |
| pl_close(obj->fParagraphLayout[i]); |
| } |
| |
| DELETE_ARRAY(obj->fParagraphLayout); |
| |
| DELETE_ARRAY(obj->fChars); |
| |
| DELETE_ARRAY(obj); |
| } |
| |
| |
| le_int32 pf_getAscent(pf_flow *flow) |
| { |
| pf_object *obj = (pf_object *) flow; |
| |
| return obj->fAscent; |
| } |
| |
| le_int32 pf_getLineHeight(pf_flow *flow) |
| { |
| pf_object *obj = (pf_object *) flow; |
| |
| return obj->fLineHeight; |
| } |
| |
| le_int32 pf_getLineCount(pf_flow *flow) |
| { |
| pf_object *obj = (pf_object *) flow; |
| |
| return obj->fLineCount; |
| } |
| |
| static void addLine(pf_object *obj, pl_line *line) |
| { |
| if (obj->fLineCount >= obj->fLinesMax) { |
| obj->fLines = (pl_line **) GROW_ARRAY(obj->fLines, obj->fLinesMax + obj->fLinesGrow); |
| obj->fLinesMax += obj->fLinesGrow; |
| } |
| |
| obj->fLines[obj->fLineCount++] = line; |
| } |
| |
| void pf_breakLines(pf_flow *flow, le_int32 width, le_int32 height) |
| { |
| pf_object *obj = (pf_object *) flow; |
| le_int32 li, p; |
| float lineWidth; |
| pl_line *line; |
| |
| obj->fHeight = height; |
| |
| /* don't re-break if the width hasn't changed */ |
| if (obj->fWidth == width) { |
| return; |
| } |
| |
| obj->fWidth = width; |
| |
| lineWidth = (float) (width - 2 * MARGIN); |
| |
| /* Free the old Lines... */ |
| for (li = 0; li < obj->fLineCount; li += 1) { |
| pl_closeLine(obj->fLines[li]); |
| } |
| |
| obj->fLineCount = 0; |
| |
| for (p = 0; p < obj->fParagraphCount; p += 1) { |
| pl_paragraph *paragraphLayout = obj->fParagraphLayout[p]; |
| |
| if (paragraphLayout != NULL) { |
| pl_reflow(paragraphLayout); |
| while ((line = pl_nextLine(paragraphLayout, lineWidth)) != NULL) { |
| addLine(obj, line); |
| } |
| } else { |
| addLine(obj, NULL); |
| } |
| } |
| } |
| |
| void pf_draw(pf_flow *flow, rs_surface *surface, le_int32 firstLine, le_int32 lastLine) |
| { |
| pf_object *obj = (pf_object *) flow; |
| le_int32 li, x, y; |
| |
| x = MARGIN; |
| y = obj->fAscent; |
| |
| for (li = firstLine; li <= lastLine; li += 1) { |
| const pl_line *line = obj->fLines[li]; |
| |
| if (line != NULL) { |
| le_int32 runCount = pl_countLineRuns(line); |
| le_int32 run; |
| |
| if (obj->fParagraphLevel == UBIDI_RTL) { |
| le_int32 lastX = pl_getLineWidth(line); |
| |
| x = (obj->fWidth - lastX - MARGIN); |
| } |
| |
| |
| for (run = 0; run < runCount; run += 1) { |
| const pl_visualRun *visualRun = pl_getLineVisualRun(line, run); |
| le_int32 glyphCount = pl_getVisualRunGlyphCount(visualRun); |
| const le_font *font = pl_getVisualRunFont(visualRun); |
| const LEGlyphID *glyphs = pl_getVisualRunGlyphs(visualRun); |
| const float *positions = pl_getVisualRunPositions(visualRun); |
| |
| rs_drawGlyphs(surface, font, glyphs, glyphCount, positions, x, y, obj->fWidth, obj->fHeight); |
| } |
| } |
| |
| y += obj->fLineHeight; |
| } |
| } |
| |
| pf_flow *pf_factory(const char *fileName, const le_font *font, gs_guiSupport *guiSupport) |
| { |
| LEErrorCode status = LE_NO_ERROR; |
| le_int32 charCount; |
| const UChar *text = uc_readFile(fileName, guiSupport, &charCount); |
| pl_fontRuns *fontRuns; |
| pf_flow *result = NULL; |
| |
| if (text == NULL) { |
| return NULL; |
| } |
| |
| fontRuns = pl_openEmptyFontRuns(0); |
| |
| pl_addFontRun(fontRuns, font, charCount); |
| |
| result = pf_create(text, charCount, fontRuns, &status); |
| |
| if (LE_FAILURE(status)) { |
| pf_close(result); |
| result = NULL; |
| } |
| |
| pl_closeFontRuns(fontRuns); |
| |
| DELETE_ARRAY(text); |
| |
| return result; |
| } |
| |