]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/layoutex/ParagraphLayout.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / layoutex / ParagraphLayout.cpp
index c0316b4bbfb375da18040c2e10bee88a7590620c..9e525cf1c5219817cdb288bf1b03fa8d8f47a313 100644 (file)
@@ -1,6 +1,8 @@
+// © 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.
  **********************************************************************
  */
@@ -10,6 +12,7 @@
  * BreakIterator...
  */
 #include "layout/LETypes.h"
+#include "layout/LEScripts.h"
 #include "layout/LELanguages.h"
 #include "layout/LayoutEngine.h"
 #include "layout/LEFontInstance.h"
@@ -29,6 +32,9 @@ 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:
@@ -131,7 +137,7 @@ le_int32 StyleRuns::getRuns(le_int32 runLimits[], le_int32 styleIndices[])
  * 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 */
@@ -139,7 +145,7 @@ static const le_bool complexTable[] = {
     TRUE,   /* Beng */
     FALSE,  /* Bopo */
     FALSE,  /* Cher */
-    FALSE,  /* Qaac */
+    FALSE,  /* Copt=Qaac */
     FALSE,  /* Cyrl */
     FALSE,  /* Dsrt */
     TRUE,   /* Deva */
@@ -186,12 +192,104 @@ static const le_bool complexTable[] = {
     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:
  *
@@ -236,6 +334,9 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
         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);
@@ -261,12 +362,16 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
     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...
@@ -274,6 +379,26 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
     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) {
@@ -287,9 +412,17 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
 
         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;
@@ -297,7 +430,7 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
     }
 
     // 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
@@ -307,6 +440,11 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
     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;
 
@@ -317,10 +455,29 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
 
         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];
@@ -339,6 +496,14 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
 
     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];
 
@@ -354,6 +519,10 @@ ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
     }
 
     fCharToMaxGlyphMap[fCharCount] = fGlyphCount;
+
+    // Now fill in the missing values in the char-to-glyph maps.
+    fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount);
+    fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount);
 }
 
 ParagraphLayout::~ParagraphLayout()
@@ -444,20 +613,23 @@ 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
@@ -487,9 +659,14 @@ le_int32 ParagraphLayout::getLeading() const
     return fLeading;
 }
 
+le_bool ParagraphLayout::isDone() const
+{
+    return fLineEnd >= fCharCount;
+}
+
 ParagraphLayout::Line *ParagraphLayout::nextLine(float width)
 {
-    if (fLineEnd >= fCharCount) {
+    if (isDone()) {
         return NULL;
     }
 
@@ -687,30 +864,71 @@ struct LanguageMap
 
 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
@@ -745,7 +963,7 @@ le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
 
     return nullLanguageCode;
 }
-#elif
+#else
 
 // TODO - dummy implementation for right now...
 le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
@@ -756,7 +974,7 @@ 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;
     }
 
@@ -875,7 +1093,7 @@ void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_in
     // 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;
@@ -890,21 +1108,26 @@ void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_in
 
     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];
         }
     }
 
@@ -977,9 +1200,14 @@ le_int32 ParagraphLayout::Line::getLeading() const
 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];
 }
 
@@ -1051,4 +1279,3 @@ U_NAMESPACE_END
 
 #endif
 
-