void SVGInlineTextBox::paintTextMatchMarker(GraphicsContext* context, const FloatPoint&, DocumentMarker* marker, RenderStyle* style, const Font& font)
{
    // SVG is only interested in the TextMatch markers.
    if (marker->type() != DocumentMarker::TextMatch)
        return;

    RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    ASSERT(textRenderer);

    FloatRect markerRect;
    AffineTransform fragmentTransform;
    for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
        if (!box->isSVGInlineTextBox())
            continue;

        SVGInlineTextBox* textBox = toSVGInlineTextBox(box);

        int markerStartPosition = max<int>(marker->startOffset() - textBox->start(), 0);
        int markerEndPosition = min<int>(marker->endOffset() - textBox->start(), textBox->len());

        if (markerStartPosition >= markerEndPosition)
            continue;

        const Vector<SVGTextFragment>& fragments = textBox->textFragments();
        unsigned textFragmentsSize = fragments.size();
        for (unsigned i = 0; i < textFragmentsSize; ++i) {
            const SVGTextFragment& fragment = fragments.at(i);

            int fragmentStartPosition = markerStartPosition;
            int fragmentEndPosition = markerEndPosition;
            if (!textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
                continue;

            FloatRect fragmentRect = textBox->selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
            fragment.buildFragmentTransform(fragmentTransform);
            bool fragmentTransformIsIdentity = fragmentTransform.isIdentity();

            // Draw the marker highlight.
            if (renderer()->frame()->editor().markedTextMatchesAreHighlighted()) {
                Color color = marker->activeMatch() ?
                    RenderTheme::theme().platformActiveTextSearchHighlightColor() :
                    RenderTheme::theme().platformInactiveTextSearchHighlightColor();
                GraphicsContextStateSaver stateSaver(*context);
                if (!fragmentTransformIsIdentity)
                    context->concatCTM(fragmentTransform);
                context->setFillColor(color);
                context->fillRect(fragmentRect, color);
            }

            if (!fragmentTransformIsIdentity)
                fragmentRect = fragmentTransform.mapRect(fragmentRect);
            markerRect.unite(fragmentRect);
        }
    }

    toRenderedDocumentMarker(marker)->setRenderedRect(textRenderer->localToAbsoluteQuad(markerRect).enclosingBoundingBox());
}
void SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(RenderSVGInlineText* textRenderer)
{
    ASSERT(textRenderer);

    Node* node = textRenderer->node();
    if (!node || !node->inDocument())
        return;

    RenderStyle* style = textRenderer->style();
    ASSERT(style);

    Document* document = textRenderer->document();
    Vector<DocumentMarker> markers = document->markers()->markersForNode(textRenderer->node());

    Vector<DocumentMarker>::iterator markerEnd = markers.end();
    for (Vector<DocumentMarker>::iterator markerIt = markers.begin(); markerIt != markerEnd; ++markerIt) {
        const DocumentMarker& marker = *markerIt;

        // SVG is only interessted in the TextMatch marker, for now.
        if (marker.type != DocumentMarker::TextMatch)
            continue;

        FloatRect markerRect;
        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
            ASSERT(box->isSVGInlineTextBox());
            SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box);

            int markerStartPosition = max<int>(marker.startOffset - textBox->start(), 0);
            int markerEndPosition = min<int>(marker.endOffset - textBox->start(), textBox->len());

            if (markerStartPosition >= markerEndPosition)
                continue;

            int fragmentStartPosition = 0;
            int fragmentEndPosition = 0;

            const Vector<SVGTextFragment>& fragments = textBox->textFragments();
            unsigned textFragmentsSize = fragments.size();
            for (unsigned i = 0; i < textFragmentsSize; ++i) {
                const SVGTextFragment& fragment = fragments.at(i);

                fragmentStartPosition = markerStartPosition;
                fragmentEndPosition = markerEndPosition;
                if (!textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
                    continue;

                FloatRect fragmentRect = textBox->selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
                if (!fragment.transform.isIdentity())
                    fragmentRect = fragment.transform.mapRect(fragmentRect);

                markerRect.unite(fragmentRect);
            }
        }

        document->markers()->setRenderedRectForMarker(node, marker, textRenderer->localToAbsoluteQuad(markerRect).enclosingBoundingBox());
    }
}
Beispiel #3
0
void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRect)
{
    for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
        FloatRect boxRect;
        if (child->isSVGInlineTextBox()) {
            ASSERT(child->renderer().isSVGInlineText());

            SVGInlineTextBox* textBox = toSVGInlineTextBox(child);
            boxRect = textBox->calculateBoundaries();
            textBox->setX(boxRect.x());
            textBox->setY(boxRect.y());
            textBox->setLogicalWidth(boxRect.width());
            textBox->setLogicalHeight(boxRect.height());
        } else {
            // Skip generated content.
            if (!child->renderer().node())
                continue;

            ASSERT_WITH_SECURITY_IMPLICATION(child->isInlineFlowBox());

            SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child);
            layoutChildBoxes(flowBox);

            boxRect = flowBox->calculateBoundaries();
            flowBox->setX(boxRect.x());
            flowBox->setY(boxRect.y());
            flowBox->setLogicalWidth(boxRect.width());
            flowBox->setLogicalHeight(boxRect.height());
        }
        if (childRect)
            childRect->unite(boxRect);
    }
}
Beispiel #4
0
void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start)
{
    for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
        if (child->isSVGInlineTextBox()) {
            ASSERT(child->renderer());
            ASSERT(child->renderer()->isSVGInlineText());

            SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
            IntRect boxRect = textBox->calculateBoundaries();
            textBox->setX(boxRect.x());
            textBox->setY(boxRect.y());
            textBox->setLogicalWidth(boxRect.width());
            textBox->setLogicalHeight(boxRect.height());
        } else {
            ASSERT(child->isInlineFlowBox());

            // Skip generated content.
            if (!child->renderer()->node())
                continue;

            SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
            layoutChildBoxes(flowBox);

            IntRect boxRect = flowBox->calculateBoundaries();
            flowBox->setX(boxRect.x());
            flowBox->setY(boxRect.y());
            flowBox->setLogicalWidth(boxRect.width());
            flowBox->setLogicalHeight(boxRect.height());
        }
    }
}
void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& boxes)
{
    unsigned boxCount = boxes.size();
    if (!boxCount)
        return;

    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
        SVGInlineTextBox* textBox = boxes.at(boxPosition);
        AffineTransform textBoxTransformation = m_chunkLayoutBuilder.transformationForTextBox(textBox);
        if (textBoxTransformation.isIdentity())
            continue;

        Vector<SVGTextFragment>& fragments = textBox->textFragments();
        unsigned fragmentCount = fragments.size();
        for (unsigned i = 0; i < fragmentCount; ++i) {
            ASSERT(fragments[i].lengthAdjustTransform.isIdentity());
            fragments[i].lengthAdjustTransform = textBoxTransformation;
        }
    }

    boxes.clear();
}
static inline void dumpTextBoxes(Vector<SVGInlineTextBox*>& boxes)
{
    unsigned boxCount = boxes.size();
    fprintf(stderr, "Dumping all text fragments in text sub tree, %i boxes\n", boxCount);

    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
        SVGInlineTextBox* textBox = boxes.at(boxPosition);
        Vector<SVGTextFragment>& fragments = textBox->textFragments();
        fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->textRenderer());
        fprintf(stderr, "        textBox properties, start=%i, len=%i, box direction=%i\n", textBox->start(), textBox->len(), textBox->direction());
        fprintf(stderr, "   textRenderer properties, textLength=%i\n", textBox->textRenderer()->textLength());

        const UChar* characters = textBox->textRenderer()->characters();

        unsigned fragmentCount = fragments.size();
        for (unsigned i = 0; i < fragmentCount; ++i) {
            SVGTextFragment& fragment = fragments.at(i);
            String fragmentString(characters + fragment.characterOffset, fragment.length);
            fprintf(stderr, "    -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, characterOffset=%i, length=%i, characters='%s'\n"
                          , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.characterOffset, fragment.length, fragmentString.utf8().data());
        }
    }
}
void SVGTextChunk::calculateLength(float& length, unsigned& characters) const
{
    SVGTextFragment* lastFragment = 0;

    unsigned boxCount = m_boxes.size();
    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
        SVGInlineTextBox* textBox = m_boxes.at(boxPosition);
        Vector<SVGTextFragment>& fragments = textBox->textFragments();

        unsigned size = fragments.size();
        if (!size)
            continue;

        for (unsigned i = 0; i < size; ++i) {
            SVGTextFragment& fragment = fragments.at(i);
            characters += fragment.length;

            if (m_chunkStyle & VerticalText)
                length += fragment.height;
            else
                length += fragment.width;

            if (!lastFragment) {
                lastFragment = &fragment;
                continue;
            }

            // Resepect gap between chunks.
            if (m_chunkStyle & VerticalText)
                 length += fragment.y - (lastFragment->y + lastFragment->height);
            else
                 length += fragment.x - (lastFragment->x + lastFragment->width);

            lastFragment = &fragment;
        }
    }
}
static Vector<SVGInlineTextBox*> findInlineTextBoxInTextChunks(const SVGTextContentElement* element, const Vector<SVGTextChunk>& chunks)
{   
    Vector<SVGTextChunk>::const_iterator it = chunks.begin();
    const Vector<SVGTextChunk>::const_iterator end = chunks.end();

    Vector<SVGInlineTextBox*> boxes;

    for (; it != end; ++it) {
        Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = it->boxes.begin();
        const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = it->boxes.end();

        for (; boxIt != boxEnd; ++boxIt) {
            SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(boxIt->box);

            Node* textElement = textBox->textObject()->parent()->element();
            ASSERT(textElement);

            if (textElement == element || textElement->parent() == element)
                boxes.append(textBox);
        }
    }

    return boxes;
}
Beispiel #9
0
static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
{
    ASSERT(userData);
    Vector<SVGTextLayoutAttributes*>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes*>*>(userData);

    // This is a copy of std::reverse(first, last). It additionally assures that the metrics map within the renderers belonging to the InlineBoxes are reordered as well.
    while (true)  {
        if (first == last || first == --last)
            return;

        if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
            InlineBox* temp = *first;
            *first = *last;
            *last = temp;
            ++first;
            continue;
        }

        SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first);
        SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last);

        // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
        if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
            RenderSVGInlineText& firstContext = firstTextBox->renderer();
            RenderSVGInlineText& lastContext = lastTextBox->renderer();

            SVGTextLayoutAttributes* firstAttributes = 0;
            SVGTextLayoutAttributes* lastAttributes = 0;
            findFirstAndLastAttributesInVector(attributes, &firstContext, &lastContext, firstAttributes, lastAttributes);
            swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start());
        }

        InlineBox* temp = *first;
        *first = *last;
        *last = temp;

        ++first;
    }
}
void SVGInlineTextBoxPainter::paintTextMatchMarker(GraphicsContext* context, const LayoutPoint&, DocumentMarker* marker, const ComputedStyle& style, const Font& font)
{
    // SVG is only interested in the TextMatch markers.
    if (marker->type() != DocumentMarker::TextMatch)
        return;

    LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(m_svgInlineTextBox.layoutObject());

    AffineTransform fragmentTransform;
    for (InlineTextBox* box = textLayoutObject.firstTextBox(); box; box = box->nextTextBox()) {
        if (!box->isSVGInlineTextBox())
            continue;

        SVGInlineTextBox* textBox = toSVGInlineTextBox(box);

        int markerStartPosition = std::max<int>(marker->startOffset() - textBox->start(), 0);
        int markerEndPosition = std::min<int>(marker->endOffset() - textBox->start(), textBox->len());

        if (markerStartPosition >= markerEndPosition)
            continue;

        const Vector<SVGTextFragment>& fragments = textBox->textFragments();
        unsigned textFragmentsSize = fragments.size();
        for (unsigned i = 0; i < textFragmentsSize; ++i) {
            const SVGTextFragment& fragment = fragments.at(i);

            int fragmentStartPosition = markerStartPosition;
            int fragmentEndPosition = markerEndPosition;
            if (!textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
                continue;

            FloatRect fragmentRect = textBox->selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
            fragment.buildFragmentTransform(fragmentTransform);

            // Draw the marker highlight.
            if (m_svgInlineTextBox.layoutObject().frame()->editor().markedTextMatchesAreHighlighted()) {
                Color color = marker->activeMatch() ?
                    LayoutTheme::theme().platformActiveTextSearchHighlightColor() :
                    LayoutTheme::theme().platformInactiveTextSearchHighlightColor();
                GraphicsContextStateSaver stateSaver(*context);
                if (!fragmentTransform.isIdentity())
                    context->concatCTM(fragmentTransform);
                context->setFillColor(color);
                context->fillRect(fragmentRect, color);
            }
        }
    }
}
VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point)
{
    SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox());

    if (!textBox || textLength() == 0)
        return createVisiblePosition(0, DOWNSTREAM);

    SVGRootInlineBox* rootBox = textBox->svgRootInlineBox();
    RenderBlock* object = rootBox ? rootBox->block() : 0;

    if (!object)
        return createVisiblePosition(0, DOWNSTREAM);

    int closestOffsetInBox = 0;

    // FIXME: This approach is wrong.  The correct code would first find the
    // closest SVGInlineTextBox to the point, and *then* ask only that inline box
    // what the closest text offset to that point is.  This code instead walks
    // through all boxes in order, so when you click "near" a box, you'll actually
    // end up returning the nearest offset in the last box, even if the
    // nearest offset to your click is contained in another box.
    for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) {
        if (box->svgCharacterHitsPosition(point.x() + object->x(), point.y() + object->y(), closestOffsetInBox)) {
            // If we're not at the end/start of the box, stop looking for other selected boxes.
            if (box->direction() == LTR) {
                if (closestOffsetInBox <= (int) box->end() + 1)
                    break;
            } else {
                if (closestOffsetInBox > (int) box->start())
                    break;
            }
        }
    }

    return createVisiblePosition(closestOffsetInBox, DOWNSTREAM);
}
static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly)
{
    float length = 0.0f;
    Vector<SVGChar>::iterator charIt = chunk.start;

    Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin();
    Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end();

    for (; it != end; ++it) {
        SVGInlineBoxCharacterRange& range = *it;

        SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box);
        RenderStyle* style = box->renderer()->style();

        for (int i = range.startOffset; i < range.endOffset; ++i) {
            ASSERT(charIt <= chunk.end);

            // Determine how many characters - starting from the current - can be measured at once.
            // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width
            // of a string is not the sum of the boundaries of all contained glyphs.
            Vector<SVGChar>::iterator itSearch = charIt + 1;
            Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i;
            while (itSearch != endSearch) {
                // No need to check for 'isHidden()' here as this function is not called for text paths.
                if (itSearch->drawnSeperated)
                    break;

                itSearch++;
            }

            unsigned int positionOffset = itSearch - charIt;

            // Calculate width/height of subrange
            SVGInlineBoxCharacterRange subRange;
            subRange.box = range.box;
            subRange.startOffset = i;
            subRange.endOffset = i + positionOffset;

            if (calcWidthOnly)
                length += cummulatedWidthOfInlineBoxCharacterRange(subRange);
            else
                length += cummulatedHeightOfInlineBoxCharacterRange(subRange);

            // Calculate gap between the previous & current range
            // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account
            // so add "40" as width, and analogous for B & C, add "20" as width.
            if (itSearch > chunk.start && itSearch < chunk.end) {
                SVGChar& lastCharacter = *(itSearch - 1);
                SVGChar& currentCharacter = *itSearch;

                int charsConsumed = 0;
                float glyphWidth = 0.0f;
                float glyphHeight = 0.0f;
                String glyphName;
                String unicodeString;
                box->measureCharacter(style, i + positionOffset - 1, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight);

                if (calcWidthOnly)
                    length += currentCharacter.x - lastCharacter.x - glyphWidth;
                else
                    length += currentCharacter.y - lastCharacter.y - glyphHeight;
            }

            // Advance processed characters
            i += positionOffset - 1;
            charIt = itSearch;
        }
    }

    ASSERT(charIt == chunk.end);
    return length;
}
Beispiel #13
0
void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk)
{
    bool processTextLength = chunk.hasDesiredTextLength();
    bool processTextAnchor = chunk.hasTextAnchor();
    if (!processTextAnchor && !processTextLength)
        return;

    const Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
    unsigned boxCount = boxes.size();
    if (!boxCount)
        return;

    // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes).
    float chunkLength = 0;
    unsigned chunkCharacters = 0;
    chunk.calculateLength(chunkLength, chunkCharacters);

    bool isVerticalText = chunk.isVerticalText();
    if (processTextLength) {
        if (chunk.hasLengthAdjustSpacing()) {
            float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;
            unsigned atCharacter = 0;
            for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
                Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
                if (fragments.isEmpty())
                    continue;
                processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);
            }
        } else {
            ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs());
            float textLengthScale = chunk.desiredTextLength() / chunkLength;
            AffineTransform spacingAndGlyphsTransform;

            bool foundFirstFragment = false;
            for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
                SVGInlineTextBox* textBox = boxes[boxPosition];
                Vector<SVGTextFragment>& fragments = textBox->textFragments();
                if (fragments.isEmpty())
                    continue;

                if (!foundFirstFragment) {
                    foundFirstFragment = true;
                    buildSpacingAndGlyphsTransform(isVerticalText, textLengthScale, fragments.first(), spacingAndGlyphsTransform);
                }

                m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);
            }
        }
    }

    if (!processTextAnchor)
        return;

    // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift.
    if (processTextLength && chunk.hasLengthAdjustSpacing()) {
        chunkLength = 0;
        chunkCharacters = 0;
        chunk.calculateLength(chunkLength, chunkCharacters);
    }

    float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
        Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
        if (fragments.isEmpty())
            continue;
        processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
    }
}
Beispiel #14
0
void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount)
{
    SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart];
    ASSERT(textBox);

    RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
    ASSERT(textRenderer);

    const RenderStyle* style = textRenderer->style();
    ASSERT(style);

    const SVGRenderStyle* svgStyle = style->svgStyle();
    ASSERT(svgStyle);

    // Build chunk style flags.
    unsigned chunkStyle = SVGTextChunk::DefaultStyle;

    // Handle 'direction' property.
    if (!style->isLeftToRightDirection())
        chunkStyle |= SVGTextChunk::RightToLeftText;

    // Handle 'writing-mode' property.
    if (svgStyle->isVerticalWritingMode())
        chunkStyle |= SVGTextChunk::VerticalText;

    // Handle 'text-anchor' property.
    switch (svgStyle->textAnchor()) {
    case TA_START:
        break;
    case TA_MIDDLE:
        chunkStyle |= SVGTextChunk::MiddleAnchor;
        break;
    case TA_END:
        chunkStyle |= SVGTextChunk::EndAnchor;
        break;
    };

    // Handle 'lengthAdjust' property.
    float desiredTextLength = 0;
    if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) {
        desiredTextLength = textContentElement->specifiedTextLength().value(textContentElement);

        switch (textContentElement->lengthAdjust()) {
        case SVGTextContentElement::LENGTHADJUST_UNKNOWN:
            break;
        case SVGTextContentElement::LENGTHADJUST_SPACING:
            chunkStyle |= SVGTextChunk::LengthAdjustSpacing;
            break;
        case SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS:
            chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs;
            break;
        };
    }

    SVGTextChunk chunk(chunkStyle, desiredTextLength);

    Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
    for (unsigned i = boxStart; i < boxStart + boxCount; ++i)
        boxes.append(lineLayoutBoxes[i]);

    m_textChunks.append(chunk);
}
Beispiel #15
0
void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart,
                                          BoxListConstIterator boxEnd) {
  ASSERT(*boxStart);

  const LineLayoutSVGInlineText textLineLayout =
      LineLayoutSVGInlineText((*boxStart)->getLineLayoutItem());
  const ComputedStyle& style = textLineLayout.styleRef();

  // Handle 'lengthAdjust' property.
  float desiredTextLength = 0;
  SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown;
  if (SVGTextContentElement* textContentElement =
          SVGTextContentElement::elementFromLineLayoutItem(
              textLineLayout.parent())) {
    lengthAdjust =
        textContentElement->lengthAdjust()->currentValue()->enumValue();

    SVGLengthContext lengthContext(textContentElement);
    if (textContentElement->textLengthIsSpecifiedByUser())
      desiredTextLength =
          textContentElement->textLength()->currentValue()->value(
              lengthContext);
    else
      desiredTextLength = 0;
  }

  bool processTextLength = desiredTextLength > 0;
  bool processTextAnchor = needsTextAnchorAdjustment(style);
  if (!processTextAnchor && !processTextLength)
    return;

  bool isVerticalText = !style.isHorizontalWritingMode();

  // Calculate absolute length of whole text chunk (starting from text box
  // 'start', spanning 'length' text boxes).
  ChunkLengthAccumulator lengthAccumulator(isVerticalText);
  lengthAccumulator.processRange(boxStart, boxEnd);

  if (processTextLength) {
    float chunkLength = lengthAccumulator.length();
    if (lengthAdjust == SVGLengthAdjustSpacing) {
      float textLengthShift =
          (desiredTextLength - chunkLength) / lengthAccumulator.numCharacters();
      unsigned atCharacter = 0;
      for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) {
        Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments();
        if (fragments.isEmpty())
          continue;
        processTextLengthSpacingCorrection(isVerticalText, textLengthShift,
                                           fragments, atCharacter);
      }

      // Fragments have been adjusted, we have to recalculate the chunk
      // length, to be able to apply the text-anchor shift.
      if (processTextAnchor) {
        lengthAccumulator.reset();
        lengthAccumulator.processRange(boxStart, boxEnd);
      }
    } else {
      ASSERT(lengthAdjust == SVGLengthAdjustSpacingAndGlyphs);
      float textLengthScale = desiredTextLength / chunkLength;
      float textLengthBias = 0;

      bool foundFirstFragment = false;
      for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) {
        SVGInlineTextBox* textBox = *boxIter;
        Vector<SVGTextFragment>& fragments = textBox->textFragments();
        if (fragments.isEmpty())
          continue;

        if (!foundFirstFragment) {
          foundFirstFragment = true;
          textLengthBias =
              computeTextLengthBias(fragments.first(), textLengthScale);
        }

        applyTextLengthScaleAdjustment(textLengthScale, textLengthBias,
                                       fragments);
      }
    }
  }

  if (!processTextAnchor)
    return;

  float textAnchorShift =
      calculateTextAnchorShift(style, lengthAccumulator.length());
  for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) {
    Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments();
    if (fragments.isEmpty())
      continue;
    processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
  }
}