ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, CFRange runRange)
    : m_fontData(fontData)
    , m_characters(characters)
    , m_stringLocation(stringLocation)
    , m_stringLength(stringLength)
    , m_indexEnd(runRange.location + runRange.length)
    , m_isMonotonic(true)
{
    m_glyphCount = CTRunGetGlyphCount(ctRun);
    m_coreTextIndices = CTRunGetStringIndicesPtr(ctRun);
    if (!m_coreTextIndices) {
        m_coreTextIndicesVector.grow(m_glyphCount);
        CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), m_coreTextIndicesVector.data());
        m_coreTextIndices = m_coreTextIndicesVector.data();
    }

    m_glyphs = CTRunGetGlyphsPtr(ctRun);
    if (!m_glyphs) {
        m_glyphsVector.grow(m_glyphCount);
        CTRunGetGlyphs(ctRun, CFRangeMake(0, 0), m_glyphsVector.data());
        m_glyphs = m_glyphsVector.data();
    }

    m_advances = CTRunGetAdvancesPtr(ctRun);
    if (!m_advances) {
        m_advancesVector.grow(m_glyphCount);
        CTRunGetAdvances(ctRun, CFRangeMake(0, 0), m_advancesVector.data());
        m_advances = m_advancesVector.data();
    }
}
Пример #2
0
void CoreTextController::adjustGlyphsAndAdvances()
{
    size_t runCount = m_coreTextRuns.size();
    for (size_t r = 0; r < runCount; ++r) {
        const CoreTextRun& coreTextRun = m_coreTextRuns[r];
        unsigned glyphCount = coreTextRun.glyphCount();
        const SimpleFontData* fontData = coreTextRun.fontData();

        Vector<CGGlyph, 256> glyphsVector;
        const CGGlyph* glyphs;

        Vector<CGSize, 256> advancesVector;
        const CGSize* advances;

        if (coreTextRun.ctRun()) {
            glyphs = CTRunGetGlyphsPtr(coreTextRun.ctRun());
            if (!glyphs) {
                glyphsVector.grow(glyphCount);
                CTRunGetGlyphs(coreTextRun.ctRun(), CFRangeMake(0, 0), glyphsVector.data());
                glyphs = glyphsVector.data();
            }

            advances = CTRunGetAdvancesPtr(coreTextRun.ctRun());
            if (!advances) {
                advancesVector.grow(glyphCount);
                CTRunGetAdvances(coreTextRun.ctRun(), CFRangeMake(0, 0), advancesVector.data());
                advances = advancesVector.data();
            }
        } else {
            // Synthesize a run of missing glyphs.
            glyphsVector.fill(0, glyphCount);
            glyphs = glyphsVector.data();
            advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), glyphCount);
            advances = advancesVector.data();
        }

        bool lastRun = r + 1 == runCount;
        const UChar* cp = coreTextRun.characters();
        CGFloat roundedSpaceWidth = roundCGFloat(fontData->m_spaceWidth);
        bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
        bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled();


        for (unsigned i = 0; i < glyphCount; i++) {
            CFIndex characterIndex = coreTextRun.indexAt(i);
            UChar ch = *(cp + characterIndex);
            bool lastGlyph = lastRun && i + 1 == glyphCount;
            UChar nextCh;
            if (lastGlyph)
                nextCh = ' ';
            else if (i + 1 < glyphCount)
                nextCh = *(cp + coreTextRun.indexAt(i + 1));
            else
                nextCh = *(m_coreTextRuns[r + 1].characters() + m_coreTextRuns[r + 1].indexAt(0));

            bool treatAsSpace = Font::treatAsSpace(ch);
            CGGlyph glyph = treatAsSpace ? fontData->m_spaceGlyph : glyphs[i];
            CGSize advance = treatAsSpace ? CGSizeMake(fontData->m_spaceWidth, advances[i].height) : advances[i];

            if (ch == '\t' && m_run.allowTabs()) {
                float tabWidth = m_font.tabWidth();
                advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth);
            } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
                advance.width = 0;
                glyph = fontData->m_spaceGlyph;
            }

            float roundedAdvanceWidth = roundf(advance.width);
            if (roundsAdvances)
                advance.width = roundedAdvanceWidth;

            advance.width += fontData->m_syntheticBoldOffset;

            // We special case spaces in two ways when applying word rounding.
            // First, we round spaces to an adjusted width in all fonts.
            // Second, in fixed-pitch fonts we ensure that all glyphs that
            // match the width of the space glyph have the same width as the space glyph.
            if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding())
                advance.width = fontData->m_adjustedSpaceWidth;

            if (hasExtraSpacing) {
                // If we're a glyph with an advance, go ahead and add in letter-spacing.
                // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
                if (advance.width && m_font.letterSpacing())
                    advance.width += m_font.letterSpacing();

                // Handle justification and word-spacing.
                if (glyph == fontData->m_spaceGlyph) {
                    // Account for padding. WebCore uses space padding to justify text.
                    // We distribute the specified padding over the available spaces in the run.
                    if (m_padding) {
                        // Use leftover padding if not evenly divisible by number of spaces.
                        if (m_padding < m_padPerSpace) {
                            advance.width += m_padding;
                            m_padding = 0;
                        } else {
                            advance.width += m_padPerSpace;
                            m_padding -= m_padPerSpace;
                        }
                    }

                    // Account for word-spacing.
                    if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
                        advance.width += m_font.wordSpacing();
                }
            }

            // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters 
            // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
            // We adjust the width of the last character of a "word" to ensure an integer width.
            // Force characters that are used to determine word boundaries for the rounding hack
            // to be integer width, so the following words will start on an integer boundary.
            if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
                advance.width = ceilCGFloat(advance.width);

            // Check to see if the next character is a "rounding hack character", if so, adjust the
            // width so that the total run width will be on an integer boundary.
            if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) {
                CGFloat totalWidth = m_totalWidth + advance.width;
                CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth;
                if (m_run.ltr())
                    advance.width += extraWidth;
                else {
                    m_totalWidth += extraWidth;
                    if (m_lastRoundingGlyph)
                        m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
                    else
                        m_finalRoundingWidth = extraWidth;
                    m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
                }
            }

            m_totalWidth += advance.width;
            advance.height *= -1;
            m_adjustedAdvances.append(advance);
            m_adjustedGlyphs.append(glyph);
        }
    }
}