+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
**********************************************************************
- * Copyright (C) 2002-2004, International Business Machines
+ * Copyright (C) 2002-2014, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*/
* BreakIterator...
*/
#include "layout/LETypes.h"
+#include "layout/LEScripts.h"
#include "layout/LELanguages.h"
#include "layout/LayoutEngine.h"
#include "layout/LEFontInstance.h"
#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:
* process, rather for all scripts which require
* complex processing for correct rendering.
*/
-static const le_bool complexTable[] = {
+static const le_bool complexTable[scriptCodeCount] = {
FALSE , /* Zyyy */
FALSE, /* Qaai */
TRUE, /* Arab */
TRUE, /* Beng */
FALSE, /* Bopo */
FALSE, /* Cher */
- FALSE, /* Qaac */
+ FALSE, /* Copt=Qaac */
FALSE, /* Cyrl */
FALSE, /* Dsrt */
TRUE, /* Deva */
FALSE, /* Shaw */
FALSE, /* Tale */
FALSE, /* Ugar */
- FALSE /* Hrkt */
+ 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 */
+ FALSE, /* Armi */
+ FALSE, /* Avst */
+ FALSE, /* Cakm */
+ FALSE, /* Kore */
+ FALSE, /* Kthi */
+ FALSE, /* Mani */
+ FALSE, /* Phli */
+ FALSE, /* Phlp */
+ FALSE, /* Phlv */
+ FALSE, /* Prti */
+ FALSE, /* Samr */
+ FALSE, /* Tavt */
+ FALSE, /* Zmth */
+ FALSE, /* Zsym */
+ FALSE, /* Bamu */
+ FALSE, /* Lisu */
+ FALSE, /* Nkgb */
+ FALSE /* Sarb */
};
const char ParagraphLayout::fgClassID = 0;
+static void fillMissingCharToGlyphMapValues(le_int32 *charToGlyphMap,
+ le_int32 charCount) {
+ le_int32 lastValidGlyph = -1;
+ le_int32 ch;
+ for (ch = 0; ch <= charCount; ch += 1) {
+ if (charToGlyphMap[ch] == -1) {
+ charToGlyphMap[ch] = lastValidGlyph;
+ } else {
+ lastValidGlyph = charToGlyphMap[ch];
+ }
+ }
+}
+
/*
* How to deal with composite fonts:
*
return;
}
+ (void)copyright; // Suppress unused variable warning.
+ (void)fVertical; // Suppress warning for unused field fVertical.
+
// FIXME: should check the limit arrays for consistency...
computeLevels(paragraphLevel);
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);
-
+ if ((fStyleRunLimits == NULL) || (fStyleIndices == NULL)) {
+ status = LE_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
styleRuns.getRuns(fStyleRunLimits, fStyleIndices);
// now build a LayoutEngine for each style run...
le_int32 run, runStart;
fStyleRunInfo = LE_NEW_ARRAY(StyleRunInfo, fStyleRunCount);
+ if (fStyleRunInfo == NULL) {
+ status = LE_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ else {
+ // initialize
+ for (run = 0; run < fStyleRunCount; run += 1) {
+ fStyleRunInfo[run].font = NULL;
+ fStyleRunInfo[run].runBase = 0;
+ fStyleRunInfo[run].runLimit = 0;
+ fStyleRunInfo[run].script = (UScriptCode)0;
+ fStyleRunInfo[run].locale = NULL;
+ fStyleRunInfo[run].level = 0;
+ fStyleRunInfo[run].glyphBase = 0;
+ fStyleRunInfo[run].engine = NULL;
+ fStyleRunInfo[run].glyphCount = 0;
+ fStyleRunInfo[run].glyphs = NULL;
+ fStyleRunInfo[run].positions = NULL;
+ }
+ }
fGlyphCount = 0;
for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font,
fStyleRunInfo[run].script, getLanguageCode(fStyleRunInfo[run].locale), layoutStatus);
+ if (LE_FAILURE(layoutStatus)) {
+ status = layoutStatus;
+ return;
+ }
fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount,
fStyleRunInfo[run].level & 1, 0, 0, layoutStatus);
+ if (LE_FAILURE(layoutStatus)) {
+ status = layoutStatus;
+ return;
+ }
runStart = fStyleRunLimits[run];
styleIndices += styleCount;
}
// 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.)
+ // 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
fGlyphToCharMap = LE_NEW_ARRAY(le_int32, fGlyphCount + 1);
fCharToMinGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
fCharToMaxGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
+ if ((fGlyphWidths == NULL) || (fGlyphToCharMap == NULL) ||
+ (fCharToMinGlyphMap == NULL) || (fCharToMaxGlyphMap == NULL)) {
+ status = LE_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
le_int32 glyph;
fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount);
fStyleRunInfo[run].positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
+ if ((fStyleRunInfo[run].glyphs == NULL) ||
+ (fStyleRunInfo[run].positions == NULL)) {
+ status = LE_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus);
+ if (LE_FAILURE(layoutStatus)) {
+ status = layoutStatus;
+ return;
+ }
+
engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus);
+ if (LE_FAILURE(layoutStatus)) {
+ status = layoutStatus;
+ return;
+ }
+
engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus);
+ if (LE_FAILURE(layoutStatus)) {
+ status = layoutStatus;
+ return;
+ }
for (glyph = 0; glyph < glyphCount; glyph += 1) {
fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2];
fGlyphToCharMap[fGlyphCount] = fCharCount;
+ // Initialize the char-to-glyph maps to -1 so that we can later figure out
+ // whether any of the entries in the map aren't filled in below.
+ le_int32 chIndex;
+ for (chIndex = 0; chIndex <= fCharCount; chIndex += 1) {
+ fCharToMinGlyphMap[chIndex] = -1;
+ fCharToMaxGlyphMap[chIndex] = -1;
+ }
+
for (glyph = fGlyphCount - 1; glyph >= 0; glyph -= 1) {
le_int32 ch = fGlyphToCharMap[glyph];
}
fCharToMaxGlyphMap[fCharCount] = fGlyphCount;
+
+ // Now fill in the missing values in the char-to-glyph maps.
+ fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount);
+ fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount);
}
ParagraphLayout::~ParagraphLayout()
}
}
-
+
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)) {
- return TRUE;
+ result = TRUE;
+ break;
}
}
- return FALSE;
+ uscript_closeRun(sr);
+ return result;
}
le_int32 ParagraphLayout::getAscent() const
return fLeading;
}
+le_bool ParagraphLayout::isDone() const
+{
+ return fLineEnd >= fCharCount;
+}
+
ParagraphLayout::Line *ParagraphLayout::nextLine(float width)
{
- if (fLineEnd >= fCharCount) {
+ if (isDone()) {
return NULL;
}
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
- {"snd", sndLanguageCode}, // Sindhi
{"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
return nullLanguageCode;
}
-#elif
+#else
// TODO - dummy implementation for right now...
le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
le_bool ParagraphLayout::isComplex(UScriptCode script)
{
- if (script < 0 || script >= USCRIPT_CODE_LIMIT) {
+ if (script < 0 || script >= (UScriptCode) scriptCodeCount) {
return FALSE;
}
// 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;
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 */;
+ 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];
+ fVisualRunLastY = positions[outGlyph - 1];
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];
+ // 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];
}
}
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];
}
#endif
-