static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, jbyteArray infoArray, int n, jboolean haveInfo) { // Parameters are checked on java side // Failures from GetXXXArrayElements indicate a serious out-of-memory condition // that we don't bother to report, we're probably dead anyway. jint result = 0; jchar* chs = env->GetCharArrayElements(chsArray, NULL); if (chs != NULL) { jbyte* info = env->GetByteArrayElements(infoArray, NULL); if (info != NULL) { UErrorCode status = U_ZERO_ERROR; UBiDi* bidi = ubidi_openSized(n, 0, &status); ubidi_setPara(bidi, chs, n, dir, NULL, &status); if (U_SUCCESS(status)) { for (int i = 0; i < n; ++i) { info[i] = ubidi_getLevelAt(bidi, i); } result = ubidi_getParaLevel(bidi); } else { jniThrowException(env, "java/lang/RuntimeException", NULL); } ubidi_close(bidi); env->ReleaseByteArrayElements(infoArray, info, 0); } env->ReleaseCharArrayElements(chsArray, chs, JNI_ABORT); } return result; }
extern "C" jlong Java_java_text_Bidi_ubidi_1setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint limit) { UErrorCode status = U_ZERO_ERROR; UBiDi* sized = ubidi_openSized(limit - start, 0, &status); if (maybeThrowIcuException(env, "ubidi_openSized", status)) { return 0; } UniquePtr<BiDiData> lineData(new BiDiData(sized)); ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status); maybeThrowIcuException(env, "ubidi_setLine", status); return reinterpret_cast<uintptr_t>(lineData.release()); }
OSMAND_CORE_API QString OSMAND_CORE_CALL OsmAnd::ICU::convertToVisualOrder(const QString& input) { QString output; const auto len = input.length(); UErrorCode icuError = U_ZERO_ERROR; bool ok = true; // Allocate ICU BiDi context const auto pContext = ubidi_openSized(len, 0, &icuError); if(pContext == nullptr || !U_SUCCESS(icuError)) { LogPrintf(LogSeverityLevel::Error, "ICU error: %d", icuError); return input; } // Configure context to reorder from logical to visual ubidi_setReorderingMode(pContext, UBIDI_REORDER_DEFAULT); // Set data ubidi_setPara(pContext, reinterpret_cast<const UChar*>(input.unicode()), len, UBIDI_DEFAULT_RTL, nullptr, &icuError); ok = U_SUCCESS(icuError); if(ok) { QVector<UChar> reordered(len); ubidi_writeReordered(pContext, reordered.data(), len, UBIDI_DO_MIRRORING | UBIDI_REMOVE_BIDI_CONTROLS, &icuError); ok = U_SUCCESS(icuError); if(ok) { QVector<UChar> reshaped(len); const auto newLen = u_shapeArabic(reordered.constData(), len, reshaped.data(), len, U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_AT_END, &icuError); ok = U_SUCCESS(icuError); if(ok) { output = qMove(QString(reinterpret_cast<const QChar*>(reshaped.constData()), newLen)); } } } // Release context ubidi_close(pContext); if(!ok) { LogPrintf(LogSeverityLevel::Error, "ICU error: %d", icuError); return input; } return output; }
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 font_face_set::get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format) { double avg_height = character_dimensions('X').height(); UErrorCode err = U_ZERO_ERROR; UnicodeString reordered; UnicodeString shaped; int32_t length = ustr.length(); UBiDi *bidi = ubidi_openSized(length, 0, &err); ubidi_setPara(bidi, ustr.getBuffer(), length, UBIDI_DEFAULT_LTR, 0, &err); ubidi_writeReordered(bidi, reordered.getBuffer(length), length, UBIDI_DO_MIRRORING, &err); reordered.releaseBuffer(length); u_shapeArabic(reordered.getBuffer(), length, shaped.getBuffer(length), length, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR, &err); shaped.releaseBuffer(length); if (U_SUCCESS(err)) { StringCharacterIterator iter(shaped); for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); char_info char_dim = character_dimensions(ch); char_dim.format = format; char_dim.avg_height = avg_height; info.add_info(char_dim); } } #if (U_ICU_VERSION_MAJOR_NUM*100 + U_ICU_VERSION_MINOR_NUM >= 406) if (ubidi_getBaseDirection(ustr.getBuffer(), length) == UBIDI_RTL) { info.set_rtl(true); } #endif ubidi_close(bidi); }
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); } }
JNIEXPORT void JNICALL Java_java_text_Bidi_nativeBidiChars (JNIEnv *env, jclass cls, jobject jbidi, jcharArray text, jint tStart, jbyteArray embs, jint eStart, jint length, jint dir) { UErrorCode err = U_ZERO_ERROR; UBiDi* bidi = ubidi_openSized(length, length, &err); if (!U_FAILURE(err)) { jchar *cText = (jchar*)(*env)->GetPrimitiveArrayCritical(env, text, NULL); if (cText) { UBiDiLevel baseLevel = (UBiDiLevel)dir; jbyte *cEmbs = 0; uint8_t *cEmbsAdj = 0; if (embs != NULL) { cEmbs = (jbyte*)(*env)->GetPrimitiveArrayCritical(env, embs, NULL); if (cEmbs) { cEmbsAdj = (uint8_t*)(cEmbs + eStart); } } ubidi_setPara(bidi, cText + tStart, length, baseLevel, cEmbsAdj, &err); if (cEmbs) { (*env)->ReleasePrimitiveArrayCritical(env, embs, cEmbs, JNI_ABORT); } (*env)->ReleasePrimitiveArrayCritical(env, text, cText, JNI_ABORT); if (!U_FAILURE(err)) { jint resDir = (jint)ubidi_getDirection(bidi); jint resLevel = (jint)ubidi_getParaLevel(bidi); jint resRunCount = 0; jintArray resRuns = 0; jintArray resCWS = 0; if (resDir == UBIDI_MIXED) { resRunCount = (jint)ubidi_countRuns(bidi, &err); if (!U_FAILURE(err)) { if (resRunCount) { jint* cResRuns = (jint*)calloc(resRunCount * 2, sizeof(jint)); if (cResRuns) { UTextOffset limit = 0; UBiDiLevel level; jint *p = cResRuns; while (limit < length) { ubidi_getLogicalRun(bidi, limit, &limit, &level); *p++ = (jint)limit; *p++ = (jint)level; } { const DirProp *dp = bidi->dirProps; jint ccws = 0; jint n = 0; p = cResRuns; do { if ((*(p+1) ^ resLevel) & 0x1) { while (n < *p) { if (dp[n++] == WS) { ++ccws; } } } else { n = *p; } p += 2; } while (n < length); resCWS = (*env)->NewIntArray(env, ccws); if (resCWS) { jint* cResCWS = (jint*)(*env)->GetPrimitiveArrayCritical(env, resCWS, NULL); if (cResCWS) { jint ccws = 0; jint n = 0; p = cResRuns; do { if ((*(p+1) ^ resLevel) & 0x1) { while (n < *p) { if (dp[n] == WS) { cResCWS[ccws++] = n; } ++n; } } else { n = *p; } p += 2; } while (n < length); (*env)->ReleasePrimitiveArrayCritical(env, resCWS, cResCWS, 0); } } } resRuns = (*env)->NewIntArray(env, resRunCount * 2); if (resRuns) { (*env)->SetIntArrayRegion(env, resRuns, 0, resRunCount * 2, cResRuns); } free(cResRuns); } } } } resetBidi(env, cls, jbidi, resDir, resLevel, length, resRuns, resCWS); } } ubidi_close(bidi); } }
U_DRAFT uint32_t U_EXPORT2 ubiditransform_transform(UBiDiTransform *pBiDiTransform, const UChar *src, int32_t srcLength, UChar *dest, int32_t destSize, UBiDiLevel inParaLevel, UBiDiOrder inOrder, UBiDiLevel outParaLevel, UBiDiOrder outOrder, UBiDiMirroring doMirroring, uint32_t shapingOptions, UErrorCode *pErrorCode) { uint32_t destLength = 0; UBool textChanged = FALSE; const UBiDiTransform *pOrigTransform = pBiDiTransform; const UBiDiAction *action = NULL; if (U_FAILURE(*pErrorCode)) { return 0; } if (src == NULL || dest == NULL) { *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; return 0; } CHECK_LEN(src, srcLength, pErrorCode); CHECK_LEN(dest, destSize, pErrorCode); if (pBiDiTransform == NULL) { pBiDiTransform = ubiditransform_open(pErrorCode); if (U_FAILURE(*pErrorCode)) { return 0; } } /* Current limitation: in multiple paragraphs will be resolved according to the 1st paragraph */ resolveBaseDirection(src, srcLength, &inParaLevel, &outParaLevel); pBiDiTransform->pActiveScheme = findMatchingScheme(inParaLevel, outParaLevel, inOrder, outOrder); if (pBiDiTransform->pActiveScheme == NULL) { goto cleanup; } pBiDiTransform->reorderingOptions = doMirroring ? UBIDI_DO_MIRRORING : UBIDI_REORDER_DEFAULT; /* Ignore TEXT_DIRECTION_* flags, as we apply our own depending on the text scheme at the time shaping is invoked. */ shapingOptions &= ~U_SHAPE_TEXT_DIRECTION_MASK; pBiDiTransform->digits = shapingOptions & ~U_SHAPE_LETTERS_MASK; pBiDiTransform->letters = shapingOptions & ~U_SHAPE_DIGITS_MASK; updateSrc(pBiDiTransform, src, srcLength, destSize > srcLength ? destSize : srcLength, pErrorCode); if (U_FAILURE(*pErrorCode)) { goto cleanup; } if (pBiDiTransform->pBidi == NULL) { pBiDiTransform->pBidi = ubidi_openSized(0, 0, pErrorCode); if (U_FAILURE(*pErrorCode)) { goto cleanup; } } pBiDiTransform->dest = dest; pBiDiTransform->destSize = destSize; pBiDiTransform->pDestLength = &destLength; /* Checking for U_SUCCESS() within the loop to bail out on first failure. */ for (action = pBiDiTransform->pActiveScheme->actions; *action && U_SUCCESS(*pErrorCode); action++) { if ((*action)(pBiDiTransform, pErrorCode)) { if (action + 1) { updateSrc(pBiDiTransform, pBiDiTransform->dest, *pBiDiTransform->pDestLength, *pBiDiTransform->pDestLength, pErrorCode); } textChanged = TRUE; } } ubidi_setInverse(pBiDiTransform->pBidi, FALSE); if (!textChanged && U_SUCCESS(*pErrorCode)) { /* Text was not changed - just copy src to dest */ if (destSize < srcLength) { *pErrorCode = U_BUFFER_OVERFLOW_ERROR; } else { u_strncpy(dest, src, srcLength); destLength = srcLength; } } cleanup: if (pOrigTransform != pBiDiTransform) { ubiditransform_close(pBiDiTransform); } else { pBiDiTransform->dest = NULL; pBiDiTransform->pDestLength = NULL; pBiDiTransform->srcLength = 0; pBiDiTransform->destSize = 0; } return U_FAILURE(*pErrorCode) ? 0 : destLength; }
int main (int argc, char** argv) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " <num-iter>" << std::endl; return EXIT_FAILURE; } const unsigned NUM_ITER = atoi(argv[1]); // open first face in the font FT_Library ft_library = 0; FT_Error error = FT_Init_FreeType(&ft_library); if (error) throw std::runtime_error("Failed to initialize FreeType2 library"); FT_Face ft_face[NUM_EXAMPLES]; FT_New_Face(ft_library, "fonts/DejaVuSerif.ttf", 0, &ft_face[ENGLISH]); FT_New_Face(ft_library, "fonts/amiri-0.104/amiri-regular.ttf", 0, &ft_face[ARABIC]); FT_New_Face(ft_library, "fonts/fireflysung-1.3.0/fireflysung.ttf", 0, &ft_face[CHINESE]); // Get our harfbuzz font structs hb_font_t *hb_ft_font[NUM_EXAMPLES]; hb_ft_font[ENGLISH] = hb_ft_font_create(ft_face[ENGLISH], NULL); hb_ft_font[ARABIC] = hb_ft_font_create(ft_face[ARABIC] , NULL); hb_ft_font[CHINESE] = hb_ft_font_create(ft_face[CHINESE], NULL); { std::cerr << "Starting ICU shaping:" << std::endl; progress_timer timer1(std::clog,"ICU shaping done"); UErrorCode err = U_ZERO_ERROR; for (unsigned i = 0; i < NUM_ITER; ++i) { for (unsigned j = 0; j < NUM_EXAMPLES; ++j) { UnicodeString text = UnicodeString::fromUTF8(texts[j]); int32_t length = text.length(); UnicodeString reordered; UnicodeString shaped; UBiDi *bidi = ubidi_openSized(length, 0, &err); ubidi_setPara(bidi, text.getBuffer(), length, UBIDI_DEFAULT_LTR, 0, &err); ubidi_writeReordered(bidi, reordered.getBuffer(length), length, UBIDI_DO_MIRRORING, &err); ubidi_close(bidi); reordered.releaseBuffer(length); u_shapeArabic(reordered.getBuffer(), length, shaped.getBuffer(length), length, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR, &err); shaped.releaseBuffer(length); if (U_SUCCESS(err)) { U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped); for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); int32_t glyph_index = FT_Get_Char_Index(ft_face[j], ch); if (i == 0) { std::cerr << glyph_index << ":"; } } if (i == 0) std::cerr << std::endl; } } } } { const char **shaper_list = hb_shape_list_shapers(); for ( ;*shaper_list; shaper_list++) { std::cerr << *shaper_list << std::endl; } std::cerr << "Starting Harfbuzz shaping" << std::endl; progress_timer timer2(std::clog,"Harfbuzz shaping done"); const char* const shapers[] = { /*"ot",*/"fallback" }; hb_buffer_t *buffer(hb_buffer_create()); for (unsigned i = 0; i < NUM_ITER; ++i) { for (unsigned j = 0; j < NUM_EXAMPLES; ++j) { UnicodeString text = UnicodeString::fromUTF8(texts[j]); int32_t length = text.length(); hb_buffer_clear_contents(buffer); //hb_buffer_set_unicode_funcs(buffer.get(), hb_icu_get_unicode_funcs()); hb_buffer_pre_allocate(buffer, length); hb_buffer_add_utf16(buffer, text.getBuffer(), text.length(), 0, length); hb_buffer_set_direction(buffer, text_directions[j]); hb_buffer_set_script(buffer, scripts[j]); hb_buffer_set_language(buffer,hb_language_from_string(languages[j], std::strlen(languages[j]))); //hb_shape(hb_ft_font[j], buffer.get(), 0, 0); hb_shape_full(hb_ft_font[j], buffer, 0, 0, shapers); unsigned num_glyphs = hb_buffer_get_length(buffer); hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer, NULL); //hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), NULL); for (unsigned k=0; k<num_glyphs; ++k) { int32_t glyph_index = glyphs[k].codepoint; if (i == 0) { std::cerr << glyph_index << ":"; } } if (i == 0) std::cerr << std::endl; } } hb_buffer_destroy(buffer); } // cleanup for (int j=0; j < NUM_EXAMPLES; ++j) { hb_font_destroy(hb_ft_font[j]); } FT_Done_FreeType(ft_library); return EXIT_SUCCESS; }
static void shape_text(text_line & line, text_itemizer & itemizer, std::map<unsigned,double> & width_map, face_manager_freetype & font_manager, double scale_factor ) { unsigned start = line.first_char(); unsigned end = line.last_char(); mapnik::value_unicode_string const& text = itemizer.text(); size_t length = end - start; if (!length) return; line.reserve(length); std::list<text_item> const& list = itemizer.itemize(start, end); UErrorCode err = U_ZERO_ERROR; mapnik::value_unicode_string shaped; mapnik::value_unicode_string reordered; for (auto const& text_item : list) { face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset); double size = text_item.format->text_size * scale_factor; face_set->set_unscaled_character_sizes(); for (auto const& face : *face_set) { UBiDi *bidi = ubidi_openSized(length, 0, &err); ubidi_setPara(bidi, text.getBuffer(), length, UBIDI_DEFAULT_LTR, 0, &err); ubidi_writeReordered(bidi, reordered.getBuffer(length), length, UBIDI_DO_MIRRORING, &err); ubidi_close(bidi); reordered.releaseBuffer(length); int32_t num_char = u_shapeArabic(reordered.getBuffer(), length, shaped.getBuffer(length), length, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR, &err); if (num_char < 0) { MAPNIK_LOG_ERROR(icu_shaper) << " u_shapeArabic returned negative num_char " << num_char; } std::size_t num_chars = static_cast<std::size_t>(num_char); shaped.releaseBuffer(length); bool shaped_status = true; if (U_SUCCESS(err) && (num_chars == length)) { U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped); unsigned i = 0; for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); glyph_info tmp; tmp.offset.clear(); tmp.char_index = i; tmp.glyph_index = FT_Get_Char_Index(face->get_face(), ch); if (tmp.glyph_index == 0) { shaped_status = false; break; } tmp.face = face; tmp.format = text_item.format; face->glyph_dimensions(tmp); tmp.scale_multiplier = size / face->get_face()->units_per_EM; width_map[i] += tmp.advance(); line.add_glyph(std::move(tmp), scale_factor); ++i; } } if (!shaped_status) continue; line.update_max_char_height(face->get_char_height(size)); return; } } }
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); }
U_CAPI UBiDi * U_EXPORT2 ubidi_open(void) { UErrorCode errorCode=U_ZERO_ERROR; return ubidi_openSized(0, 0, &errorCode); }