Ejemplo n.º 1
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 4
0
///////////////////////////////////////////////////////////////////////////////
// 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.
}
Ejemplo n.º 5
0
////////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 6
0
// 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);
    }
}
Ejemplo n.º 7
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 8
0
///////////////////////////////////////////////////////////////////////////////
// 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;
    }
}
Ejemplo n.º 9
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 10
0
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);
}
Ejemplo n.º 11
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];
}
Ejemplo n.º 12
0
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);
}
Ejemplo n.º 13
0
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
}
Ejemplo n.º 14
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 15
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 16
0
// 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);
    }
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
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;
}
Ejemplo n.º 19
0
///////////////////////////////////////////////////////////////////////////////
// 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);
    }
}
Ejemplo n.º 20
0
///////////////////////////////////////////////////////////////////////////////
// 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);
    }
}
Ejemplo n.º 21
0
///////////////////////////////////////////////////////////////////////////////
// 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;
        }
    }
}
Ejemplo n.º 22
0
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;
}
Ejemplo n.º 23
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.
}
Ejemplo n.º 24
0
/// 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;
    }
}
Ejemplo n.º 25
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Ejemplo n.º 26
0
///////////////////////////////////////////////////////////////////////////////
// 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());
}
Ejemplo n.º 27
0
int Font::AddRef()
{
    EA_ASSERT(mRefCount < 50000); // Sanity check.
    return ++mRefCount;
}
Ejemplo n.º 28
0
///////////////////////////////////////////////////////////////////////////////
// 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
    }
}
Ejemplo n.º 29
0
///////////////////////////////////////////////////////////////////////////////
// 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);
}
Ejemplo n.º 30
0
///////////////////////////////////////////////////////////////////////////////
// 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());
    }
}