Logo Search packages:      
Sourcecode: icu version File versions

paragraph.cpp

/*
 *******************************************************************************
 *
 *   Copyright (C) 1999-2001, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
 *   file name:  Paragraph.cpp
 *
 *   created on: 09/06/2000
 *   created by: Eric R. Mader
 */

#include "unicode/loengine.h"

#include "RenderingFontInstance.h"

#include "unicode/utypes.h"
#include "unicode/uchar.h"
#include "unicode/uchriter.h"
#include "unicode/brkiter.h"
#include "unicode/locid.h"
#include "unicode/ubidi.h"

#include "paragraph.h"
#include "scrptrun.h"
#include "UnicodeReader.h"
#include "FontMap.h"

#define MARGIN 10

Paragraph::Paragraph(void *surface, RunParams params[], int32_t count, UBiDi *bidi)
    : fBidi(bidi), fRunCount(count), fRunInfo(NULL), fCharCount(0), fText(NULL), fGlyphCount(0), fGlyphs(NULL),
      fCharIndices(NULL), fGlyphIndices(NULL), fDX(NULL), fBreakArray(NULL), fBreakCount(0),
      fLineHeight(-1), fAscent(-1)
{
    int32_t i;

    fWidth = fHeight = 0;

    fRunInfo = new RunInfo[count + 1];

    // Set charBase and rightToLeft for
    // each run and count the total characters
    for (i = 0; i < count; i += 1) {
        fRunInfo[i].charBase = fCharCount;
        fRunInfo[i].rightToLeft = params[i].rightToLeft;
        fCharCount += params[i].count;
    }

    // Set charBase and rightToLeft for the
    // fake run at the end.
    fRunInfo[count].charBase = fCharCount;
    fRunInfo[count].rightToLeft = false;

    fBreakArray = new int32_t[fCharCount + 1];
    fText = new LEUnicode[fCharCount];
    
    // Copy the text runs into a single array
    for (i = 0; i < count; i += 1) {
        int32_t charBase = fRunInfo[i].charBase;
        int32_t charCount = fRunInfo[i + 1].charBase - charBase;

        LE_ARRAY_COPY(&fText[charBase], params[i].text, charCount);
    }

    Locale thai("th");
    UCharCharacterIterator *iter = new UCharCharacterIterator(fText, fCharCount);
    UErrorCode status = U_ZERO_ERROR;
    Locale dummyLocale;

    fBrkiter = BreakIterator::createLineInstance(thai, status);
    fBrkiter->adoptText(iter);

    ICULayoutEngine **engines = new ICULayoutEngine *[count];
    int32_t maxAscent = -1, maxDescent = -1, maxLeading = -1;
    float x = 0, y = 0;

    // Layout each run, set glyphBase and glyphCount
    // and count the total number of glyphs
    for (i = 0; i < count; i += 1) {
        int32_t charBase = fRunInfo[i].charBase;
        int32_t charCount = fRunInfo[i + 1].charBase - charBase;
        int32_t glyphCount = 0;
        int32_t runAscent = 0, runDescent = 0, runLeading = 0;
        UErrorCode success = U_ZERO_ERROR;

        fRunInfo[i].fontInstance = params[i].fontInstance;

        fRunInfo[i].fontInstance->setFont(surface);

        runAscent  = fRunInfo[i].fontInstance->getAscent();
        runDescent = fRunInfo[i].fontInstance->getDescent();
        runLeading = fRunInfo[i].fontInstance->getLeading();


        if (runAscent > maxAscent) {
            maxAscent = runAscent;
        }

        if (runDescent > maxDescent) {
            maxDescent = runDescent;
        }

        if (runLeading > maxLeading) {
            maxLeading = runLeading;
        }

        engines[i] = ICULayoutEngine::createInstance(fRunInfo[i].fontInstance, params[i].scriptCode, dummyLocale, success);

        glyphCount = engines[i]->layoutChars(fText, charBase, charBase + charCount, fCharCount,
            fRunInfo[i].rightToLeft, x, y, success);

        engines[i]->getGlyphPosition(glyphCount, x, y, success);

        fRunInfo[i].glyphBase = fGlyphCount;
        fGlyphCount += glyphCount;
    }

    fLineHeight = maxAscent + maxDescent + maxLeading;
    fAscent = maxAscent;

    // Set glyphBase for the fake run at the end
    fRunInfo[count].glyphBase = fGlyphCount;

    fGlyphs = new LEGlyphID[fGlyphCount];
    fCharIndices = new int32_t[fGlyphCount];
    fGlyphIndices = new int32_t[fCharCount + 1];
    fDX = new int32_t[fGlyphCount];
    fDY = new int32_t[fGlyphCount];


    float *positions = new float[fGlyphCount * 2 + 2];

    // Build the glyph, charIndices and positions arrays
    for (i = 0; i < count; i += 1) {
        ICULayoutEngine *engine = engines[i];
        int32_t charBase = fRunInfo[i].charBase;
        int32_t glyphBase = fRunInfo[i].glyphBase;
        UErrorCode success = U_ZERO_ERROR;

        engine->getGlyphs(&fGlyphs[glyphBase], success);
        engine->getCharIndices(&fCharIndices[glyphBase], charBase, success);
        engine->getGlyphPositions(&positions[glyphBase * 2], success);
    }

    // Filter deleted glyphs, compute logical advances
    // and set the char to glyph map
    for (i = 0; i < fGlyphCount; i += 1) {
        // Filter deleted glyphs
        if (fGlyphs[i] == 0xFFFE || fGlyphs[i] == 0xFFFF) {
            fGlyphs[i] = 0x0001;
        }

        // compute the logical advance
        fDX[i] = (int32_t) (positions[i * 2 + 2] - positions[i * 2]);

        // save the Y offset
        fDY[i] = (int32_t) positions[i * 2 + 1];

        // set char to glyph map
        fGlyphIndices[fCharIndices[i]] = i;
    }

    if (fRunInfo[count - 1].rightToLeft) {
        fGlyphIndices[fCharCount] = fRunInfo[count - 1].glyphBase - 1;
    } else {
        fGlyphIndices[fCharCount] = fGlyphCount;
    }

    delete[] positions;

    // Get rid of the LayoutEngine's:
    for (i = 0; i < count; i += 1) {
        delete engines[i];
    }

    delete[] engines;
}

Paragraph::~Paragraph()
{
    delete[] fDY;
    delete[] fDX;
    delete[] fGlyphIndices;
    delete[] fCharIndices;
    delete[] fGlyphs;

    delete fBrkiter;
    delete fText;

    delete[] fBreakArray;
    delete[] fRunInfo;

    ubidi_close(fBidi);
}

int32_t Paragraph::getLineHeight()
{
    return fLineHeight;
}

int32_t Paragraph::getLineCount()
{
    return fBreakCount;
}

int32_t Paragraph::getAscent()
{
    return fAscent;
}

int32_t Paragraph::previousBreak(int32_t charIndex)
{
    LEUnicode ch = fText[charIndex];

    // skip over any whitespace or control
    // characters, because they can hang in
    // the margin.
    while (charIndex < fCharCount &&
           (u_isWhitespace(ch) ||
            u_iscntrl(ch))) {
        ch = fText[++charIndex];
    }

    // return the break location that's at or before
    // the character we stopped on. Note: if we're
    // on a break, the "+ 1" will cause preceding to
    // back up to it.
    return fBrkiter->preceding(charIndex + 1);
}

void Paragraph::breakLines(int32_t width, int32_t height)
{
    int32_t lineWidth = width - (2 * MARGIN);
    int32_t thisWidth = 0;
    int32_t thisBreak = -1;
    int32_t prevWidth = fWidth;

    fWidth  = width;
    fHeight = height;

    // don't re-break if the width hasn't changed
    if (width == prevWidth) {
        return;
    }

    fBreakArray[0] = 0;
    fBreakCount = 1;

    for (int32_t run = 0; run < fRunCount; run += 1) {
        int32_t glyph = fRunInfo[run].glyphBase;
        int32_t stop = fRunInfo[run + 1].glyphBase;
        int32_t dir = 1;

        if (fRunInfo[run].rightToLeft) {
            glyph = stop - 1;
            stop = fRunInfo[run].glyphBase - 1;
            dir = -1;
        }

        while (glyph != stop) {
            // Find the first glyph that doesn't fit on the line
            while (thisWidth + fDX[glyph] <= lineWidth) {
                thisWidth += fDX[glyph];
                glyph += dir;

                if (glyph == stop) {
                    break;
                }
            }

            // Check to see if we fell off the
            // end of the run
            if (glyph == stop) {
                break;
            }


            // Find a place before here to break,
            thisBreak = previousBreak(fCharIndices[glyph]);

            // If there wasn't one, force one
            if (thisBreak <= fBreakArray[fBreakCount - 1]) {
                thisBreak = fCharIndices[glyph];
            }

            // Save the break location.
            fBreakArray[fBreakCount++] = thisBreak;

            // Reset the accumulated width
            thisWidth = 0;

            // Map the character back to a glyph
            glyph = fGlyphIndices[thisBreak];

            // Check to see if the new glyph is off
            // the end of the run.
            if (glyph == stop) {
                break;
            }

            // If the glyph's not in the run we stopped in, we
            // have to re-synch to the new run
            if (glyph < fRunInfo[run].glyphBase || glyph >= fRunInfo[run + 1].glyphBase) {
                run = getGlyphRun(glyph, 0, 1);

                if (fRunInfo[run].rightToLeft) {
                    stop = fRunInfo[run].glyphBase - 1;
                    dir = -1;
                } else {
                    stop = fRunInfo[run + 1].glyphBase;
                    dir = 1;
                }
            }
        }
    }

    // Make sure the last break is after the last character
    if (fBreakArray[--fBreakCount] != fCharCount) {
        fBreakArray[++fBreakCount] = fCharCount;
    }

    return;
}

int32_t Paragraph::getGlyphRun(int32_t glyph, int32_t startingRun, int32_t direction)
{
    int32_t limit;

    if (direction < 0) {
        limit = -1;
    } else {
        limit = fRunCount;
    }

    for (int32_t run = startingRun; run != limit; run += direction) {
        if (glyph >= fRunInfo[run].glyphBase && glyph < fRunInfo[run + 1].glyphBase) {
            return run;
        }
    }

    return limit;
}

int32_t Paragraph::getCharRun(int32_t ch, int32_t startingRun, int32_t direction)
{
    int32_t limit;

    if (direction < 0) {
        limit = -1;
    } else {
        limit = fRunCount;
    }

    for (int32_t run = startingRun; run != limit; run += direction) {
        if (ch >= fRunInfo[run].charBase && ch < fRunInfo[run + 1].charBase) {
            return run;
        }
    }

    return limit;
}

int32_t Paragraph::getRunWidth(int32_t startGlyph, int32_t endGlyph)
{
    int32_t width = 0;

    for (int32_t glyph = startGlyph; glyph <= endGlyph; glyph += 1) {
        width += fDX[glyph];
    }

    return width;
}

int32_t Paragraph::drawRun(void *surface, const RenderingFontInstance *fontInstance, int32_t firstChar, int32_t lastChar,
                         int32_t x, int32_t y)
{
    int32_t firstGlyph = fGlyphIndices[firstChar];
    int32_t lastGlyph  = fGlyphIndices[lastChar];

    for (int32_t ch = firstChar; ch <= lastChar; ch += 1) {
        int32_t glyph = fGlyphIndices[ch];

        if (glyph < firstGlyph) {
            firstGlyph = glyph;
        }

        if (glyph > lastGlyph) {
            lastGlyph = glyph;
        }
    }

    int32_t dyStart = firstGlyph, dyEnd = dyStart;

    fontInstance->setFont(surface);

    while (dyEnd <= lastGlyph) {
        while (dyEnd <= lastGlyph && fDY[dyStart] == fDY[dyEnd]) {
            dyEnd += 1;
        }

        fontInstance->drawGlyphs(surface, &fGlyphs[dyStart], dyEnd - dyStart,
            &fDX[dyStart], x, y + fDY[dyStart], fWidth, fHeight);

        for (int32_t i = dyStart; i < dyEnd; i += 1) {
            x += fDX[i];
        }

        dyStart = dyEnd;
    }

    return getRunWidth(firstGlyph, lastGlyph);
}

void Paragraph::draw(void *surface, int32_t firstLine, int32_t lastLine)
{
    int32_t line, x, y;
    int32_t prevRun = 0;
    UErrorCode bidiStatus = U_ZERO_ERROR;
    UBiDi  *lBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);

    y = fAscent;

    for (line = firstLine; line <= lastLine; line += 1) {
        int32_t firstChar = fBreakArray[line];
        int32_t lastChar  = fBreakArray[line + 1] - 1;
        int32_t dirCount, dirRun;

        x = MARGIN;

        ubidi_setLine(fBidi, firstChar, lastChar + 1, lBidi, &bidiStatus);

        dirCount = ubidi_countRuns(lBidi, &bidiStatus);

        for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
            int32_t relStart = 0, runLength = 0;
            UBiDiDirection runDirection = ubidi_getVisualRun(lBidi, dirRun, &relStart, &runLength);
            int32_t runStart  = relStart + firstChar;
            int32_t runEnd    = runStart + runLength - 1;
            int32_t firstRun  = getCharRun(runStart, prevRun, 1);
            int32_t lastRun   = getCharRun(runEnd,   firstRun, 1);

            for (int32_t run = firstRun; run <= lastRun; run += 1) {
                const RenderingFontInstance *fontInstance = fRunInfo[run].fontInstance;
                int32_t nextBase;

                if (run == lastRun) {
                    nextBase = runEnd + 1;
                } else {
                    nextBase = fRunInfo[run + 1].charBase;
                }

                x += drawRun(surface, fontInstance, runStart, nextBase - 1, x, y);
                runStart = nextBase;
            }

            prevRun = lastRun;
        }

        y += fLineHeight;
    }

    ubidi_close(lBidi);
}

Paragraph *Paragraph::paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport, void *surface)
{
    RunParams params[64];
    int32_t paramCount = 0;
    int32_t charCount  = 0;
    int32_t dirCount   = 0;
    int32_t dirRun     = 0;
    RFIErrorCode fontStatus = RFI_NO_ERROR;
    UErrorCode bidiStatus = U_ZERO_ERROR;
    const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount);
    ScriptRun scriptRun(text, charCount);

    if (text == NULL) {
        return NULL;
    }

    UBiDi *pBidi = ubidi_openSized(charCount, 0, &bidiStatus);

    ubidi_setPara(pBidi, text, charCount, UBIDI_DEFAULT_LTR, NULL, &bidiStatus);

    dirCount = ubidi_countRuns(pBidi, &bidiStatus);

    for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
        int32_t runStart = 0, runLength = 0;
        UBiDiDirection runDirection = ubidi_getVisualRun(pBidi, dirRun, &runStart, &runLength);
        
        scriptRun.reset(runStart, runLength);

        while (scriptRun.next()) {
            int32_t     start = scriptRun.getScriptStart();
            int32_t     end   = scriptRun.getScriptEnd();
            UScriptCode code  = scriptRun.getScriptCode();

            params[paramCount].text = &((UChar *) text)[start];
            params[paramCount].count = end - start;
            params[paramCount].scriptCode = (UScriptCode) code;
            params[paramCount].rightToLeft = runDirection == UBIDI_RTL;

            params[paramCount].fontInstance = fontMap->getScriptFont(code, fontStatus);

            if (params[paramCount].fontInstance == NULL) {
                ubidi_close(pBidi);
                return 0;
            }

            paramCount += 1;
        }
    }

    return new Paragraph(surface, params, paramCount, pBidi);
}


Generated by  Doxygen 1.6.0   Back to index