753 lines
23 KiB
C++
753 lines
23 KiB
C++
/*
|
|
**********************************************************************
|
|
* Copyright (C) 2002, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
**********************************************************************
|
|
*/
|
|
|
|
#include "layout/LETypes.h"
|
|
#include "layout/LayoutEngine.h"
|
|
#include "layout/LEFontInstance.h"
|
|
#include "unicode/ubidi.h"
|
|
#include "unicode/uchriter.h"
|
|
#include "unicode/brkiter.h"
|
|
#include "Utilities.h"
|
|
#include "usc_impl.h" /* this is currently private! */
|
|
|
|
#include "ParagraphLayout.h"
|
|
|
|
struct VisualRunInfo
|
|
{
|
|
le_int32 styleRun;
|
|
le_int32 firstChar;
|
|
le_int32 lastChar;
|
|
};
|
|
|
|
struct StyleRunInfo
|
|
{
|
|
LayoutEngine *engine;
|
|
const LEFontInstance *font;
|
|
LEGlyphID *glyphs;
|
|
float *positions;
|
|
UScriptCode script;
|
|
UBiDiLevel level;
|
|
le_int32 runBase;
|
|
le_int32 runLimit;
|
|
le_int32 glyphBase;
|
|
le_int32 glyphCount;
|
|
};
|
|
|
|
class StyleRuns
|
|
{
|
|
public:
|
|
StyleRuns(const le_int32 *styleRunLimits[], const le_int32 styleRunCounts[], le_int32 styleCount);
|
|
|
|
~StyleRuns();
|
|
|
|
le_int32 getRuns(le_int32 runLimits[], le_int32 styleIndices[]);
|
|
|
|
private:
|
|
le_int32 fStyleCount;
|
|
le_int32 fRunCount;
|
|
|
|
le_int32 *fRunLimits;
|
|
le_int32 *fStyleIndices;
|
|
};
|
|
|
|
StyleRuns::StyleRuns(const le_int32 *styleRunLimits[], const le_int32 styleRunCounts[], le_int32 styleCount)
|
|
: fStyleCount(styleCount), fRunCount(0), fRunLimits(NULL), fStyleIndices(NULL)
|
|
{
|
|
le_int32 maxRunCount = 0;
|
|
le_int32 style, run, runStyle;
|
|
le_int32 *currentRun = new le_int32[styleCount];
|
|
|
|
for (int i = 0; i < styleCount; i += 1) {
|
|
maxRunCount += styleRunCounts[i];
|
|
}
|
|
|
|
maxRunCount -= styleCount - 1;
|
|
|
|
fRunLimits = new le_int32[maxRunCount];
|
|
fStyleIndices = new le_int32[maxRunCount * styleCount];
|
|
|
|
for (style = 0; style < styleCount; style += 1) {
|
|
currentRun[style] = 0;
|
|
}
|
|
|
|
run = 0;
|
|
runStyle = 0;
|
|
|
|
/*
|
|
* Since the last run limit for each style run must be
|
|
* the same, all the styles will hit the last limit at
|
|
* the same time, so we know when we're done when the first
|
|
* style hits the last limit.
|
|
*/
|
|
while (currentRun[0] < styleRunCounts[0]) {
|
|
fRunLimits[run] = 0x7FFFFFFF;
|
|
|
|
// find the minimum run limit for all the styles
|
|
for (style = 0; style < styleCount; style += 1) {
|
|
if (styleRunLimits[style][currentRun[style]] < fRunLimits[run]) {
|
|
fRunLimits[run] = styleRunLimits[style][currentRun[style]];
|
|
}
|
|
}
|
|
|
|
// advance all styles whose current run is at this limit to the next run
|
|
for (style = 0; style < styleCount; style += 1) {
|
|
fStyleIndices[runStyle++] = currentRun[style];
|
|
|
|
if (styleRunLimits[style][currentRun[style]] == fRunLimits[run]) {
|
|
currentRun[style] += 1;
|
|
}
|
|
}
|
|
|
|
run += 1;
|
|
}
|
|
|
|
fRunCount = run;
|
|
delete[] currentRun;
|
|
}
|
|
|
|
StyleRuns::~StyleRuns()
|
|
{
|
|
fRunCount = 0;
|
|
|
|
delete[] fStyleIndices;
|
|
fStyleIndices = NULL;
|
|
|
|
delete[] fRunLimits;
|
|
fRunLimits = NULL;
|
|
}
|
|
|
|
le_int32 StyleRuns::getRuns(le_int32 runLimits[], le_int32 styleIndices[])
|
|
{
|
|
if (runLimits != NULL) {
|
|
LE_ARRAY_COPY(runLimits, fRunLimits, fRunCount);
|
|
}
|
|
|
|
if (styleIndices != NULL) {
|
|
LE_ARRAY_COPY(styleIndices, fStyleIndices, fRunCount * fStyleCount);
|
|
}
|
|
|
|
return fRunCount;
|
|
}
|
|
|
|
/*
|
|
* NOTE: This table only has "true" values for
|
|
* those scripts which the LayoutEngine can currently
|
|
* process, rather for all scripts which require
|
|
* complex processing for correct rendering.
|
|
*/
|
|
le_bool ParagraphLayout::fComplexTable[] = {
|
|
false , /* Zyyy */
|
|
false, /* Qaai */
|
|
true, /* Arab */
|
|
false, /* Armn */
|
|
true, /* Beng */
|
|
false, /* Bopo */
|
|
false, /* Cher */
|
|
false, /* Qaac */
|
|
false, /* Cyrl */
|
|
false, /* Dsrt */
|
|
true, /* Deva */
|
|
false, /* Ethi */
|
|
false, /* Geor */
|
|
false, /* Goth */
|
|
false, /* Grek */
|
|
true, /* Gujr */
|
|
true, /* Guru */
|
|
false, /* Hani */
|
|
false, /* Hang */
|
|
true, /* Hebr */
|
|
false, /* Hira */
|
|
true, /* Knda */
|
|
false, /* Kana */
|
|
false, /* Khmr */
|
|
false, /* Laoo */
|
|
false, /* Latn */
|
|
true, /* Mlym */
|
|
false, /* Mong */
|
|
false, /* Mymr */
|
|
false, /* Ogam */
|
|
false, /* Ital */
|
|
true, /* Orya */
|
|
false, /* Runr */
|
|
false, /* Sinh */
|
|
false, /* Syrc */
|
|
true, /* Taml */
|
|
true, /* Telu */
|
|
false, /* Thaa */
|
|
true, /* Thai */
|
|
false, /* Tibt */
|
|
false, /* Cans */
|
|
false, /* Yiii */
|
|
false, /* Tglg */
|
|
false, /* Hano */
|
|
false, /* Buhd */
|
|
false /* Tagb */
|
|
};
|
|
|
|
ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
|
|
const LEFontInstance **fonts, const le_int32 fontRunLimits[], le_int32 fontRunCount,
|
|
const UBiDiLevel levels[], const le_int32 levelRunLimits[], le_int32 levelRunCount,
|
|
const UScriptCode scripts[], const le_int32 scriptRunLimits[], le_int32 scriptRunCount,
|
|
UBiDiLevel paragraphLevel, le_bool vertical)
|
|
: fChars(chars), fCharCount(count),
|
|
fFonts(fonts), fFontRunLimits(fontRunLimits), fFontRunCount(fontRunCount),
|
|
fLevels(levels), fLevelRunLimits(levelRunLimits), fLevelRunCount(levelRunCount),
|
|
fScripts(scripts), fScriptRunLimits(scriptRunLimits), fScriptRunCount(scriptRunCount),
|
|
fVertical(vertical), fClientLevels(true), fClientScripts(true), fEmbeddingLevels(NULL),
|
|
fGlyphToCharMap(NULL), fCharToGlyphMap(NULL), fGlyphWidths(NULL), fGlyphCount(0),
|
|
fParaBidi(NULL), fLineBidi(NULL),
|
|
fStyleRunLimits(NULL), fStyleIndices(NULL), fStyleRunCount(0),
|
|
fBreakIterator(NULL), fLineStart(-1), fLineEnd(0),
|
|
fVisualRuns(NULL), fStyleRunInfo(NULL), fVisualRunCount(-1),
|
|
fFirstVisualRun(-1), fLastVisualRun(-1), fVisualRunLastX(0), fVisualRunLastY(0)
|
|
{
|
|
// FIXME: should check the limit arrays for consistency...
|
|
|
|
computeLevels(paragraphLevel);
|
|
|
|
if (scripts == NULL) {
|
|
computeScripts();
|
|
}
|
|
|
|
// now intersect the font, direction and script runs...
|
|
const le_int32 *styleRunLimits[] = {fFontRunLimits, fLevelRunLimits, fScriptRunLimits};
|
|
const le_int32 styleRunCounts[] = {fFontRunCount, fLevelRunCount, fScriptRunCount};
|
|
le_int32 styleCount = sizeof styleRunLimits / sizeof styleRunLimits[0];
|
|
StyleRuns styleRuns(styleRunLimits, styleRunCounts, styleCount);
|
|
LEErrorCode layoutStatus = LE_NO_ERROR;
|
|
|
|
fStyleRunCount = styleRuns.getRuns(NULL, NULL);
|
|
|
|
fStyleRunLimits = new le_int32[fStyleRunCount];
|
|
fStyleIndices = new le_int32[fStyleRunCount * styleCount];
|
|
|
|
styleRuns.getRuns(fStyleRunLimits, fStyleIndices);
|
|
|
|
// now build a LayoutEngine for each style run...
|
|
le_int32 *styleIndices = fStyleIndices;
|
|
le_int32 run, runStart;
|
|
|
|
fStyleRunInfo = new StyleRunInfo[fStyleRunCount];
|
|
|
|
fGlyphCount = 0;
|
|
for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
|
|
fStyleRunInfo[run].font = fFonts[styleIndices[0]];
|
|
fStyleRunInfo[run].runBase = runStart;
|
|
fStyleRunInfo[run].runLimit = fStyleRunLimits[run];
|
|
fStyleRunInfo[run].script = fScripts[styleIndices[2]];
|
|
fStyleRunInfo[run].level = fLevels[styleIndices[1]];
|
|
fStyleRunInfo[run].glyphBase = fGlyphCount;
|
|
|
|
fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font,
|
|
fStyleRunInfo[run].script, 0, layoutStatus);
|
|
|
|
fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount,
|
|
fStyleRunInfo[run].level & 1, 0, 0, layoutStatus);
|
|
|
|
runStart = fStyleRunLimits[run];
|
|
styleIndices += styleCount;
|
|
fGlyphCount += fStyleRunInfo[run].glyphCount;
|
|
}
|
|
|
|
// Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps,
|
|
// in logical order. (Both maps need an extra entry for the end of the text.)
|
|
//
|
|
// For each layout get the positions and convert them into glyph widths, in
|
|
// logical order. Get the glyph-to-char mapping, offset by starting index in the
|
|
// width array, and swap it into logical order. Then fill in the char-to-glyph map
|
|
// from this. (charToGlyph[glyphToChar[i]] = i)
|
|
fGlyphWidths = new float[fGlyphCount];
|
|
fGlyphToCharMap = new le_int32[fGlyphCount];
|
|
fCharToGlyphMap = new le_int32[fCharCount + 1];
|
|
|
|
for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
|
|
LayoutEngine *engine = fStyleRunInfo[run].engine;
|
|
le_int32 glyphCount = fStyleRunInfo[run].glyphCount;
|
|
le_int32 glyphBase = fStyleRunInfo[run].glyphBase;
|
|
le_int32 glyph;
|
|
|
|
fStyleRunInfo[run].glyphs = new LEGlyphID[glyphCount];
|
|
fStyleRunInfo[run].positions = new float[glyphCount * 2 + 2];
|
|
|
|
engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus);
|
|
engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus);
|
|
engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus);
|
|
|
|
for (glyph = 0; glyph < glyphCount; glyph += 1) {
|
|
fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2];
|
|
fCharToGlyphMap[fGlyphToCharMap[glyphBase + glyph]] = glyphBase + glyph;
|
|
}
|
|
|
|
if ((fStyleRunInfo[run].level & 1) != 0) {
|
|
Utilities::reverse(&fGlyphWidths[glyphBase], glyphCount);
|
|
Utilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount);
|
|
|
|
// Utilities::reverse(&fCharToGlyphMap[runStart], fStyleRunLimits[run] - runStart);
|
|
// convert from visual to logical glyph indices
|
|
for (glyph = glyphBase; glyph < glyphBase + glyphCount; glyph += 1) {
|
|
le_int32 ch = fGlyphToCharMap[glyph];
|
|
le_int32 lastGlyph = glyphBase + glyphCount - 1;
|
|
|
|
// both lastGlyph and fCharToGlyphMap[ch] are biased by
|
|
// glyphBase, so subtracting them will remove the bias.
|
|
fCharToGlyphMap[ch] = lastGlyph - fCharToGlyphMap[ch] + glyphBase;
|
|
}
|
|
}
|
|
|
|
runStart = fStyleRunLimits[run];
|
|
|
|
delete engine;
|
|
fStyleRunInfo[run].engine = NULL;
|
|
}
|
|
|
|
fCharToGlyphMap[fCharCount] = fGlyphCount;
|
|
fGlyphToCharMap[fGlyphCount] = fCharCount;
|
|
}
|
|
|
|
ParagraphLayout::~ParagraphLayout()
|
|
{
|
|
if (! fClientLevels) {
|
|
delete[] (UBiDiLevel *) fLevels;
|
|
fLevels = NULL;
|
|
|
|
delete[] (le_int32 *) fLevelRunLimits;
|
|
fLevelRunLimits = NULL;
|
|
|
|
fClientLevels = true;
|
|
}
|
|
|
|
if (! fClientScripts) {
|
|
delete[] (UScriptCode *) fScripts;
|
|
fScripts = NULL;
|
|
|
|
delete[] (le_int32 *) fScriptRunLimits;
|
|
fScriptRunLimits = NULL;
|
|
|
|
fClientScripts = true;
|
|
}
|
|
|
|
if (fEmbeddingLevels != NULL) {
|
|
delete[] fEmbeddingLevels;
|
|
fEmbeddingLevels = NULL;
|
|
}
|
|
|
|
if (fGlyphToCharMap != NULL) {
|
|
delete[] fGlyphToCharMap;
|
|
fGlyphToCharMap = NULL;
|
|
}
|
|
|
|
if (fCharToGlyphMap != NULL) {
|
|
delete[] fCharToGlyphMap;
|
|
fCharToGlyphMap = NULL;
|
|
}
|
|
|
|
if (fGlyphWidths != NULL) {
|
|
delete[] fGlyphWidths;
|
|
fGlyphWidths = NULL;
|
|
}
|
|
|
|
if (fParaBidi != NULL) {
|
|
ubidi_close(fParaBidi);
|
|
fParaBidi = NULL;
|
|
}
|
|
|
|
if (fLineBidi != NULL) {
|
|
ubidi_close(fLineBidi);
|
|
fLineBidi = NULL;
|
|
}
|
|
|
|
if (fStyleRunCount > 0) {
|
|
le_int32 run;
|
|
|
|
delete[] fStyleRunLimits;
|
|
delete[] fStyleIndices;
|
|
|
|
for (run = 0; run < fStyleRunCount; run += 1) {
|
|
delete[] fStyleRunInfo[run].glyphs;
|
|
delete[] fStyleRunInfo[run].positions;
|
|
|
|
fStyleRunInfo[run].glyphs = NULL;
|
|
fStyleRunInfo[run].positions = NULL;
|
|
}
|
|
|
|
delete[] fStyleRunInfo;
|
|
|
|
fStyleRunLimits = NULL;
|
|
fStyleIndices = NULL;
|
|
fStyleRunInfo = NULL;
|
|
fStyleRunCount = 0;
|
|
}
|
|
|
|
if (fBreakIterator != NULL) {
|
|
delete fBreakIterator;
|
|
fBreakIterator = NULL;
|
|
}
|
|
|
|
if (fVisualRunCount > 0) {
|
|
delete[] fVisualRuns;
|
|
|
|
fVisualRuns = NULL;
|
|
fVisualRunCount = 0;
|
|
}
|
|
}
|
|
|
|
le_bool ParagraphLayout::isComplex(const LEUnicode chars[], le_int32 count)
|
|
{
|
|
UErrorCode scriptStatus = U_ZERO_ERROR;
|
|
UScriptCode scriptCode = USCRIPT_INVALID_CODE;
|
|
UScriptRun *sr = uscript_openRun(chars, count, &scriptStatus);
|
|
|
|
while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) {
|
|
if (isComplex(scriptCode)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
le_int32 ParagraphLayout::nextLineBreak(float width)
|
|
{
|
|
if (fLineEnd >= fCharCount) {
|
|
return -1;
|
|
}
|
|
|
|
fLineStart = fLineEnd;
|
|
|
|
le_int32 glyph = fCharToGlyphMap[fLineStart];
|
|
float widthSoFar = 0;
|
|
|
|
while (glyph < fGlyphCount && widthSoFar + fGlyphWidths[glyph] <= width) {
|
|
widthSoFar += fGlyphWidths[glyph++];
|
|
}
|
|
|
|
// If no glyphs fit on the line, force one to fit.
|
|
//
|
|
// (There shouldn't be any zero width glyphs at the
|
|
// start of a line unless the paragraph consists of
|
|
// only zero width glyphs, because otherwise the zero
|
|
// width glyphs will have been included on the end of
|
|
// the previous line...)
|
|
if (widthSoFar == 0 && glyph < fGlyphCount) {
|
|
glyph += 1;
|
|
}
|
|
|
|
fLineEnd = previousBreak(fGlyphToCharMap[glyph]);
|
|
|
|
// If there's no real break, break at the
|
|
// glyph that didn't fit.
|
|
if (fLineEnd <= fLineStart) {
|
|
fLineEnd = fGlyphToCharMap[glyph];
|
|
}
|
|
|
|
computeVisualRuns();
|
|
|
|
return fLineEnd;
|
|
}
|
|
|
|
le_int32 ParagraphLayout::countLineRuns()
|
|
{
|
|
if (fVisualRunCount < 0) {
|
|
fLineStart = 0;
|
|
fLineEnd = fCharCount;
|
|
computeVisualRuns();
|
|
}
|
|
|
|
return fVisualRunCount;
|
|
}
|
|
|
|
le_int32 ParagraphLayout::getVisualRun(le_int32 runIndex, LEGlyphID glyphs[], float positions[], le_int32 glyphToCharMap[],
|
|
const LEFontInstance **font, UBiDiDirection *runDirection)
|
|
{
|
|
countLineRuns();
|
|
|
|
if (runIndex < 0 || runIndex >= fVisualRunCount) {
|
|
return -1;
|
|
}
|
|
|
|
// need to deal with partial first, last run...
|
|
le_int32 run = fVisualRuns[runIndex].styleRun;
|
|
le_int32 firstChar = fVisualRuns[runIndex].firstChar;
|
|
le_int32 lastChar = fVisualRuns[runIndex].lastChar;
|
|
le_int32 glyphBase = fStyleRunInfo[run].glyphBase;
|
|
le_int32 inGlyph, outGlyph;
|
|
|
|
// Get the glyph indices for all the characters between firstChar and lastChar,
|
|
// make the minimum one be leftGlyph and the maximum one be rightGlyph.
|
|
// (need to do this to handle local reorderings like Indic left matras)
|
|
le_int32 leftGlyph = fGlyphCount;
|
|
le_int32 rightGlyph = -1;
|
|
le_int32 ch;
|
|
|
|
for (ch = firstChar; ch <= lastChar; ch += 1) {
|
|
le_int32 glyph = fCharToGlyphMap[ch];
|
|
|
|
if (glyph < leftGlyph) {
|
|
leftGlyph = glyph;
|
|
}
|
|
|
|
if (glyph > rightGlyph) {
|
|
rightGlyph = glyph;
|
|
}
|
|
}
|
|
|
|
if ((fStyleRunInfo[run].level & 1) != 0) {
|
|
le_int32 swap = rightGlyph;
|
|
le_int32 last = glyphBase + fStyleRunInfo[run].glyphCount - 1;
|
|
|
|
// Here, we want to remove the glyphBase bias...
|
|
rightGlyph = last - leftGlyph;
|
|
leftGlyph = last - swap;
|
|
} else {
|
|
rightGlyph -= glyphBase;
|
|
leftGlyph -= glyphBase;
|
|
}
|
|
|
|
// Make sure that the first glyph on the line is positioned at X = 0,
|
|
// even if we start in the middle of a layout
|
|
if (run == fFirstVisualRun) {
|
|
fVisualRunLastX = - fStyleRunInfo[run].positions[leftGlyph * 2];
|
|
}
|
|
|
|
// Make rightGlyph be the glyph just to the right of
|
|
// the run's glyphs
|
|
rightGlyph += 1;
|
|
|
|
if (glyphs != NULL) {
|
|
LE_ARRAY_COPY(glyphs, &fStyleRunInfo[run].glyphs[leftGlyph], rightGlyph - leftGlyph);
|
|
}
|
|
|
|
if(positions != NULL) {
|
|
for (outGlyph = 0, inGlyph = leftGlyph * 2; inGlyph <= rightGlyph * 2; inGlyph += 2, outGlyph += 2) {
|
|
positions[outGlyph] = fStyleRunInfo[run].positions[inGlyph] + fVisualRunLastX;
|
|
positions[outGlyph + 1] = fStyleRunInfo[run].positions[inGlyph + 1] /* + fVisualRunLastY */;
|
|
}
|
|
|
|
// Save the ending position of this run
|
|
// to use for the start of the next run
|
|
fVisualRunLastX = positions[outGlyph - 2];
|
|
// fVisualRunLastY = positions[rightGlyph * 2 + 2];
|
|
}
|
|
|
|
if(glyphToCharMap != NULL) {
|
|
if ((fStyleRunInfo[run].level & 1) == 0) {
|
|
for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
|
|
glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph];
|
|
}
|
|
} else {
|
|
for (outGlyph = 0, inGlyph = rightGlyph - 1; inGlyph >= leftGlyph; inGlyph -= 1, outGlyph += 1) {
|
|
glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (font != NULL) {
|
|
*font = fStyleRunInfo[run].font;
|
|
}
|
|
|
|
if(runDirection != NULL) {
|
|
if ((fStyleRunInfo[run].level & 1) == 0) {
|
|
*runDirection = UBIDI_LTR;
|
|
} else {
|
|
*runDirection = UBIDI_RTL;
|
|
}
|
|
}
|
|
|
|
return rightGlyph - leftGlyph;
|
|
}
|
|
|
|
void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel)
|
|
{
|
|
UErrorCode bidiStatus = U_ZERO_ERROR;
|
|
|
|
if (fLevels != NULL) {
|
|
le_int32 ch;
|
|
le_int32 run;
|
|
|
|
fEmbeddingLevels = new UBiDiLevel[fCharCount];
|
|
|
|
for (ch = 0, run = 0; run < fLevelRunCount; run += 1) {
|
|
UBiDiLevel runLevel = fLevels[run] | UBIDI_LEVEL_OVERRIDE;
|
|
le_int32 runLimit = fLevelRunLimits[run];
|
|
|
|
while (ch < runLimit) {
|
|
fEmbeddingLevels[ch++] = runLevel;
|
|
}
|
|
}
|
|
}
|
|
|
|
fParaBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
|
|
ubidi_setPara(fParaBidi, fChars, fCharCount, paragraphLevel, fEmbeddingLevels, &bidiStatus);
|
|
|
|
if (fLevels == NULL) {
|
|
fLevelRunCount = ubidi_countRuns(fParaBidi, &bidiStatus);
|
|
fLevels = new UBiDiLevel[fLevelRunCount];
|
|
fLevelRunLimits = new le_int32[fLevelRunCount];
|
|
|
|
fClientLevels = false;
|
|
|
|
le_int32 logicalStart = 0;
|
|
le_int32 run;
|
|
|
|
for (run = 0; run < fLevelRunCount; run += 1) {
|
|
ubidi_getLogicalRun(fParaBidi, logicalStart, (le_int32 *) &fLevelRunLimits[run], (UBiDiLevel *) &fLevels[run]);
|
|
logicalStart = fLevelRunLimits[run];
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: this iterates through the runs twice, which is a bit expensive
|
|
void ParagraphLayout::computeScripts()
|
|
{
|
|
UErrorCode scriptStatus = U_ZERO_ERROR;
|
|
UScriptRun *sr = uscript_openRun(fChars, fCharCount, &scriptStatus);
|
|
|
|
fScriptRunCount = 0;
|
|
|
|
while (uscript_nextRun(sr, NULL, NULL, NULL)) {
|
|
fScriptRunCount += 1;
|
|
}
|
|
|
|
uscript_resetRun(sr);
|
|
|
|
fScripts = new UScriptCode[fScriptRunCount];
|
|
fScriptRunLimits = new le_int32[fScriptRunCount];
|
|
|
|
fClientScripts = false;
|
|
|
|
le_int32 run = 0;
|
|
|
|
while (uscript_nextRun(sr, NULL, (le_int32 *) &fScriptRunLimits[run], (UScriptCode *) &fScripts[run])) {
|
|
run += 1;
|
|
}
|
|
|
|
uscript_closeRun(sr);
|
|
}
|
|
|
|
le_bool ParagraphLayout::isComplex(UScriptCode script)
|
|
{
|
|
if (script < 0 || script >= USCRIPT_CODE_LIMIT) {
|
|
return false;
|
|
}
|
|
|
|
return fComplexTable[script];
|
|
}
|
|
|
|
le_int32 ParagraphLayout::previousBreak(le_int32 charIndex)
|
|
{
|
|
// skip over any whitespace or control characters,
|
|
// because they can hang in the margin.
|
|
while (charIndex < fCharCount &&
|
|
(u_isWhitespace(fChars[charIndex]) ||
|
|
u_iscntrl(fChars[charIndex]))) {
|
|
charIndex += 1;
|
|
}
|
|
|
|
// Create the BreakIterator if we don't already have one
|
|
if (fBreakIterator == NULL) {
|
|
/*Locale thai("en_us"); //*/Locale thai("th");
|
|
UCharCharacterIterator *iter = new UCharCharacterIterator(fChars, fCharCount);
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
|
fBreakIterator = BreakIterator::createLineInstance(thai, status);
|
|
fBreakIterator->adoptText(iter);
|
|
}
|
|
|
|
// 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 fBreakIterator->preceding(charIndex + 1);
|
|
}
|
|
|
|
void ParagraphLayout::computeVisualRuns()
|
|
{
|
|
UErrorCode bidiStatus = U_ZERO_ERROR;
|
|
le_int32 dirRunCount, lineRun, visualRun;
|
|
|
|
if (fVisualRuns != NULL) {
|
|
delete[] fVisualRuns;
|
|
}
|
|
|
|
fVisualRunLastX = 0;
|
|
fVisualRunLastY = 0;
|
|
fFirstVisualRun = getCharRun(fLineStart);
|
|
fLastVisualRun = getCharRun(fLineEnd - 1);
|
|
|
|
//fVisualRunCount = fLastVisualRun - fFirstVisualRun + 1;
|
|
// + 2 because bidi might split the last run in two if
|
|
// it contains trailing whitespace
|
|
fVisualRuns = new VisualRunInfo[fLastVisualRun - fFirstVisualRun + 2];
|
|
|
|
if (fLineBidi == NULL) {
|
|
fLineBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
|
|
}
|
|
|
|
ubidi_setLine(fParaBidi, fLineStart, fLineEnd, fLineBidi, &bidiStatus);
|
|
dirRunCount = ubidi_countRuns(fLineBidi, &bidiStatus);
|
|
|
|
for (lineRun = 0, visualRun = 0; visualRun < dirRunCount; visualRun += 1) {
|
|
le_int32 relStart, run, runLength;
|
|
UBiDiDirection runDirection = ubidi_getVisualRun(fLineBidi, visualRun, &relStart, &runLength);
|
|
le_int32 runStart = fLineStart + relStart;
|
|
le_int32 runEnd = runStart + runLength - 1;
|
|
le_int32 firstRun = getCharRun(runStart);
|
|
le_int32 lastRun = getCharRun(runEnd);
|
|
|
|
if (runDirection == UBIDI_LTR) {
|
|
for (run = firstRun; run <= lastRun; run += 1) {
|
|
fVisualRuns[lineRun].styleRun = run;
|
|
fVisualRuns[lineRun].firstChar = run == firstRun? runStart : fStyleRunInfo[run].runBase;
|
|
fVisualRuns[lineRun].lastChar = run == lastRun? runEnd : fStyleRunInfo[run].runLimit - 1;
|
|
lineRun += 1;
|
|
}
|
|
} else {
|
|
for (run = lastRun; run >= firstRun; run -= 1) {
|
|
fVisualRuns[lineRun].styleRun = run;
|
|
fVisualRuns[lineRun].firstChar = run == firstRun? runStart : fStyleRunInfo[run].runBase;
|
|
fVisualRuns[lineRun].lastChar = run == lastRun? runEnd : fStyleRunInfo[run].runLimit - 1;
|
|
lineRun += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
fVisualRunCount = lineRun;
|
|
}
|
|
|
|
#if 0
|
|
// this doesn't work because the nth entry in the limit array
|
|
// contains the first offset that's *not* in the nth run, but
|
|
// Utilities::search will return a value of n for that offset.
|
|
le_int32 ParagraphLayout::getCharRun(le_int32 charIndex)
|
|
{
|
|
if (charIndex < 0 || charIndex > fCharCount) {
|
|
return -1;
|
|
}
|
|
|
|
return Utilities::search(charIndex, fStyleRunLimits, fStyleRunCount);
|
|
}
|
|
#endif
|
|
|
|
le_int32 ParagraphLayout::getCharRun(le_int32 charIndex)
|
|
{
|
|
if (charIndex < 0 || charIndex > fCharCount) {
|
|
return -1;
|
|
}
|
|
|
|
le_int32 run;
|
|
|
|
// NOTE: as long as fStyleRunLimits is well-formed
|
|
// the above range check guarantees that we'll never
|
|
// fall off the end of the array.
|
|
run = 0;
|
|
while (charIndex >= fStyleRunLimits[run]) {
|
|
run += 1;
|
|
}
|
|
|
|
return run;
|
|
}
|