Exemple #1
0
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;
	}
}
Exemple #4
0
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);
}
Exemple #5
0
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);
    }
}
Exemple #6
0
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);
}
Exemple #7
0
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);
}
Exemple #8
0
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;
}
Exemple #9
0
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;
}