U_CAPI UBiDiDirection U_EXPORT2 ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex, int32_t *pLogicalStart, int32_t *pLength) { int32_t start; UErrorCode errorCode = U_ZERO_ERROR; RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, errorCode, UBIDI_LTR); ubidi_getRuns(pBiDi, &errorCode); if(U_FAILURE(errorCode)) { return UBIDI_LTR; } RETURN_IF_BAD_RANGE(runIndex, 0, pBiDi->runCount, errorCode, UBIDI_LTR); start=pBiDi->runs[runIndex].logicalStart; if(pLogicalStart!=NULL) { *pLogicalStart=GET_INDEX(start); } if(pLength!=NULL) { if(runIndex>0) { *pLength=pBiDi->runs[runIndex].visualLimit- pBiDi->runs[runIndex-1].visualLimit; } else { *pLength=pBiDi->runs[0].visualLimit; } } return (UBiDiDirection)GET_ODD_BIT(start); }
U_CAPI int32_t U_EXPORT2 ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) { RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); ubidi_getRuns(pBiDi, pErrorCode); if(U_FAILURE(*pErrorCode)) { return -1; } return pBiDi->runCount; }
U_CAPI int32_t U_EXPORT2 ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) { if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return -1; } else if(pBiDi==NULL || (pBiDi->runCount<0 && !ubidi_getRuns(pBiDi))) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return -1; } else { return pBiDi->runCount; } }
U_CAPI int32_t U_EXPORT2 ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) { if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return 0; } else if(pBiDi==NULL) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return 0; } else if(logicalIndex<0 || pBiDi->length<=logicalIndex) { *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; return 0; } else { /* we can do the trivial cases without the runs array */ switch(pBiDi->direction) { case UBIDI_LTR: return logicalIndex; case UBIDI_RTL: return pBiDi->length-logicalIndex-1; default: if(pBiDi->runCount<0 && !ubidi_getRuns(pBiDi)) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return 0; } else { Run *runs=pBiDi->runs; int32_t i, visualStart=0, offset, length; /* linear search for the run, search on the visual runs */ for(i=0;; ++i) { length=runs[i].visualLimit-visualStart; offset=logicalIndex-GET_INDEX(runs[i].logicalStart); if(offset>=0 && offset<length) { if(IS_EVEN_RUN(runs[i].logicalStart)) { /* LTR */ return visualStart+offset; } else { /* RTL */ return visualStart+length-offset-1; } } visualStart+=length; } } } } }
U_CAPI UBiDiDirection U_EXPORT2 ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex, int32_t *pLogicalStart, int32_t *pLength) { if( pBiDi==NULL || runIndex<0 || (pBiDi->runCount==-1 && !ubidi_getRuns(pBiDi)) || runIndex>=pBiDi->runCount ) { return UBIDI_LTR; } else { int32_t start=pBiDi->runs[runIndex].logicalStart; if(pLogicalStart!=NULL) { *pLogicalStart=GET_INDEX(start); } if(pLength!=NULL) { if(runIndex>0) { *pLength=pBiDi->runs[runIndex].visualLimit- pBiDi->runs[runIndex-1].visualLimit; } else { *pLength=pBiDi->runs[0].visualLimit; } } return (UBiDiDirection)GET_ODD_BIT(start); } }
U_CAPI int32_t U_EXPORT2 ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode) { Run *runs; int32_t i, runCount, start; RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); RETURN_IF_BAD_RANGE(visualIndex, 0, pBiDi->resultLength, *pErrorCode, -1); /* we can do the trivial cases without the runs array */ if(pBiDi->insertPoints.size==0 && pBiDi->controlCount==0) { if(pBiDi->direction==UBIDI_LTR) { return visualIndex; } else if(pBiDi->direction==UBIDI_RTL) { return pBiDi->length-visualIndex-1; } } if(!ubidi_getRuns(pBiDi, pErrorCode)) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return -1; } runs=pBiDi->runs; runCount=pBiDi->runCount; if(pBiDi->insertPoints.size>0) { /* handle inserted LRM/RLM */ int32_t markFound=0, insertRemove; int32_t visualStart=0, length; runs=pBiDi->runs; /* subtract number of marks until visual index */ for(i=0; ; i++, visualStart+=length) { length=runs[i].visualLimit-visualStart; insertRemove=runs[i].insertRemove; if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) { if(visualIndex<=(visualStart+markFound)) { return UBIDI_MAP_NOWHERE; } markFound++; } /* is adjusted visual index within this run? */ if(visualIndex<(runs[i].visualLimit+markFound)) { visualIndex-=markFound; break; } if(insertRemove&(LRM_AFTER|RLM_AFTER)) { if(visualIndex==(visualStart+length+markFound)) { return UBIDI_MAP_NOWHERE; } markFound++; } } } else if(pBiDi->controlCount>0) { /* handle removed BiDi control characters */ int32_t controlFound=0, insertRemove, length; int32_t logicalStart, logicalEnd, visualStart=0, j, k; UChar uchar; UBool evenRun; /* add number of controls until visual index */ for(i=0; ; i++, visualStart+=length) { length=runs[i].visualLimit-visualStart; insertRemove=runs[i].insertRemove; /* is adjusted visual index beyond current run? */ if(visualIndex>=(runs[i].visualLimit-controlFound+insertRemove)) { controlFound-=insertRemove; continue; } /* adjusted visual index is within current run */ if(insertRemove==0) { visualIndex+=controlFound; break; } /* count non-control chars until visualIndex */ logicalStart=runs[i].logicalStart; evenRun=IS_EVEN_RUN(logicalStart); REMOVE_ODD_BIT(logicalStart); logicalEnd=logicalStart+length-1; for(j=0; j<length; j++) { k= evenRun ? logicalStart+j : logicalEnd-j; uchar=pBiDi->text[k]; if(IS_BIDI_CONTROL_CHAR(uchar)) { controlFound++; } if((visualIndex+controlFound)==(visualStart+j)) { break; } } visualIndex+=controlFound; break; } } /* handle all cases */ if(runCount<=10) { /* linear search for the run */ for(i=0; visualIndex>=runs[i].visualLimit; ++i) {} } else { /* binary search for the run */ int32_t begin=0, limit=runCount; /* the middle if() is guaranteed to find the run, we don't need a loop limit */ for(;;) { i=(begin+limit)/2; if(visualIndex>=runs[i].visualLimit) { begin=i+1; } else if(i==0 || visualIndex>=runs[i-1].visualLimit) { break; } else { limit=i; } } } start=runs[i].logicalStart; if(IS_EVEN_RUN(start)) { /* LTR */ /* the offset in runs[i] is visualIndex-runs[i-1].visualLimit */ if(i>0) { visualIndex-=runs[i-1].visualLimit; } return start+visualIndex; } else { /* RTL */ return GET_INDEX(start)+runs[i].visualLimit-visualIndex-1; } }
U_CAPI int32_t U_EXPORT2 ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) { int32_t visualIndex=UBIDI_MAP_NOWHERE; RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); RETURN_IF_BAD_RANGE(logicalIndex, 0, pBiDi->length, *pErrorCode, -1); /* we can do the trivial cases without the runs array */ switch(pBiDi->direction) { case UBIDI_LTR: visualIndex=logicalIndex; break; case UBIDI_RTL: visualIndex=pBiDi->length-logicalIndex-1; break; default: if(!ubidi_getRuns(pBiDi, pErrorCode)) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return -1; } else { Run *runs=pBiDi->runs; int32_t i, visualStart=0, offset, length; /* linear search for the run, search on the visual runs */ for(i=0; i<pBiDi->runCount; ++i) { length=runs[i].visualLimit-visualStart; offset=logicalIndex-GET_INDEX(runs[i].logicalStart); if(offset>=0 && offset<length) { if(IS_EVEN_RUN(runs[i].logicalStart)) { /* LTR */ visualIndex=visualStart+offset; } else { /* RTL */ visualIndex=visualStart+length-offset-1; } break; /* exit for loop */ } visualStart+=length; } if(i>=pBiDi->runCount) { return UBIDI_MAP_NOWHERE; } } } if(pBiDi->insertPoints.size>0) { /* add the number of added marks until the calculated visual index */ Run *runs=pBiDi->runs; int32_t i, length, insertRemove; int32_t visualStart=0, markFound=0; for(i=0; ; i++, visualStart+=length) { length=runs[i].visualLimit-visualStart; insertRemove=runs[i].insertRemove; if(insertRemove & (LRM_BEFORE|RLM_BEFORE)) { markFound++; } /* is it the run containing the visual index? */ if(visualIndex<runs[i].visualLimit) { return visualIndex+markFound; } if(insertRemove & (LRM_AFTER|RLM_AFTER)) { markFound++; } } } else if(pBiDi->controlCount>0) { /* subtract the number of controls until the calculated visual index */ Run *runs=pBiDi->runs; int32_t i, j, start, limit, length, insertRemove; int32_t visualStart=0, controlFound=0; UChar uchar=pBiDi->text[logicalIndex]; /* is the logical index pointing to a control ? */ if(IS_BIDI_CONTROL_CHAR(uchar)) { return UBIDI_MAP_NOWHERE; } /* loop on runs */ for(i=0; ; i++, visualStart+=length) { length=runs[i].visualLimit-visualStart; insertRemove=runs[i].insertRemove; /* calculated visual index is beyond this run? */ if(visualIndex>=runs[i].visualLimit) { controlFound-=insertRemove; continue; } /* calculated visual index must be within current run */ if(insertRemove==0) { return visualIndex-controlFound; } if(IS_EVEN_RUN(runs[i].logicalStart)) { /* LTR: check from run start to logical index */ start=runs[i].logicalStart; limit=logicalIndex; } else { /* RTL: check from logical index to run end */ start=logicalIndex+1; limit=GET_INDEX(runs[i].logicalStart)+length; } for(j=start; j<limit; j++) { uchar=pBiDi->text[j]; if(IS_BIDI_CONTROL_CHAR(uchar)) { controlFound++; } } return visualIndex-controlFound; } } return visualIndex; }
U_CAPI int32_t U_EXPORT2 ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode) { if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return 0; } else if(pBiDi==NULL) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return 0; } else if(visualIndex<0 || pBiDi->length<=visualIndex) { *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; return 0; } else { /* we can do the trivial cases without the runs array */ switch(pBiDi->direction) { case UBIDI_LTR: return visualIndex; case UBIDI_RTL: return pBiDi->length-visualIndex-1; default: if(pBiDi->runCount<0 && !ubidi_getRuns(pBiDi)) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return 0; } else { Run *runs=pBiDi->runs; int32_t i, runCount=pBiDi->runCount, start; if(runCount<=10) { /* linear search for the run */ for(i=0; visualIndex>=runs[i].visualLimit; ++i) {} } else { /* binary search for the run */ int32_t begin=0, limit=runCount; /* the middle if() will guaranteed find the run, we don't need a loop limit */ for(;;) { i=(begin+limit)/2; if(visualIndex>=runs[i].visualLimit) { begin=i+1; } else if(i==0 || visualIndex>=runs[i-1].visualLimit) { break; } else { limit=i; } } } start=runs[i].logicalStart; if(IS_EVEN_RUN(start)) { /* LTR */ /* the offset in runs[i] is visualIndex-runs[i-1].visualLimit */ if(i>0) { visualIndex-=runs[i-1].visualLimit; } return GET_INDEX(start)+visualIndex; } else { /* RTL */ return GET_INDEX(start)+runs[i].visualLimit-visualIndex-1; } } } } }
U_CAPI void U_EXPORT2 ubidi_setPara(UBiDi *pBiDi, const UChar *text, int32_t length, UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels, UErrorCode *pErrorCode) { UBiDiDirection direction; /* check the argument values */ if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return; } else if(pBiDi==NULL || text==NULL || ((UBIDI_MAX_EXPLICIT_LEVEL<paraLevel) && !IS_DEFAULT_LEVEL(paraLevel)) || length<-1 ) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return; } if(length==-1) { // length=u_strlen(text); const UChar *p = text - 1; while(*++p); length = p - text; } /* initialize the UBiDi structure */ pBiDi->text=text; pBiDi->length=length; pBiDi->paraLevel=paraLevel; pBiDi->direction=UBIDI_LTR; pBiDi->trailingWSStart=length; /* the levels[] will reflect the WS run */ pBiDi->dirProps=NULL; pBiDi->levels=NULL; pBiDi->runs=NULL; if(length==0) { /* * For an empty paragraph, create a UBiDi object with the paraLevel and * the flags and the direction set but without allocating zero-length arrays. * There is nothing more to do. */ if(IS_DEFAULT_LEVEL(paraLevel)) { pBiDi->paraLevel&=1; } if(paraLevel&1) { pBiDi->flags=DIRPROP_FLAG(R); pBiDi->direction=UBIDI_RTL; } else { pBiDi->flags=DIRPROP_FLAG(L); pBiDi->direction=UBIDI_LTR; } pBiDi->runCount=0; return; } pBiDi->runCount=-1; /* * Get the directional properties, * the flags bit-set, and * determine the partagraph level if necessary. */ if(getDirPropsMemory(pBiDi, length)) { pBiDi->dirProps=pBiDi->dirPropsMemory; getDirProps(pBiDi, text); } else { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } if (getLevelsMemory(pBiDi, length)) { pBiDi->levels=pBiDi->levelsMemory; /* are explicit levels specified? */ if(embeddingLevels==NULL) { /* no: determine explicit levels according to the (Xn) rules */ direction=resolveExplicitLevels(pBiDi); } else { /* set BN for all explicit codes, check that all levels are paraLevel..UBIDI_MAX_EXPLICIT_LEVEL */ icu_memcpy(pBiDi->levels, embeddingLevels, length); direction=checkExplicitLevels(pBiDi, pErrorCode); if(U_FAILURE(*pErrorCode)) { return; } } } else { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } /* * The steps after (X9) in the UBiDi algorithm are performed only if * the paragraph text has mixed directionality! */ pBiDi->direction=direction; switch(direction) { case UBIDI_LTR: /* make sure paraLevel is even */ pBiDi->paraLevel=(UBiDiLevel)((pBiDi->paraLevel+1)&~1); /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ pBiDi->trailingWSStart=0; break; case UBIDI_RTL: /* make sure paraLevel is odd */ pBiDi->paraLevel|=1; /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ pBiDi->trailingWSStart=0; break; default: /* * If there are no external levels specified and there * are no significant explicit level codes in the text, * then we can treat the entire paragraph as one run. * Otherwise, we need to perform the following rules on runs of * the text with the same embedding levels. (X10) * "Significant" explicit level codes are ones that actually * affect non-BN characters. * Examples for "insignificant" ones are empty embeddings * LRE-PDF, LRE-RLE-PDF-PDF, etc. */ if(embeddingLevels==NULL && !(pBiDi->flags&DIRPROP_FLAG_MULTI_RUNS)) { resolveImplicitLevels(pBiDi, 0, length, GET_LR_FROM_LEVEL(pBiDi->paraLevel), GET_LR_FROM_LEVEL(pBiDi->paraLevel)); } else { /* sor, eor: start and end types of same-level-run */ UBiDiLevel *levels=pBiDi->levels; int32_t start, limit=0; UBiDiLevel level, nextLevel; DirProp sor, eor; /* determine the first sor and set eor to it because of the loop body (sor=eor there) */ level=pBiDi->paraLevel; nextLevel=levels[0]; if(level<nextLevel) { eor=GET_LR_FROM_LEVEL(nextLevel); } else { eor=GET_LR_FROM_LEVEL(level); } do { /* determine start and limit of the run (end points just behind the run) */ /* the values for this run's start are the same as for the previous run's end */ sor=eor; start=limit; level=nextLevel; /* search for the limit of this run */ while(++limit<length && levels[limit]==level) {} /* get the correct level of the next run */ if(limit<length) { nextLevel=levels[limit]; } else { nextLevel=pBiDi->paraLevel; } /* determine eor from max(level, nextLevel); sor is last run's eor */ if((level&~UBIDI_LEVEL_OVERRIDE)<(nextLevel&~UBIDI_LEVEL_OVERRIDE)) { eor=GET_LR_FROM_LEVEL(nextLevel); } else { eor=GET_LR_FROM_LEVEL(level); } /* if the run consists of overridden directional types, then there are no implicit types to be resolved */ if(!(level&UBIDI_LEVEL_OVERRIDE)) { resolveImplicitLevels(pBiDi, start, limit, sor, eor); } else { /* remove the UBIDI_LEVEL_OVERRIDE flags */ do { levels[start++]&=~UBIDI_LEVEL_OVERRIDE; } while(start<limit); } } while(limit<length); } /* reset the embedding levels for some non-graphic characters (L1), (X9) */ adjustWSLevels(pBiDi); /* for "inverse BiDi", ubidi_getRuns() modifies the levels of numeric runs following RTL runs */ if(pBiDi->isInverse) { if(!ubidi_getRuns(pBiDi)) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } } break; } }