/////////////////////////////////////////////////////////////////////////////// // GetHebrewCharCluster // // We read a single character cluster. A cluster is defined by a set of // table-based combining rules whereby each character in the Hebrew block // is given a category of 'none', 'spacing', 'non-spacing', or 'dagesh'. // // The return value is the number of chars eaten; clusterSize is the number of // chars generated. In the large majority of cases, they will be equal. // eastl_size_t Typesetter::GetHebrewCharCluster(eastl_size_t i, eastl_size_t iCharEnd, Char* pCharCluster, eastl_size_t& clusterSize) { EA_ASSERT((i < iCharEnd) && (iCharEnd <= mLineLayout.mCharArray.size())); clusterSize = 0; for(const Char* p = &mLineLayout.mCharArray[i], *pEnd = &mLineLayout.mCharArray[iCharEnd]; (p < pEnd) && (clusterSize < kMaxHebrewCharClusterSize); ++p) { const Char c = *p; // If the character is non-Hebrew, it can't compose with Hebrew. // If the character is the first and it is a diacritic, it can't compose with succeeding Hebrew. const bool bCharIsHebrew = IsCharHebrew(c); if(!bCharIsHebrew || ((clusterSize == 0) && ((1 << gHebrewCharClass[c - 0x0590]) & (kHCCFNonSpacing | kHCCFDagesh)))) { if(clusterSize == 0) // If this is the first char... { if(bCharIsHebrew) pCharCluster[clusterSize++] = c; else return GetGeneralCharCluster(i, iCharEnd, pCharCluster, clusterSize); } break; } else { if(clusterSize == 0) pCharCluster[clusterSize++] = c; EA_ASSERT(IsCharHebrew(*pCharCluster) && IsCharHebrew(c)); const unsigned prevCharClass = gHebrewCharClass[*pCharCluster - 0x0590]; const unsigned curCharClass = gHebrewCharClass[c - 0x0590]; EA_ASSERT((prevCharClass < 4) && (curCharClass < 4)); if((clusterSize == 0) || gHebrewClusterTable[prevCharClass][curCharClass]) // If the char is the first char or can pair with the first char... pCharCluster[clusterSize++] = c; else { EA_ASSERT(clusterSize > 0); break; } } } EA_ASSERT(clusterSize > 0); return clusterSize; // We always have a 1:1 relationship between chars in the source and chars in the generated cluster. }
// ShapeArabic // void Typesetter::ShapeArabic(eastl_size_t iCharBegin, eastl_size_t iCharEnd) { EA_ASSERT(iCharEnd <= mLineLayout.mCharArray.size()); const OTF* const pOTF = mLineLayout.mAnalysisInfoArray[0].mpFont->GetOTF(); // We more or less need to have OpenType font information in order to correctly display Arabic. if(pOTF && pOTF->IsScriptSupported("arab")) { const eastl_size_t iGlyphBegin = mLineLayout.GetGlyphIndexFromCharIndex(iCharBegin); for(eastl_size_t i = iCharBegin, charCount = 0; i < iCharEnd; i += charCount) { const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[i]; Char pCharCluster[kMaxArabicCharClusterSize]; eastl_size_t charClusterSize = 0; charCount = GetGeneralCharCluster(i, iCharEnd, pCharCluster, charClusterSize); for(eastl_size_t c = 0, charsEaten = 0, glyphCount = 0, glyphCountPrev = 0; c < charClusterSize; c += charsEaten) { GlyphId pGlyphIdArray[kMaxArabicGlyphClusterSize]; charsEaten = GetGlyphsForChar(pCharCluster + c, charClusterSize - c, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); AppendArabicGlyphCluster(iCharBegin, charCount, pCharCluster + c, charsEaten, pGlyphIdArray + glyphCountPrev, glyphCount - glyphCountPrev, pAnalysisInfo->mnBidiLevel, pOTF); glyphCountPrev = glyphCount; } } // Do OpenType substitutions. FeatureLookupArray featureLookupArray; SetupArabicGsubLookup(featureLookupArray, pOTF); AssignArabicCharProperties(&mLineLayout.mCharArray[iCharBegin], iCharEnd - iCharBegin, &mLineLayout.mGlyphInfoArray[iGlyphBegin]); DoGlyphSubstitution(mLineLayout, iGlyphBegin, featureLookupArray, pOTF); CompleteLineLayoutArrays(iCharBegin, iCharEnd, iGlyphBegin); PlaceGeneralGlyphCluster(iCharBegin, iCharEnd - iCharBegin); } else { // Generic fallback. Arabic text will look wrong, but at least something will be displayed. ShapeGeneral(iCharBegin, iCharEnd); } }
/////////////////////////////////////////////////////////////////////////////// // ShapeGeneral // // This function shapes general LTR (left to right) text. It should support // a number of scripts reasonably well, including all modern Western scripts. // This function expects text to be entirely LTR and will not work correctly // if there is any RTL-ordered text in it. // // Runs are positioned in their own coordinate system. If multiple runs will // be strung together on the same line, the runs will have to be offset. // // If you have any questions about this function, as Paul Pedriana. Some of the // details are subtle and may not be immediately obvious. // void Typesetter::ShapeGeneral(eastl_size_t iCharBegin, eastl_size_t iCharEnd) { Char pCharCluster[kMaxGeneralCharClusterSize]; EA_ASSERT(iCharEnd <= mLineLayout.mCharArray.size()); for(eastl_size_t i = iCharBegin, clusterSize, charCount; i < iCharEnd; i += charCount) { #ifdef EA_DEBUG memset(pCharCluster, 0, sizeof(pCharCluster)); #endif charCount = GetGeneralCharCluster(i, iCharEnd, pCharCluster, clusterSize); AppendGeneralCharCluster(i, charCount, pCharCluster, clusterSize); } }