Пример #1
0
// Execute a query in "logical" order starting at |queryRoot|. This means
// walking the lines boxes for each layout object in layout tree (pre)order.
static void logicalQuery(LayoutObject* queryRoot, QueryData* queryData, ProcessTextFragmentCallback fragmentCallback)
{
    if (!queryRoot)
        return;

    // Walk the layout tree in pre-order, starting at the specified root, and
    // run the query for each text node.
    Vector<SVGInlineTextBox*> textBoxes;
    for (LayoutObject* layoutObject = queryRoot->slowFirstChild(); layoutObject; layoutObject = layoutObject->nextInPreOrder(queryRoot)) {
        if (!layoutObject->isSVGInlineText())
            continue;

        LineLayoutSVGInlineText textLineLayout = LineLayoutSVGInlineText(toLayoutSVGInlineText(layoutObject));
        ASSERT(textLineLayout.style());

        // TODO(fs): Allow filtering the search earlier, since we should be
        // able to trivially reject (prune) at least some of the queries.
        collectTextBoxesInLogicalOrder(textLineLayout, textBoxes);

        for (const SVGInlineTextBox* textBox : textBoxes) {
            if (queryTextBox(queryData, textBox, fragmentCallback))
                return;
            queryData->currentOffset += textBox->len();
        }
    }
}
Пример #2
0
bool SVGInlineTextBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit)
{
    // FIXME: integrate with InlineTextBox::nodeAtPoint better.
    ASSERT(!isLineBreak());

    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, result.hitTestRequest(), lineLayoutItem().style()->pointerEvents());
    bool isVisible = lineLayoutItem().style()->visibility() == VISIBLE;
    if (isVisible || !hitRules.requireVisible) {
        if (hitRules.canHitBoundingBox
                || (hitRules.canHitStroke && (lineLayoutItem().style()->svgStyle().hasStroke() || !hitRules.requireStroke))
                || (hitRules.canHitFill && (lineLayoutItem().style()->svgStyle().hasFill() || !hitRules.requireFill))) {
            LayoutPoint boxOrigin(x(), y());
            boxOrigin.moveBy(accumulatedOffset);
            LayoutRect rect(boxOrigin, size());
            if (locationInContainer.intersects(rect)) {
                LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->lineLayoutItem());
                ASSERT(lineLayoutItem.scalingFactor());
                float baseline = lineLayoutItem.scaledFont().fontMetrics().floatAscent() / lineLayoutItem.scalingFactor();

                FloatPoint floatLocation = FloatPoint(locationInContainer.point());
                for (const SVGTextFragment& fragment : m_textFragments) {
                    FloatQuad fragmentQuad = fragment.boundingQuad(baseline);
                    if (fragmentQuad.containsPoint(floatLocation)) {
                        lineLayoutItem.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
                        if (!result.addNodeToListBasedTestResult(lineLayoutItem.node(), locationInContainer, rect))
                            return true;
                    }
                }
            }
        }
    }
    return false;
}
Пример #3
0
int SVGInlineTextBox::offsetForPositionInFragment(
    const SVGTextFragment& fragment,
    LayoutUnit position,
    bool includePartialGlyphs) const {
  LineLayoutSVGInlineText lineLayoutItem =
      LineLayoutSVGInlineText(this->getLineLayoutItem());

  float scalingFactor = lineLayoutItem.scalingFactor();
  ASSERT(scalingFactor);

  const ComputedStyle& style = lineLayoutItem.styleRef();

  TextRun textRun = constructTextRun(style, fragment);

  // Eventually handle lengthAdjust="spacingAndGlyphs".
  // FIXME: Handle vertical text.
  if (fragment.isTransformed()) {
    AffineTransform fragmentTransform = fragment.buildFragmentTransform();
    textRun.setHorizontalGlyphStretch(
        clampTo<float>(fragmentTransform.xScale()));
  }

  return fragment.characterOffset - start() +
         lineLayoutItem.scaledFont().offsetForPosition(
             textRun, position * scalingFactor, includePartialGlyphs);
}
Пример #4
0
FloatRect SVGInlineTextBox::selectionRectForTextFragment(
    const SVGTextFragment& fragment,
    int startPosition,
    int endPosition,
    const ComputedStyle& style) const {
  ASSERT(startPosition < endPosition);

  LineLayoutSVGInlineText lineLayoutItem =
      LineLayoutSVGInlineText(this->getLineLayoutItem());

  float scalingFactor = lineLayoutItem.scalingFactor();
  ASSERT(scalingFactor);

  const Font& scaledFont = lineLayoutItem.scaledFont();
  const SimpleFontData* fontData = scaledFont.primaryFont();
  DCHECK(fontData);
  if (!fontData)
    return FloatRect();

  const FontMetrics& scaledFontMetrics = fontData->getFontMetrics();
  FloatPoint textOrigin(fragment.x, fragment.y);
  if (scalingFactor != 1)
    textOrigin.scale(scalingFactor, scalingFactor);

  textOrigin.move(0, -scaledFontMetrics.floatAscent());

  FloatRect selectionRect = scaledFont.selectionRectForText(
      constructTextRun(style, fragment), textOrigin,
      fragment.height * scalingFactor, startPosition, endPosition);
  if (scalingFactor == 1)
    return selectionRect;

  selectionRect.scale(1 / scalingFactor);
  return selectionRect;
}
SVGTextMetricsCalculator::SVGTextMetricsCalculator(LayoutSVGInlineText* text)
    : m_text(LineLayoutSVGInlineText(text))
    , m_bidiRun(nullptr)
    , m_run(SVGTextMetrics::constructTextRun(m_text, 0, m_text.textLength(), m_text.styleRef().direction()))
    , m_totalWidth(0)
{
    setupBidiRuns();
}
Пример #6
0
bool SVGInlineTextBox::nodeAtPoint(HitTestResult& result,
                                   const HitTestLocation& locationInContainer,
                                   const LayoutPoint& accumulatedOffset,
                                   LayoutUnit,
                                   LayoutUnit) {
  // FIXME: integrate with InlineTextBox::nodeAtPoint better.
  ASSERT(!isLineBreak());

  PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING,
                                 result.hitTestRequest(),
                                 getLineLayoutItem().style()->pointerEvents());
  bool isVisible =
      getLineLayoutItem().style()->visibility() == EVisibility::Visible;
  if (isVisible || !hitRules.requireVisible) {
    if (hitRules.canHitBoundingBox ||
        (hitRules.canHitStroke &&
         (getLineLayoutItem().style()->svgStyle().hasStroke() ||
          !hitRules.requireStroke)) ||
        (hitRules.canHitFill &&
         (getLineLayoutItem().style()->svgStyle().hasFill() ||
          !hitRules.requireFill))) {
      LayoutRect rect(topLeft(), LayoutSize(logicalWidth(), logicalHeight()));
      rect.moveBy(accumulatedOffset);
      if (locationInContainer.intersects(rect)) {
        LineLayoutSVGInlineText lineLayoutItem =
            LineLayoutSVGInlineText(this->getLineLayoutItem());
        const SimpleFontData* fontData =
            lineLayoutItem.scaledFont().primaryFont();
        DCHECK(fontData);
        if (!fontData)
          return false;

        DCHECK(lineLayoutItem.scalingFactor());
        float baseline = fontData->getFontMetrics().floatAscent() /
                         lineLayoutItem.scalingFactor();
        FloatPoint floatLocation = FloatPoint(locationInContainer.point());
        for (const SVGTextFragment& fragment : m_textFragments) {
          FloatQuad fragmentQuad = fragment.boundingQuad(baseline);
          if (fragmentQuad.containsPoint(floatLocation)) {
            lineLayoutItem.updateHitTestResult(
                result,
                locationInContainer.point() - toLayoutSize(accumulatedOffset));
            if (result.addNodeToListBasedTestResult(lineLayoutItem.node(),
                                                    locationInContainer,
                                                    rect) == StopHitTesting)
              return true;
          }
        }
      }
    }
  }
  return false;
}
Пример #7
0
LayoutRect SVGInlineTextBox::calculateBoundaries() const
{
    LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->lineLayoutItem());
    float scalingFactor = lineLayoutItem.scalingFactor();
    ASSERT(scalingFactor);
    LayoutUnit baseline = lineLayoutItem.scaledFont().fontMetrics().floatAscent() / scalingFactor;

    LayoutRect textBoundingRect;
    for (const SVGTextFragment& fragment : m_textFragments)
        textBoundingRect.unite(LayoutRect(fragment.overflowBoundingBox(baseline)));

    return textBoundingRect;
}
Пример #8
0
static bool queryTextBox(QueryData* queryData, const SVGInlineTextBox* textBox, ProcessTextFragmentCallback fragmentCallback)
{
    queryData->textBox = textBox;
    queryData->textLineLayout = LineLayoutSVGInlineText(textBox->lineLayoutItem());

    queryData->isVerticalText = !queryData->textLineLayout.style()->isHorizontalWritingMode();

    // Loop over all text fragments in this text box, firing a callback for each.
    for (const SVGTextFragment& fragment : textBox->textFragments()) {
        if (fragmentCallback(queryData, fragment))
            return true;
    }
    return false;
}
Пример #9
0
LayoutRect SVGInlineTextBox::calculateBoundaries() const {
  LineLayoutSVGInlineText lineLayoutItem =
      LineLayoutSVGInlineText(this->getLineLayoutItem());
  const SimpleFontData* fontData = lineLayoutItem.scaledFont().primaryFont();
  DCHECK(fontData);
  if (!fontData)
    return LayoutRect();

  float scalingFactor = lineLayoutItem.scalingFactor();
  ASSERT(scalingFactor);
  LayoutUnit baseline(fontData->getFontMetrics().floatAscent() / scalingFactor);

  LayoutRect textBoundingRect;
  for (const SVGTextFragment& fragment : m_textFragments)
    textBoundingRect.unite(LayoutRect(fragment.overflowBoundingBox(baseline)));

  return textBoundingRect;
}
Пример #10
0
int CharacterNumberAtPositionData::characterNumberWithin(const LayoutObject* queryRoot) const
{
    // http://www.w3.org/TR/SVG/single-page.html#text-__svg__SVGTextContentElement__getCharNumAtPosition
    // "If no such character exists, a value of -1 is returned."
    if (!hitLayoutItem)
        return -1;
    ASSERT(queryRoot);
    int characterNumber = offsetInTextNode;

    // Accumulate the lengths of all the text nodes preceding the target layout
    // object within the queried root, to get the complete character number.
    for (LineLayoutItem layoutItem = hitLayoutItem.previousInPreOrder(queryRoot);
        layoutItem; layoutItem = layoutItem.previousInPreOrder(queryRoot)) {
        if (!layoutItem.isSVGInlineText())
            continue;
        characterNumber += LineLayoutSVGInlineText(layoutItem).resolvedTextLength();
    }
    return characterNumber;
}
Пример #11
0
void SVGTextLayoutEngine::layoutInlineTextBox(SVGInlineTextBox* textBox)
{
    ASSERT(textBox);

    LineLayoutSVGInlineText textLineLayout = LineLayoutSVGInlineText(textBox->lineLayoutItem());
    ASSERT(textLineLayout.parent());
    ASSERT(textLineLayout.parent().node());
    ASSERT(textLineLayout.parent().node()->isSVGElement());

    const ComputedStyle& style = textLineLayout.styleRef();

    textBox->clearTextFragments();
    m_isVerticalText = !style.isHorizontalWritingMode();
    layoutTextOnLineOrPath(textBox, textLineLayout, style);

    if (m_inPathLayout)
        return;

    m_lineLayoutBoxes.append(textBox);
}
Пример #12
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);
  }
}