SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned textPosition)
{
    if (m_bidiRun) {
        if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) {
            m_bidiRun = m_bidiRun->next();
            // New BiDi run means new reference position for measurements, so reset |m_totalWidth|.
            m_totalWidth = 0;
        }
        ASSERT(m_bidiRun);
        ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop());
        m_textDirection = m_bidiRun->direction();
    }

    unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1;
    SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection);
    ASSERT(metrics.length() == metricsLength);

    unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0;
    ASSERT(startPosition <= textPosition);
    SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection);
    // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
    // when laying out the glyph "in context" (with it's surrounding characters) it changes due to shaping.
    // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
    // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
    float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth;
    if (currentWidth != metrics.width())
        metrics.setWidth(currentWidth);

    m_totalWidth = complexStartToCurrentMetrics.width();
    return metrics;
}
static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent)
{
    float scalingFactor = queryData->textRenderer->scalingFactor();
    ASSERT(scalingFactor);

    extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor));

    if (startPosition) {
        SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition);
        if (queryData->isVerticalText)
            extent.move(0, metrics.height());
        else
            extent.move(metrics.width(), 0);
    }

    SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, 1);
    extent.setSize(FloatSize(metrics.width(), metrics.height()));

    AffineTransform fragmentTransform;
    fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength);
    if (fragmentTransform.isIdentity())
        return;

    extent = fragmentTransform.mapRect(extent);
}
bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes*& logicalAttributes, SVGTextMetrics& logicalMetrics)
{
    const Vector<SVGTextMetrics>* textMetricsValues = &logicalAttributes->textMetricsValues();
    unsigned textMetricsSize = textMetricsValues->size();
    while (true) {
        if (m_logicalMetricsListOffset == textMetricsSize) {
            if (!currentLogicalCharacterAttributes(logicalAttributes))
                return false;

            textMetricsValues = &logicalAttributes->textMetricsValues();
            textMetricsSize = textMetricsValues->size();
            continue;
        }

        ASSERT(textMetricsSize);
        ASSERT(m_logicalMetricsListOffset < textMetricsSize);
        logicalMetrics = textMetricsValues->at(m_logicalMetricsListOffset);
        if (logicalMetrics.isEmpty() || (!logicalMetrics.width() && !logicalMetrics.height())) {
            advanceToNextLogicalCharacter(logicalMetrics);
            continue;
        }

        // Stop if we found the next valid logical text metrics object.
        return true;
    }

    ASSERT_NOT_REACHED();
    return true;
}
bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const
{
    EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData);

    int startPosition = data->position;
    int endPosition = startPosition + 1;
    if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
        return false;

    data->endPosition = FloatPoint(fragment.x, fragment.y);

    SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition + 1);
    if (queryData->isVerticalText)
        data->endPosition.move(0, metrics.height());
    else
        data->endPosition.move(metrics.width(), 0);

    AffineTransform fragmentTransform;
    fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength);
    if (fragmentTransform.isIdentity())
        return true;

    data->endPosition = fragmentTransform.mapPoint(data->endPosition);
    return true;
}
float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, const SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const
{
    bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle);

    // The function is based on spec requirements:
    //
    // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
    // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
    //
    // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
    // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph.

    const FontMetrics& fontMetrics = m_font.fontMetrics();

    float ascent = fontMetrics.floatAscent() / m_effectiveZoom;
    float descent = fontMetrics.floatDescent() / m_effectiveZoom;

    // Vertical orientation handling.
    if (isVerticalText) {
        float ascentMinusDescent = ascent - descent;

        if (!angle) {
            xOrientationShift = (ascentMinusDescent - metrics.width()) / 2;
            yOrientationShift = ascent;
        } else if (angle == 180) {
            xOrientationShift = (ascentMinusDescent + metrics.width()) / 2;
        } else if (angle == 270) {
            yOrientationShift = metrics.width();
            xOrientationShift = ascentMinusDescent;
        }

        // Vertical advance calculation.
        if (angle && !orientationIsMultiplyOf180Degrees)
            return metrics.width();

        return metrics.height();
    }

    // Horizontal orientation handling.
    if (angle == 90) {
        yOrientationShift = -metrics.width();
    } else if (angle == 180) {
        xOrientationShift = metrics.width();
        yOrientationShift = -ascent;
    } else if (angle == 270) {
        xOrientationShift = metrics.width();
    }

    // Horizontal advance calculation.
    if (angle && !orientationIsMultiplyOf180Degrees)
        return metrics.height();

    return metrics.width();
}
SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterComplex(unsigned textPosition)
{
    unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1;
    SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection);
    ASSERT(metrics.length() == metricsLength);

    unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0;
    ASSERT(startPosition <= textPosition);
    SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection);
    // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
    // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
    // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
    // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
    float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth;
    if (currentWidth != metrics.width())
        metrics.setWidth(currentWidth);

    m_totalWidth = complexStartToCurrentMetrics.width();
    return metrics;
}
bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragment& fragment) const
{
    SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData);

    int startPosition = data->startPosition;
    int endPosition = startPosition + data->length;
    if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
        return false;

    SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, endPosition - startPosition);
    data->subStringLength += queryData->isVerticalText ? metrics.height() : metrics.width();
    return false;
}
Example #8
0
static FloatPoint calculateGlyphPositionWithoutTransform(const QueryData* queryData, const SVGTextFragment& fragment, int offsetInFragment)
{
    float glyphOffsetInDirection = 0;
    if (offsetInFragment) {
        SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textLayoutObject, fragment.characterOffset, offsetInFragment, queryData->textBox->direction());
        if (queryData->isVerticalText)
            glyphOffsetInDirection = metrics.height();
        else
            glyphOffsetInDirection = metrics.width();
    }

    if (!queryData->textBox->isLeftToRightDirection()) {
        float fragmentExtent = queryData->isVerticalText ? fragment.height : fragment.width;
        glyphOffsetInDirection = fragmentExtent - glyphOffsetInDirection;
    }

    FloatPoint glyphPosition(fragment.x, fragment.y);
    if (queryData->isVerticalText)
        glyphPosition.move(0, glyphOffsetInDirection);
    else
        glyphPosition.move(glyphOffsetInDirection, 0);

    return glyphPosition;
}
Example #9
0
void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const
{
    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 
        if (child->isSVGInlineText()) {
            RenderSVGInlineText* text = toRenderSVGInlineText(child);
            const UChar* characters = text->characters();
            unsigned textLength = text->textLength();
            bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style());

            SVGTextLayoutAttributes attributes(text);
            attributes.reserveCapacity(textLength);
    
            unsigned valueListPosition = atCharacter;
            unsigned metricsLength = 1;
            SVGTextMetrics lastMetrics(SVGTextMetrics::SkippedSpaceMetrics);

            for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) {
                const UChar& currentCharacter = characters[textPosition];

                SVGTextMetrics startToCurrentMetrics;
                SVGTextMetrics currentMetrics;
                unsigned valueListAdvance = 0;

                if (U16_IS_LEAD(currentCharacter) && (textPosition + 1) < textLength && U16_IS_TRAIL(characters[textPosition + 1])) {
                    // Handle surrogate pairs.
                    startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 2);
                    currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 2);
                    metricsLength = currentMetrics.length();
                    valueListAdvance = 1;
                } else {
                    // Handle BMP characters.
                    startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1);
                    currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1);
                    metricsLength = currentMetrics.length();
                    valueListAdvance = metricsLength;
                }

                if (!metricsLength)
                    break;
                
                // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
                // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
                // So whenever runWidthAdvance != currentMetrics.width(), we are processing a text run whose length is
                // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
                float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width();
                if (runWidthAdvance != currentMetrics.width())
                    currentMetrics.setWidth(runWidthAdvance);

                lastMetrics = startToCurrentMetrics;

                if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) {
                    attributes.positioningLists().appendEmptyValues();
                    attributes.textMetricsValues().append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
                    continue;
                }

                SVGTextLayoutAttributes::PositioningLists& positioningLists = attributes.positioningLists();
                positioningLists.appendValuesFromPosition(m_positioningLists, valueListPosition);
                attributes.textMetricsValues().append(currentMetrics);

                // Pad x/y/dx/dy/rotate value lists with empty values, if the metrics span more than one character.
                if (metricsLength > 1) {
                    for (unsigned i = 0; i < metricsLength - 1; ++i)
                        positioningLists.appendEmptyValues();
                }

                lastCharacter = currentCharacter;
                valueListPosition += valueListAdvance;
            }

#if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
            fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter);
            fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction());
            attributes.dump();
#endif

            text->storeLayoutAttributes(attributes);
            allAttributes.append(attributes);
            atCharacter = valueListPosition;
            continue;
        }

        if (!child->isSVGInline())
            continue;

        propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter);
    }
}