summaryrefslogtreecommitdiff
path: root/Build/source/libs/icu/icu-xetex/layoutex/ParagraphLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/icu/icu-xetex/layoutex/ParagraphLayout.cpp')
-rw-r--r--Build/source/libs/icu/icu-xetex/layoutex/ParagraphLayout.cpp1173
1 files changed, 1173 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-xetex/layoutex/ParagraphLayout.cpp b/Build/source/libs/icu/icu-xetex/layoutex/ParagraphLayout.cpp
new file mode 100644
index 00000000000..27e75c2e0d6
--- /dev/null
+++ b/Build/source/libs/icu/icu-xetex/layoutex/ParagraphLayout.cpp
@@ -0,0 +1,1173 @@
+/*
+ **********************************************************************
+ * Copyright (C) 2002-2008, International Business Machines
+ * Corporation and others. All Rights Reserved.
+ **********************************************************************
+ */
+
+/*
+ * paragraphLayout doesn't make much sense without
+ * BreakIterator...
+ */
+#include "layout/LETypes.h"
+#include "layout/LEScripts.h"
+#include "layout/LELanguages.h"
+#include "layout/LayoutEngine.h"
+#include "layout/LEFontInstance.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/uchriter.h"
+#include "unicode/brkiter.h"
+
+#if ! UCONFIG_NO_BREAK_ITERATION
+#include "LXUtilities.h"
+#include "usc_impl.h" /* this is currently private! */
+#include "cstring.h" /* this too! */
+
+#include "layout/ParagraphLayout.h"
+
+U_NAMESPACE_BEGIN
+
+#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
+
+/* Leave this copyright notice here! It needs to go somewhere in this library. */
+static const char copyright[] = U_COPYRIGHT_STRING;
+
+class StyleRuns
+{
+public:
+ StyleRuns(const RunArray *styleRunArrays[], 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 RunArray *styleRunArrays[], le_int32 styleCount)
+ : fStyleCount(styleCount), fRunCount(0), fRunLimits(NULL), fStyleIndices(NULL)
+{
+ le_int32 maxRunCount = 0;
+ le_int32 style, run, runStyle;
+ le_int32 *currentRun = LE_NEW_ARRAY(le_int32, styleCount);
+
+ for (int i = 0; i < styleCount; i += 1) {
+ maxRunCount += styleRunArrays[i]->getCount();
+ }
+
+ maxRunCount -= styleCount - 1;
+
+ fRunLimits = LE_NEW_ARRAY(le_int32, maxRunCount);
+ fStyleIndices = LE_NEW_ARRAY(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] < styleRunArrays[0]->getCount()) {
+ fRunLimits[run] = 0x7FFFFFFF;
+
+ // find the minimum run limit for all the styles
+ for (style = 0; style < styleCount; style += 1) {
+ if (styleRunArrays[style]->getLimit(currentRun[style]) < fRunLimits[run]) {
+ fRunLimits[run] = styleRunArrays[style]->getLimit(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 (styleRunArrays[style]->getLimit(currentRun[style]) == fRunLimits[run]) {
+ currentRun[style] += 1;
+ }
+ }
+
+ run += 1;
+ }
+
+ fRunCount = run;
+ LE_DELETE_ARRAY(currentRun);
+}
+
+StyleRuns::~StyleRuns()
+{
+ fRunCount = 0;
+
+ LE_DELETE_ARRAY(fStyleIndices);
+ fStyleIndices = NULL;
+
+ LE_DELETE_ARRAY(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.
+ */
+static const le_bool complexTable[scriptCodeCount] = {
+ FALSE , /* Zyyy */
+ FALSE, /* Qaai */
+ TRUE, /* Arab */
+ FALSE, /* Armn */
+ TRUE, /* Beng */
+ FALSE, /* Bopo */
+ FALSE, /* Cher */
+ FALSE, /* Copt=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 */
+ FALSE, /* Brai */
+ FALSE, /* Cprt */
+ FALSE, /* Limb */
+ FALSE, /* Linb */
+ FALSE, /* Osma */
+ FALSE, /* Shaw */
+ FALSE, /* Tale */
+ FALSE, /* Ugar */
+ FALSE, /* Hrkt */
+ FALSE, /* Bugi */
+ FALSE, /* Glag */
+ FALSE, /* Khar */
+ FALSE, /* Sylo */
+ FALSE, /* Talu */
+ FALSE, /* Tfng */
+ FALSE, /* Xpeo */
+ FALSE, /* Bali */
+ FALSE, /* Batk */
+ FALSE, /* Blis */
+ FALSE, /* Brah */
+ FALSE, /* Cham */
+ FALSE, /* Cirt */
+ FALSE, /* Cyrs */
+ FALSE, /* Egyd */
+ FALSE, /* Egyh */
+ FALSE, /* Egyp */
+ FALSE, /* Geok */
+ FALSE, /* Hans */
+ FALSE, /* Hant */
+ FALSE, /* Hmng */
+ FALSE, /* Hung */
+ FALSE, /* Inds */
+ FALSE, /* Java */
+ FALSE, /* Kali */
+ FALSE, /* Latf */
+ FALSE, /* Latg */
+ FALSE, /* Lepc */
+ FALSE, /* Lina */
+ FALSE, /* Mand */
+ FALSE, /* Maya */
+ FALSE, /* Mero */
+ FALSE, /* Nkoo */
+ FALSE, /* Orkh */
+ FALSE, /* Perm */
+ FALSE, /* Phag */
+ FALSE, /* Phnx */
+ FALSE, /* Plrd */
+ FALSE, /* Roro */
+ FALSE, /* Sara */
+ FALSE, /* Syre */
+ FALSE, /* Syrj */
+ FALSE, /* Syrn */
+ FALSE, /* Teng */
+ FALSE, /* Taii */
+ FALSE, /* Visp */
+ FALSE, /* Xsux */
+ FALSE, /* Zxxx */
+ FALSE, /* Zzzz */
+ FALSE, /* Cari */
+ FALSE, /* Jpan */
+ FALSE, /* Lana */
+ FALSE, /* Lyci */
+ FALSE, /* Lydi */
+ FALSE, /* Olck */
+ FALSE, /* Rjng */
+ FALSE, /* Saur */
+ FALSE, /* Sgnw */
+ FALSE, /* Sund */
+ FALSE, /* Moon */
+ FALSE /* Mtei */
+};
+
+
+const char ParagraphLayout::fgClassID = 0;
+
+/*
+ * How to deal with composite fonts:
+ *
+ * Don't store the client's FontRuns; we'll need to compute sub-font FontRuns using Doug's
+ * LEFontInstance method. Do that by intersecting the client's FontRuns with fScriptRuns. Use
+ * that to compute fFontRuns, and then intersect fFontRuns, fScriptRuns and fLevelRuns. Doing
+ * it in this order means we do a two-way intersection and a three-way intersection.
+ *
+ * An optimization would be to only do this if there's at least one composite font...
+ *
+ * Other notes:
+ *
+ * * Return the sub-fonts as the run fonts... could keep the mapping back to the client's FontRuns
+ * but that probably makes it more complicated of everyone...
+ *
+ * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API.
+ *
+ * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels?
+ *
+ */
+ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
+ const FontRuns *fontRuns,
+ const ValueRuns *levelRuns,
+ const ValueRuns *scriptRuns,
+ const LocaleRuns *localeRuns,
+ UBiDiLevel paragraphLevel, le_bool vertical,
+ LEErrorCode &status)
+ : fChars(chars), fCharCount(count),
+ fFontRuns(NULL), fLevelRuns(levelRuns), fScriptRuns(scriptRuns), fLocaleRuns(localeRuns),
+ fVertical(vertical), fClientLevels(TRUE), fClientScripts(TRUE), fClientLocales(TRUE), fEmbeddingLevels(NULL),
+ fAscent(0), fDescent(0), fLeading(0),
+ fGlyphToCharMap(NULL), fCharToMinGlyphMap(NULL), fCharToMaxGlyphMap(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)
+{
+
+ if (LE_FAILURE(status)) {
+ fCharCount = -1;
+ return;
+ }
+
+ // FIXME: should check the limit arrays for consistency...
+
+ computeLevels(paragraphLevel);
+
+ if (scriptRuns == NULL) {
+ computeScripts();
+ }
+
+ if (localeRuns == NULL) {
+ computeLocales();
+ }
+
+ computeSubFonts(fontRuns, status);
+
+ if (LE_FAILURE(status)) {
+ //other stuff?
+ fCharCount = -1;
+ return;
+ }
+
+ // now intersect the font, direction and script runs...
+ const RunArray *styleRunArrays[] = {fFontRuns, fLevelRuns, fScriptRuns, fLocaleRuns};
+ le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0];
+ StyleRuns styleRuns(styleRunArrays, styleCount);
+ LEErrorCode layoutStatus = LE_NO_ERROR;
+
+ fStyleRunCount = styleRuns.getRuns(NULL, NULL);
+
+ fStyleRunLimits = LE_NEW_ARRAY(le_int32, fStyleRunCount);
+ fStyleIndices = LE_NEW_ARRAY(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 = LE_NEW_ARRAY(StyleRunInfo, fStyleRunCount);
+
+ fGlyphCount = 0;
+ for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
+ fStyleRunInfo[run].font = fFontRuns->getFont(styleIndices[0]);
+ fStyleRunInfo[run].runBase = runStart;
+ fStyleRunInfo[run].runLimit = fStyleRunLimits[run];
+ fStyleRunInfo[run].script = (UScriptCode) fScriptRuns->getValue(styleIndices[2]);
+ fStyleRunInfo[run].locale = fLocaleRuns->getLocale(styleIndices[3]);
+ fStyleRunInfo[run].level = (UBiDiLevel) fLevelRuns->getValue(styleIndices[1]);
+ fStyleRunInfo[run].glyphBase = fGlyphCount;
+
+ fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font,
+ fStyleRunInfo[run].script, getLanguageCode(fStyleRunInfo[run].locale), 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
+ // character array. Swap the glyph width and glyph-to-char arrays into logical order.
+ // Finally, fill in the char-to-glyph mappings.
+ fGlyphWidths = LE_NEW_ARRAY(float, fGlyphCount);
+ fGlyphToCharMap = LE_NEW_ARRAY(le_int32, fGlyphCount + 1);
+ fCharToMinGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
+ fCharToMaxGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
+
+ le_int32 glyph;
+
+ 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;
+
+ fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount);
+ fStyleRunInfo[run].positions = LE_NEW_ARRAY(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];
+ }
+
+ if ((fStyleRunInfo[run].level & 1) != 0) {
+ LXUtilities::reverse(&fGlyphWidths[glyphBase], glyphCount);
+ LXUtilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount);
+ }
+
+ runStart = fStyleRunLimits[run];
+
+ delete engine;
+ fStyleRunInfo[run].engine = NULL;
+ }
+
+ fGlyphToCharMap[fGlyphCount] = fCharCount;
+
+ for (glyph = fGlyphCount - 1; glyph >= 0; glyph -= 1) {
+ le_int32 ch = fGlyphToCharMap[glyph];
+
+ fCharToMinGlyphMap[ch] = glyph;
+ }
+
+ fCharToMinGlyphMap[fCharCount] = fGlyphCount;
+
+ for (glyph = 0; glyph < fGlyphCount; glyph += 1) {
+ le_int32 ch = fGlyphToCharMap[glyph];
+
+ fCharToMaxGlyphMap[ch] = glyph;
+ }
+
+ fCharToMaxGlyphMap[fCharCount] = fGlyphCount;
+}
+
+ParagraphLayout::~ParagraphLayout()
+{
+ delete (FontRuns *) fFontRuns;
+
+ if (! fClientLevels) {
+ delete (ValueRuns *) fLevelRuns;
+ fLevelRuns = NULL;
+
+ fClientLevels = TRUE;
+ }
+
+ if (! fClientScripts) {
+ delete (ValueRuns *) fScriptRuns;
+ fScriptRuns = NULL;
+
+ fClientScripts = TRUE;
+ }
+
+ if (! fClientLocales) {
+ delete (LocaleRuns *) fLocaleRuns;
+ fLocaleRuns = NULL;
+
+ fClientLocales = TRUE;
+ }
+
+ if (fEmbeddingLevels != NULL) {
+ LE_DELETE_ARRAY(fEmbeddingLevels);
+ fEmbeddingLevels = NULL;
+ }
+
+ if (fGlyphToCharMap != NULL) {
+ LE_DELETE_ARRAY(fGlyphToCharMap);
+ fGlyphToCharMap = NULL;
+ }
+
+ if (fCharToMinGlyphMap != NULL) {
+ LE_DELETE_ARRAY(fCharToMinGlyphMap);
+ fCharToMinGlyphMap = NULL;
+ }
+
+ if (fCharToMaxGlyphMap != NULL) {
+ LE_DELETE_ARRAY(fCharToMaxGlyphMap);
+ fCharToMaxGlyphMap = NULL;
+ }
+
+ if (fGlyphWidths != NULL) {
+ LE_DELETE_ARRAY(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;
+
+ LE_DELETE_ARRAY(fStyleRunLimits);
+ LE_DELETE_ARRAY(fStyleIndices);
+
+ for (run = 0; run < fStyleRunCount; run += 1) {
+ LE_DELETE_ARRAY(fStyleRunInfo[run].glyphs);
+ LE_DELETE_ARRAY(fStyleRunInfo[run].positions);
+
+ fStyleRunInfo[run].glyphs = NULL;
+ fStyleRunInfo[run].positions = NULL;
+ }
+
+ LE_DELETE_ARRAY(fStyleRunInfo);
+
+ fStyleRunLimits = NULL;
+ fStyleIndices = NULL;
+ fStyleRunInfo = NULL;
+ fStyleRunCount = 0;
+ }
+
+ if (fBreakIterator != NULL) {
+ delete fBreakIterator;
+ fBreakIterator = NULL;
+ }
+}
+
+
+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);
+ le_bool result = FALSE;
+
+ while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) {
+ if (isComplex(scriptCode)) {
+ result = TRUE;
+ break;
+ }
+ }
+
+ uscript_closeRun(sr);
+ return result;
+}
+
+le_int32 ParagraphLayout::getAscent() const
+{
+ if (fAscent <= 0 && fCharCount > 0) {
+ ((ParagraphLayout *) this)->computeMetrics();
+ }
+
+ return fAscent;
+}
+
+le_int32 ParagraphLayout::getDescent() const
+{
+ if (fAscent <= 0 && fCharCount > 0) {
+ ((ParagraphLayout *) this)->computeMetrics();
+ }
+
+ return fDescent;
+}
+
+le_int32 ParagraphLayout::getLeading() const
+{
+ if (fAscent <= 0 && fCharCount > 0) {
+ ((ParagraphLayout *) this)->computeMetrics();
+ }
+
+ return fLeading;
+}
+
+ParagraphLayout::Line *ParagraphLayout::nextLine(float width)
+{
+ if (fLineEnd >= fCharCount) {
+ return NULL;
+ }
+
+ fLineStart = fLineEnd;
+
+ if (width > 0) {
+ le_int32 glyph = fCharToMinGlyphMap[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 this break is at or before the last one,
+ // find a glyph, starting at the one which didn't
+ // fit, that produces a break after the last one.
+ while (fLineEnd <= fLineStart) {
+ fLineEnd = fGlyphToCharMap[glyph++];
+ }
+ } else {
+ fLineEnd = fCharCount;
+ }
+
+ return computeVisualRuns();
+}
+
+void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel)
+{
+ UErrorCode bidiStatus = U_ZERO_ERROR;
+
+ if (fLevelRuns != NULL) {
+ le_int32 ch;
+ le_int32 run;
+
+ fEmbeddingLevels = LE_NEW_ARRAY(UBiDiLevel, fCharCount);
+
+ for (ch = 0, run = 0; run < fLevelRuns->getCount(); run += 1) {
+ UBiDiLevel runLevel = (UBiDiLevel) fLevelRuns->getValue(run) | UBIDI_LEVEL_OVERRIDE;
+ le_int32 runLimit = fLevelRuns->getLimit(run);
+
+ while (ch < runLimit) {
+ fEmbeddingLevels[ch++] = runLevel;
+ }
+ }
+ }
+
+ fParaBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
+ ubidi_setPara(fParaBidi, fChars, fCharCount, paragraphLevel, fEmbeddingLevels, &bidiStatus);
+
+ if (fLevelRuns == NULL) {
+ le_int32 levelRunCount = ubidi_countRuns(fParaBidi, &bidiStatus);
+ ValueRuns *levelRuns = new ValueRuns(levelRunCount);
+
+ le_int32 logicalStart = 0;
+ le_int32 run;
+ le_int32 limit;
+ UBiDiLevel level;
+
+ for (run = 0; run < levelRunCount; run += 1) {
+ ubidi_getLogicalRun(fParaBidi, logicalStart, &limit, &level);
+ levelRuns->add(level, limit);
+ logicalStart = limit;
+ }
+
+ fLevelRuns = levelRuns;
+ fClientLevels = FALSE;
+ }
+}
+
+void ParagraphLayout::computeScripts()
+{
+ UErrorCode scriptStatus = U_ZERO_ERROR;
+ UScriptRun *sr = uscript_openRun(fChars, fCharCount, &scriptStatus);
+ ValueRuns *scriptRuns = new ValueRuns(0);
+ le_int32 limit;
+ UScriptCode script;
+
+ while (uscript_nextRun(sr, NULL, &limit, &script)) {
+ scriptRuns->add(script, limit);
+ }
+
+ uscript_closeRun(sr);
+
+ fScriptRuns = scriptRuns;
+ fClientScripts = FALSE;
+}
+
+void ParagraphLayout::computeLocales()
+{
+ LocaleRuns *localeRuns = new LocaleRuns(0);
+ const Locale *defaultLocale = &Locale::getDefault();
+
+ localeRuns->add(defaultLocale, fCharCount);
+
+ fLocaleRuns = localeRuns;
+ fClientLocales = FALSE;
+}
+
+void ParagraphLayout::computeSubFonts(const FontRuns *fontRuns, LEErrorCode &status)
+{
+ if (LE_FAILURE(status)) {
+ return;
+ }
+
+ const RunArray *styleRunArrays[] = {fontRuns, fScriptRuns};
+ le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0];
+ StyleRuns styleRuns(styleRunArrays, styleCount);
+ le_int32 styleRunCount = styleRuns.getRuns(NULL, NULL);
+ le_int32 *styleRunLimits = LE_NEW_ARRAY(le_int32, styleRunCount);
+ le_int32 *styleIndices = LE_NEW_ARRAY(le_int32, styleRunCount * styleCount);
+ FontRuns *subFontRuns = new FontRuns(0);
+ le_int32 run, offset, *si;
+
+ styleRuns.getRuns(styleRunLimits, styleIndices);
+
+ si = styleIndices;
+ offset = 0;
+
+ for (run = 0; run < styleRunCount; run += 1) {
+ const LEFontInstance *runFont = fontRuns->getFont(si[0]);
+ le_int32 script = fScriptRuns->getValue(si[1]);
+
+ while (offset < styleRunLimits[run]) {
+ const LEFontInstance *subFont = runFont->getSubFont(fChars, &offset, styleRunLimits[run], script, status);
+
+ if (LE_FAILURE(status)) {
+ delete subFontRuns;
+ goto cleanUp;
+ }
+
+ subFontRuns->add(subFont, offset);
+ }
+
+ si += styleCount;
+ }
+
+ fFontRuns = subFontRuns;
+
+cleanUp:
+ LE_DELETE_ARRAY(styleIndices);
+ LE_DELETE_ARRAY(styleRunLimits);
+}
+
+void ParagraphLayout::computeMetrics()
+{
+ le_int32 i, count = fFontRuns->getCount();
+ le_int32 maxDL = 0;
+
+ for (i = 0; i < count; i += 1) {
+ const LEFontInstance *font = fFontRuns->getFont(i);
+ le_int32 ascent = font->getAscent();
+ le_int32 descent = font->getDescent();
+ le_int32 leading = font->getLeading();
+ le_int32 dl = descent + leading;
+
+ if (ascent > fAscent) {
+ fAscent = ascent;
+ }
+
+ if (descent > fDescent) {
+ fDescent = descent;
+ }
+
+ if (leading > fLeading) {
+ fLeading = leading;
+ }
+
+ if (dl > maxDL) {
+ maxDL = dl;
+ }
+ }
+
+ fLeading = maxDL - fDescent;
+}
+
+#if 1
+struct LanguageMap
+{
+ const char *localeCode;
+ le_int32 languageCode;
+};
+
+static const LanguageMap languageMap[] =
+{
+ {"afr", afkLanguageCode}, // Afrikaans
+ {"ara", araLanguageCode}, // Arabic
+ {"asm", asmLanguageCode}, // Assamese
+ {"bel", belLanguageCode}, // Belarussian
+ {"ben", benLanguageCode}, // Bengali
+ {"bod", tibLanguageCode}, // Tibetan
+ {"bul", bgrLanguageCode}, // Bulgarian
+ {"cat", catLanguageCode}, // Catalan
+ {"ces", csyLanguageCode}, // Czech
+ {"che", cheLanguageCode}, // Chechen
+ {"cop", copLanguageCode}, // Coptic
+ {"cym", welLanguageCode}, // Welsh
+ {"dan", danLanguageCode}, // Danish
+ {"deu", deuLanguageCode}, // German
+ {"dzo", dznLanguageCode}, // Dzongkha
+ {"ell", ellLanguageCode}, // Greek
+ {"eng", engLanguageCode}, // English
+ {"est", etiLanguageCode}, // Estonian
+ {"eus", euqLanguageCode}, // Basque
+ {"fas", farLanguageCode}, // Farsi
+ {"fin", finLanguageCode}, // Finnish
+ {"fra", fraLanguageCode}, // French
+ {"gle", gaeLanguageCode}, // Irish Gaelic
+ {"guj", gujLanguageCode}, // Gujarati
+ {"hau", hauLanguageCode}, // Hausa
+ {"heb", iwrLanguageCode}, // Hebrew
+ {"hin", hinLanguageCode}, // Hindi
+ {"hrv", hrvLanguageCode}, // Croatian
+ {"hun", hunLanguageCode}, // Hungarian
+ {"hye", hyeLanguageCode}, // Armenian
+ {"ind", indLanguageCode}, // Indonesian
+ {"ita", itaLanguageCode}, // Italian
+ {"jpn", janLanguageCode}, // Japanese
+ {"kan", kanLanguageCode}, // Kannada
+ {"kas", kshLanguageCode}, // Kashmiri
+ {"khm", khmLanguageCode}, // Khmer
+ {"kok", kokLanguageCode}, // Konkani
+ {"kor", korLanguageCode}, // Korean
+// {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
+ {"mal", mlrLanguageCode}, // Malayalam - Reformed
+ {"mar", marLanguageCode}, // Marathi
+ {"mlt", mtsLanguageCode}, // Maltese
+ {"mni", mniLanguageCode}, // Manipuri
+ {"mon", mngLanguageCode}, // Mongolian
+ {"nep", nepLanguageCode}, // Nepali
+ {"ori", oriLanguageCode}, // Oriya
+ {"pol", plkLanguageCode}, // Polish
+ {"por", ptgLanguageCode}, // Portuguese
+ {"pus", pasLanguageCode}, // Pashto
+ {"ron", romLanguageCode}, // Romanian
+ {"rus", rusLanguageCode}, // Russian
+ {"san", sanLanguageCode}, // Sanskrit
+ {"sin", snhLanguageCode}, // Sinhalese
+ {"slk", skyLanguageCode}, // Slovak
+ {"snd", sndLanguageCode}, // Sindhi
+ {"slv", slvLanguageCode}, // Slovenian
+ {"spa", espLanguageCode}, // Spanish
+ {"sqi", sqiLanguageCode}, // Albanian
+ {"srp", srbLanguageCode}, // Serbian
+ {"swe", sveLanguageCode}, // Swedish
+ {"syr", syrLanguageCode}, // Syriac
+ {"tam", tamLanguageCode}, // Tamil
+ {"tel", telLanguageCode}, // Telugu
+ {"tha", thaLanguageCode}, // Thai
+ {"tur", trkLanguageCode}, // Turkish
+ {"urd", urdLanguageCode}, // Urdu
+ {"yid", jiiLanguageCode}, // Yiddish
+// {"zhp", zhpLanguageCode}, // Chinese - Phonetic
+ {"zho", zhsLanguageCode}, // Chinese
+ {"zho_CHN", zhsLanguageCode}, // Chinese - China
+ {"zho_HKG", zhsLanguageCode}, // Chinese - Hong Kong
+ {"zho_MAC", zhtLanguageCode}, // Chinese - Macao
+ {"zho_SGP", zhsLanguageCode}, // Chinese - Singapore
+ {"zho_TWN", zhtLanguageCode} // Chinese - Taiwan
+};
+
+static const le_int32 languageMapCount = ARRAY_SIZE(languageMap);
+
+le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
+{
+ char code[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ const char *language = locale->getISO3Language();
+ const char *country = locale->getISO3Country();
+
+ uprv_strcat(code, language);
+
+ if ((uprv_strcmp(language, "zho") == 0) && country != NULL) {
+ uprv_strcat(code, "_");
+ uprv_strcat(code, country);
+ }
+
+ for (le_int32 i = 0; i < languageMapCount; i += 1) {
+ if (uprv_strcmp(code, languageMap[i].localeCode) == 0) {
+ return languageMap[i].languageCode;
+ }
+ }
+
+ return nullLanguageCode;
+}
+#else
+
+// TODO - dummy implementation for right now...
+le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
+{
+ return nullLanguageCode;
+}
+#endif
+
+le_bool ParagraphLayout::isComplex(UScriptCode script)
+{
+ if (script < 0 || script >= (UScriptCode) scriptCodeCount) {
+ return FALSE;
+ }
+
+ return complexTable[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("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);
+}
+
+ParagraphLayout::Line *ParagraphLayout::computeVisualRuns()
+{
+ UErrorCode bidiStatus = U_ZERO_ERROR;
+ le_int32 dirRunCount, visualRun;
+
+ fVisualRunLastX = 0;
+ fVisualRunLastY = 0;
+ fFirstVisualRun = getCharRun(fLineStart);
+ fLastVisualRun = getCharRun(fLineEnd - 1);
+
+ if (fLineBidi == NULL) {
+ fLineBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
+ }
+
+ ubidi_setLine(fParaBidi, fLineStart, fLineEnd, fLineBidi, &bidiStatus);
+ dirRunCount = ubidi_countRuns(fLineBidi, &bidiStatus);
+
+ Line *line = new Line();
+
+ for (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);
+ le_int32 startRun = (runDirection == UBIDI_LTR)? firstRun : lastRun;
+ le_int32 stopRun = (runDirection == UBIDI_LTR)? lastRun + 1 : firstRun - 1;
+ le_int32 dir = (runDirection == UBIDI_LTR)? 1 : -1;
+
+ for (run = startRun; run != stopRun; run += dir) {
+ le_int32 firstChar = (run == firstRun)? runStart : fStyleRunInfo[run].runBase;
+ le_int32 lastChar = (run == lastRun)? runEnd : fStyleRunInfo[run].runLimit - 1;
+
+ appendRun(line, run, firstChar, lastChar);
+ }
+ }
+
+ return line;
+}
+
+void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_int32 firstChar, le_int32 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 minGlyph = fCharToMinGlyphMap[ch];
+ le_int32 maxGlyph = fCharToMaxGlyphMap[ch];
+
+ if (minGlyph < leftGlyph) {
+ leftGlyph = minGlyph;
+ }
+
+ if (maxGlyph > rightGlyph) {
+ rightGlyph = maxGlyph;
+ }
+ }
+
+ 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;
+ }
+
+ // Set the position bias for the glyphs. If we're at the start of
+ // a line, we want the first glyph to be at x = 0, even if it comes
+ // from the middle of a layout. If we've got a right-to-left run, we
+ // want the left-most glyph to start at the final x position of the
+ // previous run, even though this glyph may be in the middle of the
+ // run.
+ fVisualRunLastX -= fStyleRunInfo[run].positions[leftGlyph * 2];
+
+ // Make rightGlyph be the glyph just to the right of
+ // the run's glyphs
+ rightGlyph += 1;
+
+ UBiDiDirection direction = ((fStyleRunInfo[run].level & 1) == 0)? UBIDI_LTR : UBIDI_RTL;
+ le_int32 glyphCount = rightGlyph - leftGlyph;
+ LEGlyphID *glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount);
+ float *positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
+ le_int32 *glyphToCharMap = LE_NEW_ARRAY(le_int32, glyphCount);
+
+ LE_ARRAY_COPY(glyphs, &fStyleRunInfo[run].glyphs[leftGlyph], glyphCount);
+
+ 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 ((fStyleRunInfo[run].level & 1) == 0) {
+ for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
+ glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph];
+ }
+ } else {
+ // Because fGlyphToCharMap is stored in logical order to facilitate line breaking,
+ // we need to map the physical glyph indices to logical indices while we copy the
+ // character indices.
+ le_int32 base = glyphBase + fStyleRunInfo[run].glyphCount - 1;
+
+ for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
+ glyphToCharMap[outGlyph] = fGlyphToCharMap[base - inGlyph];
+ }
+ }
+
+ line->append(fStyleRunInfo[run].font, direction, glyphCount, glyphs, positions, glyphToCharMap);
+}
+
+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;
+}
+
+
+const char ParagraphLayout::Line::fgClassID = 0;
+
+#define INITIAL_RUN_CAPACITY 4
+#define RUN_CAPACITY_GROW_LIMIT 16
+
+ParagraphLayout::Line::~Line()
+{
+ le_int32 i;
+
+ for (i = 0; i < fRunCount; i += 1) {
+ delete fRuns[i];
+ }
+
+ LE_DELETE_ARRAY(fRuns);
+}
+
+le_int32 ParagraphLayout::Line::getAscent() const
+{
+ if (fAscent <= 0) {
+ ((ParagraphLayout::Line *)this)->computeMetrics();
+ }
+
+ return fAscent;
+}
+
+le_int32 ParagraphLayout::Line::getDescent() const
+{
+ if (fAscent <= 0) {
+ ((ParagraphLayout::Line *)this)->computeMetrics();
+ }
+
+ return fDescent;
+}
+
+le_int32 ParagraphLayout::Line::getLeading() const
+{
+ if (fAscent <= 0) {
+ ((ParagraphLayout::Line *)this)->computeMetrics();
+ }
+
+ return fLeading;
+}
+
+le_int32 ParagraphLayout::Line::getWidth() const
+{
+ const VisualRun *lastRun = getVisualRun(fRunCount - 1);
+
+ if (lastRun == NULL) {
+ return 0;
+ }
+
+ le_int32 glyphCount = lastRun->getGlyphCount();
+ const float *positions = lastRun->getPositions();
+
+ return (le_int32) positions[glyphCount * 2];
+}
+
+const ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(le_int32 runIndex) const
+{
+ if (runIndex < 0 || runIndex >= fRunCount) {
+ return NULL;
+ }
+
+ return fRuns[runIndex];
+}
+
+void ParagraphLayout::Line::append(const LEFontInstance *font, UBiDiDirection direction, le_int32 glyphCount,
+ const LEGlyphID glyphs[], const float positions[], const le_int32 glyphToCharMap[])
+{
+ if (fRunCount >= fRunCapacity) {
+ if (fRunCapacity == 0) {
+ fRunCapacity = INITIAL_RUN_CAPACITY;
+ fRuns = LE_NEW_ARRAY(ParagraphLayout::VisualRun *, fRunCapacity);
+ } else {
+ fRunCapacity += (fRunCapacity < RUN_CAPACITY_GROW_LIMIT? fRunCapacity : RUN_CAPACITY_GROW_LIMIT);
+ fRuns = (ParagraphLayout::VisualRun **) LE_GROW_ARRAY(fRuns, fRunCapacity);
+ }
+ }
+
+ fRuns[fRunCount++] = new ParagraphLayout::VisualRun(font, direction, glyphCount, glyphs, positions, glyphToCharMap);
+}
+
+void ParagraphLayout::Line::computeMetrics()
+{
+ le_int32 maxDL = 0;
+
+ for (le_int32 i = 0; i < fRunCount; i += 1) {
+ le_int32 ascent = fRuns[i]->getAscent();
+ le_int32 descent = fRuns[i]->getDescent();
+ le_int32 leading = fRuns[i]->getLeading();
+ le_int32 dl = descent + leading;
+
+ if (ascent > fAscent) {
+ fAscent = ascent;
+ }
+
+ if (descent > fDescent) {
+ fDescent = descent;
+ }
+
+ if (leading > fLeading) {
+ fLeading = leading;
+ }
+
+ if (dl > maxDL) {
+ maxDL = dl;
+ }
+ }
+
+ fLeading = maxDL - fDescent;
+}
+
+const char ParagraphLayout::VisualRun::fgClassID = 0;
+
+ParagraphLayout::VisualRun::~VisualRun()
+{
+ LE_DELETE_ARRAY(fGlyphToCharMap);
+ LE_DELETE_ARRAY(fPositions);
+ LE_DELETE_ARRAY(fGlyphs);
+}
+
+U_NAMESPACE_END
+
+#endif
+
+