/* * * (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; }