/////////////////////////////////////////////////////////////////////////////// // AppendThaiCharCluster // eastl_size_t Typesetter::AppendThaiCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[iCharBegin], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxThaiGlyphClusterSize]; eastl_size_t glyphCount = 0; EA_ASSERT(clusterSize && ((iCharBegin + clusterSize) <= mLineLayout.mCharArray.size())); if(IsCharThaiLao(*pCharCluster)) // If the cluster is using Thai or Lao script... glyphCount = GetThaiGlyphs(iCharBegin, pCharCluster, clusterSize, pGlyphIdArray); else { // In this case we choose glyphs as if doing general glyph shaping. // We just convert the Unicode Chars to glyphs. for(eastl_size_t i = 0; i < clusterSize; ) i += GetGlyphsForChar(pCharCluster + i, clusterSize - i, &mLineLayout.mAnalysisInfoArray[i], pGlyphIdArray + glyphCount, glyphCount); } AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster, charCount, pGlyphIdArray, glyphCount, pAnalysisInfo->mnBidiLevel); PlaceGeneralGlyphCluster(iCharBegin, charCount); // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }
bool EA::IO::FixedMemoryStream::SetPosition( off_type nPosition, PositionType positionType ) { switch (positionType) { case kPositionTypeBegin: EA_ASSERT(nPosition >= 0); mnPosition = (size_type)nPosition; // We deal with negative positions below. break; case kPositionTypeCurrent: mnPosition = mnPosition + (size_type)nPosition; // We have a signed/unsigned match, but the math should work anyway. break; case kPositionTypeEnd: mnPosition = mnSize + nPosition; // We deal with invalid resulting positions below. break; } // Deal with out-of-bounds situations that result from the above. if (mnPosition > mnSize) { EA_ASSERT( mnPosition < (size_type(-1) / 2) ); mnPosition = mnSize; return false; } return true; }
/////////////////////////////////////////////////////////////////////////////// // GetThaiCharCluster // // 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::GetThaiCharCluster(eastl_size_t i, eastl_size_t iCharEnd, Char* pCharCluster, eastl_size_t& clusterSize) { EA_ASSERT((i < iCharEnd) && (iCharEnd <= mLineLayout.mCharArray.size())); Script clusterScript = kScriptUnknown; Char cPrev = 0; clusterSize = 0; for(const Char* p = &mLineLayout.mCharArray[i], *pEnd = &mLineLayout.mCharArray[iCharEnd]; (p < pEnd) && (clusterSize < kMaxThaiCharClusterSize); ++p) { const Char c = *p; const Script script = GetScriptFromChar(c); EA_ASSERT(script != kScriptUnknown); if(clusterScript == kScriptUnknown) clusterScript = script; if((script == clusterScript) && ((clusterSize == 0) || (GetThaiCharPairingResult(cPrev, c) == 'C'))) // If this is the first char of the cluster or is a composed char with the previous char(s)... { pCharCluster[clusterSize++] = c; cPrev = c; } else break; } EA_ASSERT(clusterSize > 0); return clusterSize; }
/////////////////////////////////////////////////////////////////////////////// // 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. }
//////////////////////////////////////////////////////////////////////////////// // EndElement // bool XmlWriter::EndElement( const char *psElementName ) { EA_ASSERT(mnIndentLevel > 0); mnIndentLevel--; switch (mnState) { case kStateProcessingInstruction: EA_FAIL_MESSAGE( "XmlWriter: Cannot close element because we are in a processing instruction." ); return false; case kStateElement: mnState = kStateChars; mbSimpleElement = false; return WriteText( "/>", 2 ); case kStateCDATA: CloseCurrentElement(); // Fall through. case kStateChars: if (!mbSimpleElement ) { // If the current element has no child elements... if (!WriteIndent() ) // Then write an indent before writing the element end. return false; } mbSimpleElement = false; return ( WriteText( "</", 2 ) && WriteText( psElementName, kSizeTypeNull ) && WriteText( ">", 1 ) ); } return false; }
// AppendArabicGlyphCluster // // This function differs from other versions of AppendXXXCluster in Typesetter // because this version updates only the GlyphArray and the GlyphInfoArray. // It does this because it needs to perform substitutions on the glyphs before // laying them out and making mappings between glyphs and the source Unicode chars. // void Typesetter::AppendArabicGlyphCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t charClusterSize, const GlyphId* pGlyphCluster, eastl_size_t glyphClusterSize, int embeddingLevel, const OTF* pOTF) { (void)charClusterSize; // Prevent compiler warnings. (void)iCharBegin; EA_ASSERT(charCount && charClusterSize && glyphClusterSize); for(eastl_size_t g = 0; g < glyphClusterSize; g++) { const GlyphId glyphId = pGlyphCluster[g]; // Add a GlyphId entry. This will refer to the glyph provided by the Font. mLineLayout.mGlyphArray.push_back(glyphId); // Add a GlyphInfo entry. This will store logical information about the glyph. mLineLayout.mGlyphInfoArray.push_back(); GlyphInfo& gi = mLineLayout.mGlyphInfoArray.back(); gi.mGJC = kGJCNone; // We set mGJC to something else later if and when we do justification. gi.mClusterPosition = (unsigned)g; // If we were to have multiple glyphs that are together visually, gi.mClusterSize = (unsigned)glyphClusterSize; // we would revise mClusterPosition and mClusterSize. We would need to gi.mClusterBreakable = 0; // do this revision later, when we have all glyphs shaped for a line or paragraph. gi.mCharCount = (unsigned)charCount; // gi.mCharCount currently has only two bits of data. gi.mDirection = embeddingLevel; // gi.mbGlyphIsObject = *pCharCluster != kCharOBJ; // gi.mOpenTypeLookupFlags = (uint8_t)pOTF->mGdef.mGlyphClassDef.GetGlyphClass(glyphId); } }
/////////////////////////////////////////////////////////////////////////////// // GetGeneralCharCluster // // We read just a single character cluster. For now in this general shaper we make // all clusters just a single char. Decoration chars are not yet recognized. // However, when we do read decoration chars, charCount and cluster count would // be incremented appropriately. It turns out that standalone Latin decoration // chars (e.g. 0x0300 Combining Grave Accent) are set to have negative x offsets // and zero advance, so they don't need to be put into a char cluster in order // to work. // // 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::GetGeneralCharCluster(eastl_size_t iCharBegin, eastl_size_t iCharEnd, Char* pCharCluster, eastl_size_t& clusterSize) { (void)iCharEnd; // Prevent compiler warnings. EA_ASSERT((iCharBegin < iCharEnd) && (iCharEnd <= mLineLayout.mCharArray.size())); Char c = mLineLayout.mCharArray[iCharBegin]; const bool bZW = IsCharZeroWidth(c); if(EA_UNLIKELY(mLayoutSettings.mTextStyleDefault.mPasswordMode == kPMPassword)) // If the text is password text... { // Possibly we should eat the current cluster instead of doing a // conversion of each Unicode code point to a password char. c = mPasswordChar; } else if(EA_UNLIKELY(c == kCharNBSP)) // If we have a non-breaking space character... c = kCharSpace; else if(EA_UNLIKELY(bZW && mLayoutSettings.mbDisplayZWG)) // If we have a zero-width glyph but we are supposed to display such glyphs... c = '_'; // Replace the ZW char with an actual char. else if(EA_UNLIKELY((c == kCharLF) || (c == kCharCR) || (c == kCharPSEP) || (c == kCharNEL) || (c == kCharLSEP))) // We always substitute newlines for ZWSpace. c = kCharZWSP; else if(EA_UNLIKELY(mLayoutSettings.mbHideControlCharacters && IsCharCategory(c, kCCFlagControlChar))) c = kCharZWNBSP; else if(EA_UNLIKELY(mLineLayout.mAnalysisInfoArray[iCharBegin].mnBidiLevel % 2)) // If we have a RTL bidi level... c = GetMirrorChar(c); // Most often, this will just return c. // As it stands now, we have a 1:1 mapping of input to output chars. clusterSize = 1; pCharCluster[0] = c; return 1; }
/////////////////////////////////////////////////////////////////////////////// // SubstituteGlyphs1 // // Replaces a single glyph in the lineLayout with one or more glyphs from pGlyphIdArray. // Such a substitution is a "decomposition" and is not common but not unheard of either // in complex scripts. // void SubstituteGlyphs1(LineLayout& lineLayout, eastl_size_t iGlyph, const GlyphId* pGlyphIdArray, eastl_size_t glyphIdCount) { // Most commonly, glyphIdCount will be 2. EA_ASSERT(glyphIdCount > 0); if(glyphIdCount > 1) { // Need to save this because the insertion operation may resize the glyph info array. const GlyphInfo glyphInfoSaved = lineLayout.mGlyphInfoArray[iGlyph]; lineLayout.mGlyphArray .insert(lineLayout.mGlyphArray .begin() + (iGlyph + 1), glyphIdCount - 1, GlyphId()); lineLayout.mGlyphInfoArray.insert(lineLayout.mGlyphInfoArray.begin() + (iGlyph + 1), glyphIdCount - 1, glyphInfoSaved); for(eastl_size_t i = 0; i < glyphIdCount; i++) { lineLayout.mGlyphArray[i] = pGlyphIdArray[i]; lineLayout.mGlyphInfoArray[i].mClusterSize = (unsigned)glyphIdCount; // The new multiple glyphs form a glyph cluster of size 'glyphIdCount'. lineLayout.mGlyphInfoArray[i].mClusterPosition = (unsigned)i; // The position of this glyph within the cluster is 'i'. lineLayout.mGlyphInfoArray[i].mCharCount = 1; // The new multiple glyphs refer to a single initial char/glyph. lineLayout.mGlyphInfoArray[i].mClusterBreakable = 0; // Since a single glyph is being represented by multiple, the set is indivisible. } } else { // Else there is a simple 1:1 glyph substitution. This usually won't happen, as such // 1:1 substitutions are usually done directly via Gsub lookup type 1 and never call // a heavier function like this one. However, a font artist may in fact have set up // a 1:1 substitution via Gsub lookup types other than type 1. lineLayout.mGlyphArray[iGlyph] = *pGlyphIdArray; } }
/////////////////////////////////////////////////////////////////////////////// // AppendHebrewCharCluster // eastl_size_t Typesetter::AppendHebrewCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[iCharBegin], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxHebrewGlyphClusterSize]; eastl_size_t glyphCount = 0; eastl_size_t glyphCountPrev = 0; eastl_size_t charsEaten = 0; EA_ASSERT(clusterSize && ((iCharBegin + charCount) <= mLineLayout.mCharArray.size())); #ifdef EA_DEBUG memset(pGlyphIdArray, 0, sizeof(pGlyphIdArray)); #endif for(eastl_size_t c = 0; c < clusterSize; c += charsEaten) { if(IsCharHebrew(*pCharCluster)) charsEaten = GetHebrewGlyphsForChars(pCharCluster + c, clusterSize - c, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); else charsEaten = GetGlyphsForChar(pCharCluster + c, clusterSize - c, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster + c, charsEaten, pGlyphIdArray + glyphCountPrev, glyphCount - glyphCountPrev, pAnalysisInfo->mnBidiLevel); // Hebrew fonts have decorations that may need to be positionally adjusted. // We could do such adjustments with OpenType or with heuristics. The former // is more involved and slower but results in the best output. The latter // has the opposite characteristics. Right now we do general placement, but // we ought to do some adjustments, such as the case whereby we have lone // diacritics that we added a 0x25CC char to. PlaceGeneralGlyphCluster(iCharBegin + c, charsEaten); glyphCountPrev = glyphCount; } // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }
inline bool IsThaiCharLayoutFlag(Char c, unsigned flags) { // To consider: We can probably remove the IsCharThaiLao check if we can guarantee // the input char is always Thai/Lao. // return (IsCharThaiLao(c) ? ((gThaiCharLayoutFlagsTable[c - 0x0E00] & flags) != 0) : false); EA_ASSERT(IsCharThaiLao(c)); return ((gThaiCharLayoutFlagsTable[c - 0x0E00] & flags) != 0); }
inline ThaiCharLayoutFlag GetThaiCharLayoutFlag(Char c) { // To consider: We can probably remove the IsCharThaiLao check if we can guarantee // the input char is always Thai/Lao. // return (IsCharThaiLao(c) ? (ThaiCharClass)gThaiCharLayoutFlagsTable[c - 0x0E00] : kThaiCharLayoutFlagNone); EA_ASSERT(IsCharThaiLao(c)); return (ThaiCharLayoutFlag)gThaiCharLayoutFlagsTable[c - 0x0E00]; }
void BidiResolveNeutral(int nBaseEmbeddingLevel, AnalysisInfo* pAnalysisInfoArray, size_t count) { int level = nBaseEmbeddingLevel; NeutralState neutralState = (nBaseEmbeddingLevel & 1) ? r : l; size_t nRunLength = 0; BidiClass bidiClass; size_t i; for(i = 0; i < count; i++) { // Ignore boundary neutrals. if(pAnalysisInfoArray[i].mBidiClass == BN) { // Include in the count for a deferred run. if(nRunLength) nRunLength++; // Skip any further processing. continue; } EA_ASSERT(pAnalysisInfoArray[i].mBidiClass <= kBidiClassEN); // Only ON, L, R, AN, EN are allowed. bidiClass = pAnalysisInfoArray[i].mBidiClass; const NeutralStateAction neutralStateAction = gNeutralStateActionTable[neutralState][bidiClass]; // resolve the directionality for deferred runs const BidiClass clsRun = GetDeferredNeutrals(neutralStateAction, level); if(clsRun != N) { SetBidiClass(pAnalysisInfoArray + (i - nRunLength), nRunLength, clsRun); nRunLength = 0; } // Resolve the directionality class at the current location. const BidiClass clsNew = GetResolvedNeutrals(neutralStateAction); if(clsNew != N) pAnalysisInfoArray[i].mBidiClass = clsNew; if(In & neutralStateAction) nRunLength++; neutralState = gNeutralStateTable[neutralState][bidiClass]; level = pAnalysisInfoArray[i].mnBidiLevel; } // Resolve any deferred runs bidiClass = GetEmbeddingDirection(level); // eor has type of current level // Resolve the directionality for deferred runs. const BidiClass clsRun = GetDeferredNeutrals(gNeutralStateActionTable[neutralState][bidiClass], level); if(clsRun != N) SetBidiClass(pAnalysisInfoArray + (i - nRunLength), nRunLength, clsRun); }
EATEXT_API void Init() { #if EATEXT_USE_FREETYPE if(gFTLibrary == NULL) { FT_MemoryRec_ memory = { gpCoreAllocator, FTAlloc, FTFree, FTRealloc }; FT_Error error = FT_Init_FreeType_2((FT_Library*)&gFTLibrary, &memory); // Question: does casting gFTLibrary to non-volatile create a C/C++ 'strict aliasing' problem? EA_ASSERT(error == 0); (void)error; } #endif }
/////////////////////////////////////////////////////////////////////////////// // AppendGeneralCharCluster // // charCount is the number of chars referred to in mLineLayout.mCharArray. // clusterSize is the number of chars in pCharCluster, which itself was // generated from mLineLayout.mCharArray and 98% of the time is equivalent. // The strings will be unequal in the case of character-level substitutions // such as mirroring, password chars, control chars, etc. // // The return value is the number of glyphs generated from pCharCluster/clusterSize. // eastl_size_t Typesetter::AppendGeneralCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[i], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxGeneralGlyphClusterSize]; eastl_size_t glyphCount = 0; eastl_size_t glyphCountPrev = 0; eastl_size_t charsEaten = 0; EA_ASSERT(clusterSize && ((iCharBegin + charCount) <= mLineLayout.mCharArray.size())); #ifdef EA_DEBUG memset(pGlyphIdArray, 0, sizeof(pGlyphIdArray)); #endif for(eastl_size_t i = 0; i < clusterSize; i += charsEaten) { // GetGlyphsForChar does a simple char -> glyph 1:1 conversion, through pAnalysisInfo->mpFont (mLineLayout.mAnalysisInfoArray[iCharBegin]). charsEaten = GetGlyphsForChar(pCharCluster + i, clusterSize - i, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); EA_ASSERT(charsEaten > 0); AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster + i, charsEaten, pGlyphIdArray + glyphCountPrev, glyphCount - glyphCountPrev, pAnalysisInfo->mnBidiLevel); PlaceGeneralGlyphCluster(iCharBegin + i, charsEaten); glyphCountPrev = glyphCount; } // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }
/////////////////////////////////////////////////////////////////////////////// // GetHebrewGlyphsForChars // // This is the Hebrew version of the general GetGlyphsForChar function, though // it works on multiple chars. It merely does some possible filtering and then // calls GetGlyphsForChar. // eastl_size_t Typesetter::GetHebrewGlyphsForChars(const Char* pChar, eastl_size_t clusterSize, const AnalysisInfo* pAnalysisInfo, GlyphId* pGlyphIdArray, eastl_size_t& glyphCount) { Char charTemp[kMaxHebrewCharClusterSize]; eastl_size_t j, k; glyphCount = 0; // We don't convert non-final and final forms of letters based on the context. // We expect that the user has done that and if they want to use a non-final // character at the end of a word then we let them do so and we just display // that character. This refers to characters such as Kaf, Mem, Nun, and Pe. for(j = 0, k = 0; j < clusterSize; ++j, ++k) { const Char c = pChar[j]; EA_ASSERT(IsCharHebrew(c)); if((j == 0) && (gHebrewCharClass[c - 0x0590] == kHCCNonSpacing)) // If the first char is a decoration char... { // We prepend a character which is a circle implemented with dotted lines. // It is the Unicode standard for generic base character to be represented // by such a dotted circle. It is a defacto standard that we add such a // character when none is provided. It makes the text look more sensible. charTemp[k++] = c; charTemp[k] = 0x25CC; // If we can't use 0x25CC, we ought to try a space char. } else charTemp[k] = c; } for(j = 0; j < k; ) // Now 'k' is the new cluster size, though 99% of the time it will be equal to clusterSize. { j += GetGlyphsForChar(charTemp + j, k - j, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, L"\x25CB _o", 4); } EA_ASSERT(glyphCount == clusterSize); return glyphCount; }
// 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); } }
bool EA::IO::FixedMemoryStream::Write( const void* pData, size_type nSize ) { if (nSize > 0) { EA_ASSERT(mnPosition <= mnSize); size_type nRequiredSize( mnPosition + nSize ); size_type nBytesToWrite( nSize ); if (nRequiredSize > mnCapacity) // If we need to increase our capacity... nBytesToWrite = (mnSize - mnPosition); else if (mnSize < nRequiredSize) mnSize = nRequiredSize; // We assume that 99% of the time that 'nBytesToWrite' is > 0, // so we don't bother to check to see if it is zero here. EA_ASSERT(mpData && pData); memcpy( (uint8_t *)mpData + mnPosition, pData, (size_t)nBytesToWrite ); mnPosition += nBytesToWrite; return (nBytesToWrite == nSize); // This will be false if the user tried to write beyond the end. } return true; }
bool StreamChild::Write(const void* pData, size_type nSize) { EA_ASSERT(mnPosition <= mnSize); if(nSize > (mnSize - mnPosition)) nSize = (mnSize - mnPosition); if(mpStreamParent->SetPosition((off_type)(mnPositionParent + mnPosition)) && mpStreamParent->Write(pData, nSize)) { mnPosition += nSize; return true; } return false; }
/////////////////////////////////////////////////////////////////////////////// // 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); } }
/////////////////////////////////////////////////////////////////////////////// // ShapeHebrew // void Typesetter::ShapeHebrew(eastl_size_t iCharBegin, eastl_size_t iCharEnd) { EA_ASSERT(iCharEnd <= mLineLayout.mCharArray.size()); Char pCharCluster[kMaxHebrewCharClusterSize]; eastl_size_t clusterSize; eastl_size_t charCount; for(eastl_size_t i = iCharBegin; i < iCharEnd; i += charCount) { #ifdef EA_DEBUG memset(pCharCluster, 0, sizeof(pCharCluster)); #endif charCount = GetHebrewCharCluster(i, iCharEnd, pCharCluster, clusterSize); AppendHebrewCharCluster(i, charCount, pCharCluster, clusterSize); } }
/////////////////////////////////////////////////////////////////////////////// // BidiResolveImplicit // void BidiResolveImplicit(AnalysisInfo* pAnalysisInfoArray, size_t count) { for(size_t i = 0; i < count; i++) { // We cannot resolve kBidiClassBN here, since some kBidiClassBN chars // were resolved to strong types in BidiResolveWeak. To remove these // we need the original BidiClass types for the chars. if(pAnalysisInfoArray[i].mBidiClass != kBidiClassBN) { EA_ASSERT((pAnalysisInfoArray[i].mBidiClass > 0) && (pAnalysisInfoArray[i].mBidiClass < 5)); const int levelAddition = gAddLevelTable[pAnalysisInfoArray[i].mnBidiLevel & 1][pAnalysisInfoArray[i].mBidiClass - 1]; pAnalysisInfoArray[i].mnBidiLevel += levelAddition; } } }
EA::IO::size_type EA::IO::FixedMemoryStream::Read( void* pData, size_type nSize ) { if (nSize > 0) { EA_ASSERT(mnPosition <= mnSize); const size_type nBytesAvailable( mnSize - mnPosition ); if (nBytesAvailable > 0) { if (nSize > nBytesAvailable) nSize = nBytesAvailable; memcpy( pData, (const uint8_t*)mpData + mnPosition, (size_t)nSize ); mnPosition += nSize; return nSize; } } return 0; }
/////////////////////////////////////////////////////////////////////////////// // SubstituteGlyphs2 // // Replaces multiple glyphs in the lineLayout with one glyphId. // Such a substitution is a "composition" and it is usually a ligature formation. // Some languages (e.g. Arabic) have required ligatures, whereby you must implement // the ligature and it is not optional or decorative like in Western languages. // void SubstituteGlyphs2(LineLayout& lineLayout, eastl_size_t iGlyph, eastl_size_t iGlyphCount, GlyphId glyphId, uint32_t /*lookupFlags*/) { // To do: Need to skip glyphs in lineLayout that are ignored. Such glyphs // need to be preserved by copying them to the end of the replaced sequence. // This kind of ignoring operation makes writing a text editor maddening. // Most commonly, iGlyphCount will be 2. EA_ASSERT(iGlyphCount > 0); // Erase glyphs after iGlyph, leaving just a single glyph at position iGlyph. lineLayout.mGlyphArray .erase(lineLayout.mGlyphArray .begin() + (iGlyph + 1), lineLayout.mGlyphArray .begin() + (iGlyph + iGlyphCount)); lineLayout.mGlyphInfoArray.erase(lineLayout.mGlyphInfoArray.begin() + (iGlyph + 1), lineLayout.mGlyphInfoArray.begin() + (iGlyph + iGlyphCount)); // Set the replacement glyph. This glyph replaces the set that was just erased. lineLayout.mGlyphArray[iGlyph] = glyphId; // Set the new glyph info. lineLayout.mGlyphInfoArray[iGlyph].mClusterSize = 1; // This one glyph per multiple chars. In a way, mClusterSize might be thought of as being a fractional value. But we use 1. lineLayout.mGlyphInfoArray[iGlyph].mClusterPosition = 0; // There is only one glyph here, so it must be position 0. lineLayout.mGlyphInfoArray[iGlyph].mCharCount = (unsigned)iGlyphCount; // This is the number of chars this glyph refers to. lineLayout.mGlyphInfoArray[iGlyph].mClusterBreakable = 1; // Since there are multiple chars that generated this glyph, we se it to be breakable. }
/// AssignArabicCharProperties /// /// The direction argument (-1, 0, +1) refers to whether we want the previous /// non-transparent Arabic char, the current Arabic char, or the next non-transparent /// Arabic char. /// void AssignArabicCharProperties(const Char* pText, eastl_size_t nTextLength, GlyphInfo* pGlyphInfo) { EA_ASSERT(pText && (nTextLength > 0) && pGlyphInfo); for(eastl_size_t i = 0; i < nTextLength; i++) { const ArabicJoiningClass jcPrev = GetArabicJoiningClass(pText, nTextLength, i, -1); const ArabicJoiningClass jcCur = GetArabicJoiningClass(pText, nTextLength, i, 0); const ArabicJoiningClass jcNext = GetArabicJoiningClass(pText, nTextLength, i, +1); // R1 if(jcCur == kAJCTransparent) { pGlyphInfo[i].mScriptFlags = kAGEFIsolated; continue; } // R2 if(jcPrev == kAJCJoinCausing || jcPrev == kAJCLeftJoining || jcPrev == kAJCDualJoining) { if(jcCur == kAJCRightJoining) { pGlyphInfo[i].mScriptFlags = kAGEFFinal; continue; } } // R3 if(jcCur == kAJCLeftJoining) { if(jcNext == kAJCJoinCausing || jcNext == kAJCRightJoining || jcNext == kAJCDualJoining) { pGlyphInfo[i].mScriptFlags = kAGEFInitial; continue; } } // R4 if(jcPrev == kAJCJoinCausing || jcPrev == kAJCLeftJoining || jcPrev == kAJCDualJoining) { if(jcCur == kAJCDualJoining) { if(jcNext == kAJCJoinCausing || jcNext == kAJCRightJoining || jcNext == kAJCDualJoining) { pGlyphInfo[i].mScriptFlags = kAGEFMedial; continue; } } } // R5 if(jcPrev == kAJCJoinCausing || jcPrev == kAJCLeftJoining || jcPrev == kAJCDualJoining) { if(jcCur == kAJCDualJoining) { if(!(jcNext == kAJCJoinCausing || jcNext == kAJCRightJoining || jcNext == kAJCDualJoining)) { pGlyphInfo[i].mScriptFlags = kAGEFFinal; continue; } } } // R6 if(!(jcPrev == kAJCJoinCausing || jcPrev == kAJCLeftJoining || jcPrev == kAJCDualJoining)) { if(jcCur == kAJCDualJoining) { if(jcNext == kAJCJoinCausing || jcNext == kAJCRightJoining || jcNext == kAJCDualJoining) { pGlyphInfo[i].mScriptFlags = kAGEFInitial; continue; } } } // R7 pGlyphInfo[i].mScriptFlags = kAGEFIsolated; } }
/////////////////////////////////////////////////////////////////////////////// // DoGlyphSubstitution // // Fonts implement a substitution feature by not just having a single table // for all substitutions but by having multiple tables, with some of them // being in different formats. These tables are created directly by the // font artist and unfortunately are often more optimized for the artist's // convenience than for processing efficiency. See the FontLab OpenType // window, for example, to see how artists create OpenType substitutions. // eastl_size_t DoGlyphSubstitution(LineLayout& lineLayout, eastl_size_t iGlyph, const FeatureLookup& featureLookup, const OTFLookup& otfLookup, const OTF* /*pOTF*/) { const eastl_size_t iGlyphSaved = iGlyph; const uint32_t lookupFlags = otfLookup.mLookupFlags & (kOTFLookupFlagIgnoreBaseGlyphs | kOTFLookupFlagIgnoreLigatures | kOTFLookupFlagIgnoreMarks | kOTFLookupFlagMarkAttachmentType); GlyphInfo& glyphInfo = lineLayout.mGlyphInfoArray[iGlyph]; if(((featureLookup.mGlyphFlags & glyphInfo.mScriptFlags) == 0) && // This is an Arabic glyph property check. OTFLookupFlagMatch(glyphInfo.mOpenTypeLookupFlags, lookupFlags)) // This is an OpenType glyph class check. This is separate because we use OTFLookupFlagMatch elsewhere. { const uint32_t lookupType = otfLookup.mLookupType; // For each sub-table... (at most one should handle the given glyph) for(eastl_size_t i = 0; i < otfLookup.mSubTableOffsetCount; i++) { const OTFLookupSubTableGsub& gsub = otfLookup.mSubTable.mpGsubArray[i]; const GlyphId glyphIdOriginal = lineLayout.mGlyphArray[iGlyph]; const int32_t iCoverage = gsub.mCoverage.GetCoverageIndex(glyphIdOriginal); if(iCoverage >= 0) // If this sub-table handles the glyph... { switch (lookupType) { case 1: // (single glyph converted to another glyph) { if(gsub.mFormat == 2) lineLayout.mGlyphArray[iGlyph] = gsub.mLookup.mSingle2.mpSubstitutionArray[iCoverage]; else lineLayout.mGlyphArray[iGlyph] = (GlyphId)(lineLayout.mGlyphArray[iGlyph] + gsub.mLookup.mSingle1.mDeltaGlyphId); iGlyph++; break; } case 2: // (single glyph expanded to multiple glyphs) { EA_ASSERT(gsub.mFormat == 1); if(gsub.mFormat == 1) { const OTFSequence& s = gsub.mLookup.mMultiple1.mpSequenceArray[iCoverage]; // We insert (s.mSubstitutionCount) chars from (s.mpSubstitutionArray) into lineLayout at iGlyph. SubstituteGlyphs1(lineLayout, iGlyph, s.mpSubstitutionArray, s.mSubstitutionCount); iGlyph += s.mSubstitutionCount; } break; } case 4: // (multiple glyphs condensed to a single glyph) { EA_ASSERT(gsub.mFormat == 1); if((gsub.mFormat == 1) && ((iGlyph + 1) < lineLayout.mGlyphArray.size())) { const OTFGsubLigature1& ligature1 = gsub.mLookup.mLigature1; const OTFLigatureSet& ligatureSet = ligature1.mpLigatureSetArray[iCoverage]; for(uint32_t j = 0; j < ligatureSet.mLigatureCount; j++) { // If the current glyph array matches ligature.mpComponentArray, // replace the glyphs in the glyph array with ligature.mGlyph. const OTFLigature& ligature = ligatureSet.mpLigatureArray[j]; const int32_t matchCount = CompareGlyphIds(lineLayout, iGlyph + 1, ligature.mpComponentArray, ligature.mComponentCount - 1, lookupFlags); if(matchCount >= 0) { SubstituteGlyphs2(lineLayout, iGlyph, matchCount + 1, ligature.mGlyph, lookupFlags); iGlyph += 1; break; } } } break; } case 5: // (substitution of glyphs based on their context) { // Currently we handle only format 2. We'll do the others as-needed. EA_ASSERT(gsub.mFormat == 2); if(gsub.mFormat == 2) { // To do } break; } case 6: // (substitution of glyphs based on their context) { // Currently we handle only format 2. We'll do the others as-needed. EA_ASSERT(gsub.mFormat == 2); if(gsub.mFormat == 2) { // To do } break; } default: continue; } break; } } } // If the glyph was not handled by the above logic, just skip the glyph. if(iGlyph == iGlyphSaved) ++iGlyph; return iGlyph; }
/////////////////////////////////////////////////////////////////////////////// // CompleteLineLayoutArrays // void Typesetter::CompleteLineLayoutArrays(eastl_size_t iCharBegin, eastl_size_t iCharEnd, eastl_size_t iGlyphBegin) { // Upon entry into this function, our mCharArray and mAnalysisInfoArray should // be in sync with each other. And mGlyphArray and mGlyphInfoArray should be in // sync with each other. But the other arrays haven't been updated yet. EA_ASSERT(mLineLayout.mAnalysisInfoArray.size() == mLineLayout.mCharArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphInfoArray.size()); mLineLayout.mGlyphIndexArray.resize(iCharEnd); mLineLayout.mCharIndexArray.resize(mLineLayout.mGlyphInfoArray.size()); mLineLayout.mGlyphLayoutInfoArray.resize(mLineLayout.mGlyphInfoArray.size()); eastl_size_t iChar = iCharBegin; eastl_size_t iGlyph = iGlyphBegin; eastl_size_t iGlyphEnd = mLineLayout.mGlyphInfoArray.size(); eastl_size_t i; while(iGlyph < iGlyphEnd) { GlyphInfo& gi = mLineLayout.mGlyphInfoArray[iGlyph]; GlyphLayoutInfo& gli = mLineLayout.mGlyphLayoutInfoArray[iGlyph]; // gi.mCharCount // Multiple chars per glyph. // gi.mClusterSize // Multiple glyphs per char. for(i = 0; i < gi.mCharCount; i++) { mLineLayout.mGlyphIndexArray[iChar + i] = (uint32_t)iGlyph; if(EA_LIKELY(mLineLayout.mCharArray[iChar + i] != kCharOBJ)) { gli.mpFont = mLineLayout.mAnalysisInfoArray[iChar + i].mpFont; // Note that gli.mpFont is a union of mpFont and mpObject. gi.mbGlyphIsObject = 0; } else { const Item* const pItem = GetScheduleItemFromCharIndex(iChar + i, true); // iChar is line-relative. gli.mpObject = pItem->mObject.mpObjectPtr; // Note that gli.mpFont is a union of mpFont and mpObject. gi.mbGlyphIsObject = 1; } } for(i = 0; i < gi.mClusterSize; i++) mLineLayout.mCharIndexArray[iGlyph + i] = (uint32_t)iChar; iGlyph += gi.mClusterSize; iChar += gi.mCharCount; } // The GlyphInfo data should match the char and glyph string lengths. EA_ASSERT((iChar == iCharEnd) && (iGlyph == iGlyphEnd)); // Verify that the above code put the arrays in their expected sizes. // We maintain mCharArray and mGlyphIndexArray in parallel. // We maintain mGlyphArray, mGlyphInfoArray, mGlyphLayoutInfoArray, and mCharIndexArray in parallel. EA_ASSERT(mLineLayout.mGlyphIndexArray.size() == iCharEnd); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphInfoArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphLayoutInfoArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mCharIndexArray.size()); }
int Font::AddRef() { EA_ASSERT(mRefCount < 50000); // Sanity check. return ++mRefCount; }
/////////////////////////////////////////////////////////////////////////////// // PlaceGeneralGlyphCluster // // Takes the char/glyph info in mLineLayout and writes the glyphLayoutInfo and // updates the pen position. // void Typesetter::PlaceGeneralGlyphCluster(eastl_size_t iCharBegin, eastl_size_t charClusterSize) { eastl_size_t iGlyphBegin; eastl_size_t iGlyphEnd; EA_ASSERT(iCharBegin < mLineLayout.mAnalysisInfoArray.size()); const EA::Text::TextStyle* const pTextStyle = mLineLayout.mAnalysisInfoArray[iCharBegin].mpTextStyle; EA_ASSERT(pTextStyle); const float fLetterSpacing = pTextStyle->mfLetterSpacing; // To consider: As of this writing we don't have support for TextStyle::mfWordSpacing. // We aren't going to support it until users need it, but the support could be implemented // here or possibly in the ShapeText function. To implement mfWordSpacing requires that we // identify word break locations ahead of time with our WordBreakIterator. mLineLayout.GetGlyphRangeFromCharRange(iCharBegin, iCharBegin + charClusterSize, iGlyphBegin, iGlyphEnd); #if (EATEXT_KERNING_SUPPORT > 0) GlyphId glyphIdPrev = 0; #endif for(eastl_size_t g = iGlyphBegin; g < iGlyphEnd; ++g) { const GlyphId glyphId = mLineLayout.mGlyphArray[g]; GlyphInfo& glyphInfo = mLineLayout.mGlyphInfoArray[g]; GlyphLayoutInfo& glyphLayoutInfo = mLineLayout.mGlyphLayoutInfoArray[g]; GlyphMetrics glyphMetrics; // Possibly do a kerning adjustment. #if (EATEXT_KERNING_SUPPORT > 0) if((g != iGlyphBegin) && (glyphInfo.mbGlyphIsObject == 0)) // If we are past the first char and we are working with a character and not an object... { Kerning kerning; if(glyphLayoutInfo.mpFont->GetKerning(glyphIdPrev, glyphId, kerning, glyphInfo.mDirection, true)) mfPenX += kerning.mfKernX; // Typically mfKernX will be a small (~1 pixel for a 10 pixel wide character) negative (move second char closer to first char) value. } #endif // If the character is a non-spacing mark (e.g. diacritical), then possibly // we would modify the metrics of the base + mark to position the mark more // appropriately. See the Unicode Standard 4.0, Section 3.6.D16. if(glyphInfo.mbGlyphIsObject == 0) glyphLayoutInfo.mpFont->GetGlyphMetrics(glyphId, glyphMetrics); else GetObjectMetrics(g, glyphMetrics); SetGlyphLayoutInfo(glyphLayoutInfo, mfPenX, glyphMetrics); if(glyphInfo.mClusterPosition == 0) // We want to implement letter spacing adjustments only for the base char of clusters. { glyphMetrics.mfHAdvanceX += fLetterSpacing; // To do: If this is a word break location, then apply TextStyle::mfWordSpacing here. } // We track max pen position because it allows us to do better kerning. mfPenX += glyphMetrics.mfHAdvanceX; if(mfPenX > mfPenXMax) mfPenXMax = mfPenX; mfPenX = mfPenXMax; #if (EATEXT_KERNING_SUPPORT > 0) glyphIdPrev = glyphId; #endif } }
/////////////////////////////////////////////////////////////////////////////// // BidiResolveWeak // void BidiResolveWeak(int nBaseEmbeddingLevel, AnalysisInfo* pAnalysisInfoArray, size_t count) { // Compile-time assert that somebody hasn't messed with the kBidiClass ordering. EA_COMPILETIME_ASSERT((N == 0) && (ON == 0) && (L == 1) && (R == 2) && (AN == 3) && (EN == 4) && (AL == 5) && (NSM == 6) && (CS == 7) && (ES == 8) && (ET == 9) && (BN == 10) && (S == 11) && (WS == 12) && (B == 13) && (RLO == 14) && (RLE == 15) && (LRO == 16) && (LRE == 17) && (PDF == 18)); int level = nBaseEmbeddingLevel; WeakState weakState = (nBaseEmbeddingLevel & 1) ? xr : xl; size_t nRunLength = 0; BidiClass bidiClass; size_t i; for(i = 0; i < count; i++) { // We expect mBidiClass to be one of: ON, L, R, AN, EN, AL, NSM, CS, ES, ET, BN. EA_ASSERT(pAnalysisInfoArray[i].mBidiClass <= kBidiClassBN); // Ignore boundary neutrals if(pAnalysisInfoArray[i].mBidiClass == kBidiClassBN) { // must flatten levels unless at a level change; pAnalysisInfoArray[i].mnBidiLevel = level; // Look ahead for level changes. if(((i + 1) == count) && (level != nBaseEmbeddingLevel)) { // Need to fixup last kBidiClassBN before end of the loop, // since its fix-upped value will be needed below the assert. pAnalysisInfoArray[i].mBidiClass = GetEmbeddingDirection(level); } else if(((i + 1) < count) && (level != pAnalysisInfoArray[i + 1].mnBidiLevel) && (pAnalysisInfoArray[i + 1].mBidiClass != kBidiClassBN)) { // Fixup the last kBidiClassBN in front / after a level // run to make it act like the SOR/EOR in rule X10. int newLevel = pAnalysisInfoArray[i + 1].mnBidiLevel; if(level > newLevel) newLevel = level; pAnalysisInfoArray[i].mnBidiLevel = newLevel; // Must match assigned level. pAnalysisInfoArray[i].mBidiClass = GetEmbeddingDirection(newLevel); level = pAnalysisInfoArray[i + 1].mnBidiLevel; } else { // Don't interrupt runs if(nRunLength) nRunLength++; continue; } } // We expect mBidiClass to be one of: ON, L, R, AN, EN, AL, NSM, CS, ES, ET, BN. EA_ASSERT(pAnalysisInfoArray[i].mBidiClass <= kBidiClassBN); bidiClass = pAnalysisInfoArray[i].mBidiClass; // Set the directionality for deferred runs. const WeakStateAction weakStateAction = gWeakStateActionTable[weakState][bidiClass]; const BidiClass clsRun = GetDeferredType(weakStateAction); if((uint32_t)clsRun != XX) { SetBidiClass(pAnalysisInfoArray + (i - nRunLength), nRunLength, clsRun); nRunLength = 0; } // Resolve the directionality class at the current location BidiClass clsNew = GetResolvedType(weakStateAction); if((uint32_t)clsNew != XX) pAnalysisInfoArray[i].mBidiClass = clsNew; // Increment a deferred run. if(IX & weakStateAction) nRunLength++; weakState = gWeakStateTable[weakState][bidiClass]; } // Resolve any deferred runs. Use the direction of the current // level to emulate PDF (pop directional format). bidiClass = GetEmbeddingDirection(level); // Resolve the directionality for deferred runs. const WeakStateAction weakStateAction = gWeakStateActionTable[weakState][bidiClass]; const BidiClass clsRun = GetDeferredType(weakStateAction); if((uint32_t)clsRun != XX) SetBidiClass(pAnalysisInfoArray + (i - nRunLength), nRunLength, clsRun); }
/////////////////////////////////////////////////////////////////////////////// // AppendGeneralGlyphCluster // // Appends a single cluster of chars, which is represented visually by a // single cluster of glyphs. // void Typesetter::AppendGeneralGlyphCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t charClusterSize, const GlyphId* pGlyphCluster, eastl_size_t glyphClusterSize, int embeddingLevel) { (void)charClusterSize; EA_ASSERT(charCount && charClusterSize && glyphClusterSize); // We maintain mCharArray and mGlyphIndexArray in parallel. // We maintain mGlyphArray, mGlyphInfoArray, mGlyphLayoutInfoArray, and mCharIndexArray in parallel. EA_ASSERT(mLineLayout.mGlyphIndexArray.size() == iCharBegin); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphInfoArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphLayoutInfoArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mCharIndexArray.size()); // We append the chars to our LineLayout information. It is assumed that the // given N chars corresponds to the given N glyphs. Usually you will have a // 1:1 correspondence between chars and glyphs and (charClusterSize == glyphClusterSize). // Even more often you will have (charClusterSize == glyphClusterSize == 1). const eastl_size_t glyphArrayIndexEnd = mLineLayout.mGlyphArray.size(); // Map each char to the first glyph. while(mLineLayout.mGlyphIndexArray.size() < (iCharBegin + charCount)) mLineLayout.mGlyphIndexArray.push_back(glyphArrayIndexEnd); for(eastl_size_t g = 0; g < glyphClusterSize; g++) { const GlyphId glyphId = pGlyphCluster[g]; Font* const pFont = mLineLayout.mAnalysisInfoArray[iCharBegin].mpFont; // Add a GlyphId entry. This will refer to the glyph provided by the Font. mLineLayout.mGlyphArray.push_back(glyphId); // Add a GlyphInfo entry. This will store logical information about the glyph. mLineLayout.mGlyphInfoArray.push_back(); GlyphInfo& gi = mLineLayout.mGlyphInfoArray.back(); // Add a GlyphLayoutInfo entry. This will store rendering information about the glyph. mLineLayout.mGlyphLayoutInfoArray.push_back(); GlyphLayoutInfo& gli = mLineLayout.mGlyphLayoutInfoArray.back(); gi.mGJC = kGJCNone; // We set mGJC to something else later if and when we do justification. gi.mClusterPosition = (unsigned)g; // If we were to have multiple glyphs that are together visually, gi.mClusterSize = (unsigned)glyphClusterSize; // we would revise mClusterPosition and mClusterSize. We would need to gi.mClusterBreakable = 0; // do this revision later, when we have all glyphs shaped for a line or paragraph. gi.mCharCount = (unsigned)charCount; // gi.mCharCount currently has only two bits of data. gi.mDirection = embeddingLevel; // if(EA_LIKELY(*pCharCluster != kCharOBJ)) { gli.mpFont = pFont; // Note that gli.mpFont is a union of mpFont and mpObject. gi.mbGlyphIsObject = 0; } else { const Item* const pItem = GetScheduleItemFromCharIndex(iCharBegin, true); // iCharBegin is line-relative. gli.mpObject = pItem->mObject.mpObjectPtr; // Note that gli.mpFont is a union of mpFont and mpObject. gi.mbGlyphIsObject = 1; } // Map each glyph to the first char - that identified by 'iCharBegin'. mLineLayout.mCharIndexArray.push_back(iCharBegin); // We maintain mCharArray and mGlyphIndexArray in parallel. // We maintain mGlyphArray, mGlyphInfoArray, mGlyphLayoutInfoArray, and mCharIndexArray in parallel. EA_ASSERT(mLineLayout.mGlyphIndexArray.size() == (iCharBegin + charCount)); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphInfoArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mGlyphLayoutInfoArray.size()); EA_ASSERT(mLineLayout.mGlyphArray.size() == mLineLayout.mCharIndexArray.size()); } }