| /* |
| ******************************************************************************* |
| * |
| * 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/unicode.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 && |
| (Unicode::isWhitespace(ch) || |
| Unicode::isControl(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); |
| |
| 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) { |
| UTextOffset 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) { |
| UTextOffset 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); |
| } |
| |