QList<TextShaper::TextRun> TextShaper::itemizeBiDi() { QList<TextRun> textRuns; UBiDi *obj = ubidi_open(); UErrorCode err = U_ZERO_ERROR; UBiDiLevel parLevel = UBIDI_LTR; ParagraphStyle style = m_story.paragraphStyle(m_firstChar); if (style.direction() == ParagraphStyle::RTL) parLevel = UBIDI_RTL; ubidi_setPara(obj, (const UChar*) m_text.utf16(), m_text.length(), parLevel, NULL, &err); if (U_SUCCESS(err)) { int32_t count = ubidi_countRuns(obj, &err); if (U_SUCCESS(err)) { textRuns.reserve(count); for (int32_t i = 0; i < count; i++) { int32_t start, length; UBiDiDirection dir = ubidi_getVisualRun(obj, i, &start, &length); textRuns.append(TextRun(start, length, dir)); } } } ubidi_close(obj); return textRuns; }
nsresult nsBidi::GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart, int32_t* aLength, nsBidiDirection* aDirection) { *aDirection = nsBidiDirection(ubidi_getVisualRun(mBiDi, aRunIndex, aLogicalStart, aLength)); return NS_OK; }
void CTextRenderer::UpdateTextCache_BiDi(array<CHarfbuzzGlyph>* pGlyphChain, const char* pText) { //Use ICU for bidirectional text //note: bidirectional texts appear for example when a latin username is displayed in a arabic text UErrorCode ICUError = U_ZERO_ERROR; UnicodeString UTF16Text = icu::UnicodeString::fromUTF8(pText); UBiDi* pICUBiDi = ubidi_openSized(UTF16Text.length(), 0, &ICUError); //Perform the BiDi algorithm //TODO: change UBIDI_DEFAULT_LTR by some variable dependend of the user config ubidi_setPara(pICUBiDi, UTF16Text.getBuffer(), UTF16Text.length(), (Localization()->GetWritingDirection() == CLocalization::DIRECTION_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR), 0, &ICUError); if(U_SUCCESS(ICUError)) { UBiDiLevel ICULevel = 1&ubidi_getParaLevel(pICUBiDi); UBiDiDirection Direction = ubidi_getDirection(pICUBiDi); if(Direction != UBIDI_MIXED) { UpdateTextCache_Font(pGlyphChain, UTF16Text.getBuffer(), 0, UTF16Text.length(), (Direction == UBIDI_RTL)); } else { int CharStart = 0; UBiDiLevel level; int NumberOfParts = ubidi_countRuns(pICUBiDi, &ICUError); if(U_SUCCESS(ICUError)) { for(int i=0; i<NumberOfParts; i++) { int Start; int SubLength; Direction = ubidi_getVisualRun(pICUBiDi, i, &Start, &SubLength); UpdateTextCache_Font(pGlyphChain, UTF16Text.getBuffer(), Start, SubLength, (Direction == UBIDI_RTL)); } } else { dbg_msg("TextRenderer", "BiDi algorithm failed (ubidi_countRuns): %s", u_errorName(ICUError)); return; } } } else { dbg_msg("TextRenderer", "BiDi algorithm failed: %s", u_errorName(ICUError)); return; } }
void text_itemizer::itemize_direction(unsigned start, unsigned end) { direction_runs_.clear(); UErrorCode error = U_ZERO_ERROR; int32_t length = end - start; UBiDi *bidi = ubidi_openSized(length, 0, &error); if (!bidi || U_FAILURE(error)) { MAPNIK_LOG_ERROR(text_itemizer) << "Failed to create bidi object: " << u_errorName(error) << "\n"; return; } ubidi_setPara(bidi, text_.getBuffer() + start, length, UBIDI_DEFAULT_LTR, 0, &error); if (U_SUCCESS(error)) { UBiDiDirection direction = ubidi_getDirection(bidi); if (direction != UBIDI_MIXED) { direction_runs_.emplace_back(direction, start, end); } else { // mixed-directional int32_t count = ubidi_countRuns(bidi, &error); if(U_SUCCESS(error)) { for(int i=0; i<count; ++i) { int32_t vis_length; int32_t run_start; direction = ubidi_getVisualRun(bidi, i, &run_start, &vis_length); run_start += start; //Add offset to compensate offset in setPara direction_runs_.emplace_back(direction, run_start, run_start+vis_length); } } } } else { MAPNIK_LOG_ERROR(text_itemizer) << "ICU error: " << u_errorName(error) << "\n"; //TODO: Exception } ubidi_close(bidi); }
TextGroup::TextGroup(const std::string &input, hb_script_t script, const std::string &lang, hb_direction_t overallDirection) :script_(script) ,lang_(lang) ,overallDirection_(overallDirection) { if(hb_script_get_horizontal_direction(script_) == HB_DIRECTION_LTR) { addRun(input, HB_DIRECTION_LTR); } else { auto text = UnicodeString::fromUTF8(input); auto length = text.length(); printf("Hominlinx-->TextGroup::TextGroup str unicodelen[%d] ====text[0x%x]\n",length, text.charAt(0) ); UErrorCode err = U_ZERO_ERROR; UBiDi *bidi = ubidi_openSized(length, 0, &err);//Bidrectional text ubidi_setPara(bidi, text.getBuffer(), length, hbDirectionToUBIDILevel(overallDirection_), 0, &err); auto direction = ubidi_getDirection(bidi); if(direction != UBIDI_MIXED) { addRun(input, uciDirectionToHB(direction)); } else { auto count = ubidi_countRuns(bidi, &err); for(int i=0; i < count; ++i) { int32_t start, length; direction = ubidi_getVisualRun(bidi, i, &start, &length); addRun(text, direction, start, start + length); } } ubidi_close(bidi); } }
U_CAPI int32_t U_EXPORT2 ubidi_writeReordered(UBiDi *pBiDi, UChar *dest, int32_t destSize, uint16_t options, UErrorCode *pErrorCode) { const UChar *text; UChar *saveDest; int32_t length, destCapacity; int32_t run, runCount, logicalStart, runLength; if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return 0; } /* more error checking */ if( pBiDi==NULL || (text=pBiDi->text)==NULL || (length=pBiDi->length)<0 || destSize<0 || (destSize>0 && dest==NULL)) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return 0; } /* do input and output overlap? */ if( dest!=NULL && ((text>=dest && text<dest+destSize) || (dest>=text && dest<text+pBiDi->originalLength))) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return 0; } if(length==0) { /* nothing to do */ return u_terminateUChars(dest, destSize, 0, pErrorCode); } runCount=ubidi_countRuns(pBiDi, pErrorCode); if(U_FAILURE(*pErrorCode)) { return 0; } /* destSize shrinks, later destination length=destCapacity-destSize */ saveDest=dest; destCapacity=destSize; /* * Option "insert marks" implies UBIDI_INSERT_LRM_FOR_NUMERIC if the * reordering mode (checked below) is appropriate. */ if(pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { options|=UBIDI_INSERT_LRM_FOR_NUMERIC; options&=~UBIDI_REMOVE_BIDI_CONTROLS; } /* * Option "remove controls" implies UBIDI_REMOVE_BIDI_CONTROLS * and cancels UBIDI_INSERT_LRM_FOR_NUMERIC. */ if(pBiDi->reorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { options|=UBIDI_REMOVE_BIDI_CONTROLS; options&=~UBIDI_INSERT_LRM_FOR_NUMERIC; } /* * If we do not perform the "inverse BiDi" algorithm, then we * don't need to insert any LRMs, and don't need to test for it. */ if((pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_NUMBERS_AS_L) && (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_LIKE_DIRECT) && (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL) && (pBiDi->reorderingMode != UBIDI_REORDER_RUNS_ONLY)) { options&=~UBIDI_INSERT_LRM_FOR_NUMERIC; } /* * Iterate through all visual runs and copy the run text segments to * the destination, according to the options. * * The tests for where to insert LRMs ignore the fact that there may be * BN codes or non-BMP code points at the beginning and end of a run; * they may insert LRMs unnecessarily but the tests are faster this way * (this would have to be improved for UTF-8). * * Note that the only errors that are set by doWriteXY() are buffer overflow * errors. Ignore them until the end, and continue for preflighting. */ if(!(options&UBIDI_OUTPUT_REVERSE)) { /* forward output */ if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) { /* do not insert BiDi controls */ for(run=0; run<runCount; ++run) { if(UBIDI_LTR==ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength)) { runLength=doWriteForward(text+logicalStart, runLength, dest, destSize, (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); } else { runLength=doWriteReverse(text+logicalStart, runLength, dest, destSize, options, pErrorCode); } dest+=runLength; destSize-=runLength; } } else { /* insert BiDi controls for "inverse BiDi" */ const DirProp *dirProps=pBiDi->dirProps; const UChar *src; UChar uc; UBiDiDirection dir; int32_t markFlag; for(run=0; run<runCount; ++run) { dir=ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength); src=text+logicalStart; /* check if something relevant in insertPoints */ markFlag=pBiDi->runs[run].insertRemove; if(markFlag<0) { /* BiDi controls count */ markFlag=0; } if(UBIDI_LTR==dir) { if((pBiDi->isInverse) && (/*run>0 &&*/ dirProps[logicalStart]!=L)) { markFlag |= LRM_BEFORE; } if (markFlag & LRM_BEFORE) { uc=LRM_CHAR; } else if (markFlag & RLM_BEFORE) { uc=RLM_CHAR; } else uc=0; if(uc) { if(destSize>0) { *dest++=uc; } --destSize; } runLength=doWriteForward(src, runLength, dest, destSize, (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); dest+=runLength; destSize-=runLength; if((pBiDi->isInverse) && (/*run<runCount-1 &&*/ dirProps[logicalStart+runLength-1]!=L)) { markFlag |= LRM_AFTER; } if (markFlag & LRM_AFTER) { uc=LRM_CHAR; } else if (markFlag & RLM_AFTER) { uc=RLM_CHAR; } else uc=0; if(uc) { if(destSize>0) { *dest++=uc; } --destSize; } } else { /* RTL run */ if((pBiDi->isInverse) && (/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1])))) { markFlag |= RLM_BEFORE; } if (markFlag & LRM_BEFORE) { uc=LRM_CHAR; } else if (markFlag & RLM_BEFORE) { uc=RLM_CHAR; } else uc=0; if(uc) { if(destSize>0) { *dest++=uc; } --destSize; } runLength=doWriteReverse(src, runLength, dest, destSize, options, pErrorCode); dest+=runLength; destSize-=runLength; if((pBiDi->isInverse) && (/*run<runCount-1 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart])))) { markFlag |= RLM_AFTER; } if (markFlag & LRM_AFTER) { uc=LRM_CHAR; } else if (markFlag & RLM_AFTER) { uc=RLM_CHAR; } else uc=0; if(uc) { if(destSize>0) { *dest++=uc; } --destSize; } } } } } else { /* reverse output */ if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) { /* do not insert BiDi controls */ for(run=runCount; --run>=0;) { if(UBIDI_LTR==ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength)) { runLength=doWriteReverse(text+logicalStart, runLength, dest, destSize, (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); } else { runLength=doWriteForward(text+logicalStart, runLength, dest, destSize, options, pErrorCode); } dest+=runLength; destSize-=runLength; } } else { /* insert BiDi controls for "inverse BiDi" */ const DirProp *dirProps=pBiDi->dirProps; const UChar *src; UBiDiDirection dir; for(run=runCount; --run>=0;) { /* reverse output */ dir=ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength); src=text+logicalStart; if(UBIDI_LTR==dir) { if(/*run<runCount-1 &&*/ dirProps[logicalStart+runLength-1]!=L) { if(destSize>0) { *dest++=LRM_CHAR; } --destSize; } runLength=doWriteReverse(src, runLength, dest, destSize, (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); dest+=runLength; destSize-=runLength; if(/*run>0 &&*/ dirProps[logicalStart]!=L) { if(destSize>0) { *dest++=LRM_CHAR; } --destSize; } } else { if(/*run<runCount-1 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart]))) { if(destSize>0) { *dest++=RLM_CHAR; } --destSize; } runLength=doWriteForward(src, runLength, dest, destSize, options, pErrorCode); dest+=runLength; destSize-=runLength; if(/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1]))) { if(destSize>0) { *dest++=RLM_CHAR; } --destSize; } } } } } return u_terminateUChars(saveDest, destCapacity, destCapacity-destSize, pErrorCode); }
void font_face_set::get_string_info(string_info & info) { unsigned width = 0; unsigned height = 0; UErrorCode err = U_ZERO_ERROR; UnicodeString const& ustr = info.get_string(); const UChar * text = ustr.getBuffer(); UBiDi * bidi = ubidi_openSized(ustr.length(),0,&err); if (U_SUCCESS(err)) { ubidi_setPara(bidi,text,ustr.length(), UBIDI_DEFAULT_LTR,0,&err); if (U_SUCCESS(err)) { int32_t count = ubidi_countRuns(bidi,&err); int32_t logicalStart; int32_t length; for (int32_t i=0; i< count;++i) { if (UBIDI_LTR == ubidi_getVisualRun(bidi,i,&logicalStart,&length)) { do { UChar ch = text[logicalStart++]; dimension_t char_dim = character_dimensions(ch); info.add_info(ch, char_dim.width, char_dim.height); width += char_dim.width; height = char_dim.height > height ? char_dim.height : height; } while (--length > 0); } else { logicalStart += length; int32_t j=0,i=length; UnicodeString arabic; UChar * buf = arabic.getBuffer(length); do { UChar ch = text[--logicalStart]; buf[j++] = ch; } while (--i > 0); arabic.releaseBuffer(length); if ( *arabic.getBuffer() >= 0x0600 && *arabic.getBuffer() <= 0x06ff) { UnicodeString shaped; u_shapeArabic(arabic.getBuffer(),arabic.length(),shaped.getBuffer(arabic.length()),arabic.length(), U_SHAPE_LETTERS_SHAPE|U_SHAPE_LENGTH_FIXED_SPACES_NEAR| U_SHAPE_TEXT_DIRECTION_VISUAL_LTR ,&err); shaped.releaseBuffer(arabic.length()); if (U_SUCCESS(err)) { for (int j=0;j<shaped.length();++j) { dimension_t char_dim = character_dimensions(shaped[j]); info.add_info(shaped[j], char_dim.width, char_dim.height); width += char_dim.width; height = char_dim.height > height ? char_dim.height : height; } } } else { // Non-Arabic RTL for (int j=0;j<arabic.length();++j) { dimension_t char_dim = character_dimensions(arabic[j]); info.add_info(arabic[j], char_dim.width, char_dim.height); width += char_dim.width; height = char_dim.height > height ? char_dim.height : height; } } } } } ubidi_close(bidi); } info.set_dimensions(width, height); }
int icu_bidi_runs(lua_State *L) { size_t input_l; const char* input = luaL_checklstring(L, 1, &input_l); const char* direction = luaL_checkstring(L, 2); UChar *input_as_uchar; int32_t l; utf8_to_uchar(input, input_l, input_as_uchar, l); UBiDiLevel paraLevel = 0; if (strncasecmp(direction, "RTL", 3) == 0) { paraLevel = 1; } /* Now let's bidi! */ UBiDi* bidi = ubidi_open(); UErrorCode err = U_ZERO_ERROR; ubidi_setPara(bidi, input_as_uchar, l, paraLevel, NULL, &err); if (!U_SUCCESS(err)) { free(input_as_uchar); ubidi_close(bidi); return luaL_error(L, "Error in bidi %s", u_errorName(err)); } int count = ubidi_countRuns(bidi,&err); int start, length; lua_checkstack(L,count); for (int i=0; i < count; i++) { UBiDiDirection dir = ubidi_getVisualRun(bidi, i, &start, &length); lua_newtable(L); // Convert back to UTF8... int32_t l3 = 0; char* possibleOutbuf = malloc(4*length); if(!possibleOutbuf) { return luaL_error(L, "Couldn't malloc"); } u_strToUTF8(possibleOutbuf, 4 * length, &l3, input_as_uchar+start, length, &err); if (!U_SUCCESS(err)) { free(possibleOutbuf); return luaL_error(L, "Bidi run too big? %s", u_errorName(err)); } lua_pushstring(L, "run"); lua_pushstring(L, possibleOutbuf); free(possibleOutbuf); lua_settable(L, -3); lua_pushstring(L, "start"); int32_t new_start = start; // Length/start is given in terms of UTF16 codepoints. // But we want a count of Unicode characters. This means // surrogate pairs need to be counted as 1. for (int j=0; j< start; j++) { if (U_IS_TRAIL(*(input_as_uchar+j))) new_start--; } lua_pushinteger(L, new_start); lua_settable(L, -3); lua_pushstring(L, "length"); for (int j=start; j< start+length; j++) { if (U_IS_TRAIL(*(input_as_uchar+j))) length--; } lua_pushinteger(L, length); lua_settable(L, -3); lua_pushstring(L, "dir"); lua_pushstring(L, dir == UBIDI_RTL ? "RTL" : "LTR"); lua_settable(L, -3); lua_pushstring(L, "level"); lua_pushinteger(L, ubidi_getLevelAt(bidi, start)); lua_settable(L, -3); } free(input_as_uchar); ubidi_close(bidi); return count; }
std::vector<StyledText> BiDi::processStyledText(const StyledText& input, std::set<std::size_t> lineBreakPoints) { std::vector<StyledText> lines; const auto& inputText = input.first; const auto& styleIndices = input.second; UErrorCode errorCode = U_ZERO_ERROR; ubidi_setPara(impl->bidiText, mbgl::utf16char_cast<const UChar*>(inputText.c_str()), static_cast<int32_t>(inputText.size()), UBIDI_DEFAULT_LTR, nullptr, &errorCode); if (U_FAILURE(errorCode)) { throw std::runtime_error(std::string("BiDi::processStyledText: ") + u_errorName(errorCode)); } mergeParagraphLineBreaks(lineBreakPoints); std::size_t lineStartIndex = 0; for (std::size_t lineBreakPoint : lineBreakPoints) { StyledText line; line.second.reserve(lineBreakPoint - lineStartIndex); errorCode = U_ZERO_ERROR; ubidi_setLine(impl->bidiText, static_cast<int32_t>(lineStartIndex), static_cast<int32_t>(lineBreakPoint), impl->bidiLine, &errorCode); if (U_FAILURE(errorCode)) { throw std::runtime_error(std::string("BiDi::processStyledText (setLine): ") + u_errorName(errorCode)); } errorCode = U_ZERO_ERROR; uint32_t runCount = ubidi_countRuns(impl->bidiLine, &errorCode); if (U_FAILURE(errorCode)) { throw std::runtime_error(std::string("BiDi::processStyledText (countRuns): ") + u_errorName(errorCode)); } for (uint32_t runIndex = 0; runIndex < runCount; runIndex++) { int32_t runLogicalStart; int32_t runLength; UBiDiDirection direction = ubidi_getVisualRun(impl->bidiLine, runIndex, &runLogicalStart, &runLength); const bool isReversed = direction == UBIDI_RTL; std::size_t logicalStart = lineStartIndex + runLogicalStart; std::size_t logicalEnd = logicalStart + runLength; if (isReversed) { // Within this reversed section, iterate logically backwards // Each time we see a change in style, render a reversed chunk // of everything since the last change std::size_t styleRunStart = logicalEnd; uint8_t currentStyleIndex = styleIndices.at(styleRunStart - 1); for (std::size_t i = logicalEnd - 1; i >= logicalStart; i--) { if (currentStyleIndex != styleIndices.at(i) || i == logicalStart) { std::size_t styleRunEnd = i == logicalStart ? i : i + 1; std::u16string reversed = writeReverse(inputText, styleRunEnd, styleRunStart); line.first += reversed; for (std::size_t j = 0; j < reversed.size(); j++) { line.second.push_back(currentStyleIndex); } currentStyleIndex = styleIndices.at(i); styleRunStart = styleRunEnd; } if (i == 0) { break; } } } else { line.first += input.first.substr(logicalStart, runLength); line.second.insert(line.second.end(), styleIndices.begin() + logicalStart, styleIndices.begin() + logicalStart + runLength); } } lines.push_back(line); lineStartIndex = lineBreakPoint; } return lines; }