Sample code for the ICU BiDi API

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:

  #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