Rendering a paragraph with the ICU BiDi API
This is (hypothetical) sample code that illustrates how the ICU BiDi API could be used to render a paragraph of text. Rendering code depends highly on the graphics system, therefore this sample code must make a lot of assumptions, which may or may not match any existing graphics system's properties.The basic assumptions are:
- Rendering is done from left to right on a horizontal line.
- A run of single-style, unidirectional text can be rendered at once.
- Such a run of text is passed to the graphics system with characters (code units) in logical order.
- The line-breaking algorithm is very complicated and Locale-dependent - and therefore its implementation omitted from this sample code.
#include "ubidi.h" typedef enum { styleNormal=0, styleSelected=1, styleBold=2, styleItalics=4, styleSuper=8, styleSub=16 } Style; typedef struct { UTextOffset limit; Style style; } StyleRun; int getTextWidth(const UChar *text, UTextOffset start, UTextOffset limit, const StyleRun *styleRuns, int styleRunCount); // set *pLimit and *pStyleRunLimit for a line // from text[start] and from styleRuns[styleRunStart] // using ubidi_getLogicalRun(para, ...) void getLineBreak(const UChar *text, UTextOffset start, UTextOffset *pLimit, UBiDi *para, const StyleRun *styleRuns, int styleRunStart, int *pStyleRunLimit, int *pLineWidth); // render runs on a line sequentially, always from left to right // prepare rendering a new line void startLine(UBiDiDirection textDirection, int lineWidth); // render a run of text and advance to the right by the run width // the text[start..limit-1] is always in logical order void renderRun(const UChar *text, UTextOffset start, UTextOffset limit, UBiDiDirection textDirection, Style style); // We could compute a cross-product // from the style runs with the directional runs // and then reorder it. // Instead, here we iterate over each run type // and render the intersections - // with shortcuts in simple (and common) cases. // renderParagraph() is the main function. // render a directional run with // (possibly) multiple style runs intersecting with it void renderDirectionalRun(const UChar *text, UTextOffset start, UTextOffset limit, UBiDiDirection direction, const StyleRun *styleRuns, int styleRunCount) { int i; // iterate over style runs if(direction==UBIDI_LTR) { int styleLimit; for(i=0; i<styleRunCount; ++i) { styleLimit=styleRun[i].limit; if(start<styleLimit) { if(styleLimit>limit) { styleLimit=limit; } renderRun(text, start, styleLimit, direction, styleRun[i].style); if(styleLimit==limit) { break; } start=styleLimit; } } } else { int styleStart; for(i=styleRunCount-1; i>=0; --i) { if(i>0) { styleStart=styleRun[i-1].limit; } else { styleStart=0; } if(limit>=styleStart) { if(styleStart<start) { styleStart=start; } renderRun(text, styleStart, limit, direction, styleRun[i].style); if(styleStart==start) { break; } limit=styleStart; } } } } // the line object represents text[start..limit-1] void renderLine(UBiDi *line, const UChar *text, UTextOffset start, UTextOffset limit, const StyleRun *styleRuns, int styleRunCount) { UBiDiDirection direction=ubidi_getDirection(line); if(direction!=UBIDI_MIXED) { // unidirectional if(styleRunCount<=1) { renderRun(text, start, limit, direction, styleRuns[0].style); } else { renderDirectionalRun(text, start, limit, direction, styleRuns, styleRunCount); } } else { // mixed-directional UTextOffset count, i, length; UBiDiLevel level; count=ubidi_countRuns(para, pErrorCode); if(U_SUCCESS(*pErrorCode)) { if(styleRunCount<=1) { Style style=styleRuns[0].style; // iterate over directional runs for(i=0; i<count; ++i) { direction=ubidi_getVisualRun(para, i, &start, &length); renderRun(text, start, start+length, direction, style); } } else { UTextOffset j; // iterate over both directional and style runs for(i=0; i<count; ++i) { direction=ubidi_getVisualRun(line, i, &start, &length); renderDirectionalRun(text, start, start+length, direction, styleRuns, styleRunCount); } } } } } void renderParagraph(const UChar *text, UTextOffset length, UBiDiDirection textDirection, const StyleRun *styleRuns, int styleRunCount, int lineWidth, UErrorCode *pErrorCode) { UBiDi *para; if(pErrorCode==NULL || U_FAILURE(*pErrorCode) || length<=0) { return; } para=ubidi_openSized(length, 0, pErrorCode); if(para==NULL) { return; } ubidi_setPara(para, text, length, textDirection ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, pErrorCode); if(U_SUCCESS(*pErrorCode)) { UBiDiLevel paraLevel=1&ubidi_getParaLevel(para); StyleRun styleRun={ length, styleNormal }; int width; if(styleRuns==NULL || styleRunCount<=0) { styleRunCount=1; styleRuns=&styleRun; } // assume styleRuns[styleRunCount-1].limit>=length width=getTextWidth(text, 0, length, styleRuns, styleRunCount); if(width<=lineWidth) { // everything fits onto one line // prepare rendering a new line from either left or right startLine(paraLevel, width); renderLine(para, text, 0, length, styleRuns, styleRunCount); } else { UBiDi *line; // we need to render several lines line=ubidi_openSized(length, 0, pErrorCode); if(line!=NULL) { UTextOffset start=0, limit; int styleRunStart=0, styleRunLimit; for(;;) { limit=length; styleRunLimit=styleRunCount; getLineBreak(text, start, &limit, para, styleRuns, styleRunStart, &styleRunLimit, &width); ubidi_setLine(para, start, limit, line, pErrorCode); if(U_SUCCESS(*pErrorCode)) { // prepare rendering a new line // from either left or right startLine(paraLevel, width); renderLine(line, text, start, limit, styleRuns+styleRunStart, styleRunLimit-styleRunStart); } if(limit==length) { break; } start=limit; styleRunStart=styleRunLimit-1; if(start>=styleRuns[styleRunStart].limit) { ++styleRunStart; } } ubidi_close(line); } } } ubidi_close(para); }
alphabetic index hierarchy of classes
this page has been generated automatically by doc++
(c)opyright by Malte Zöckler, Roland Wunderling
contact: doc++@zib.de