예제 #1
0
int InlineTextBox::baselinePosition(FontBaseline baselineType) const
{
    if (!isText() || !parent())
        return 0;
    if (parent()->getLineLayoutItem() == getLineLayoutItem().parent())
        return parent()->baselinePosition(baselineType);
    return LineLayoutBoxModel(getLineLayoutItem().parent()).baselinePosition(baselineType, isFirstLineStyle(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
}
예제 #2
0
LayoutUnit InlineTextBox::lineHeight() const
{
    if (!isText() || !getLineLayoutItem().parent())
        return LayoutUnit();
    if (getLineLayoutItem().isBR())
        return LayoutUnit(toLayoutBR(getLineLayoutItem())->lineHeight(isFirstLineStyle()));
    if (parent()->getLineLayoutItem() == getLineLayoutItem().parent())
        return parent()->lineHeight();
    return LineLayoutBoxModel(getLineLayoutItem().parent()).lineHeight(isFirstLineStyle(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
}
예제 #3
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;
}
예제 #4
0
TextRun InlineTextBox::constructTextRun(
    const ComputedStyle& style,
    StringBuilder* charactersWithHyphen) const {
  ASSERT(getLineLayoutItem().text());

  String string = getLineLayoutItem().text();
  unsigned startPos = start();
  unsigned length = len();
  return constructTextRun(style, StringView(string, startPos, length),
                          getLineLayoutItem().textLength() - startPos,
                          charactersWithHyphen);
}
예제 #5
0
SelectionState InlineTextBox::getSelectionState() const {
  SelectionState state = getLineLayoutItem().getSelectionState();
  if (state == SelectionStart || state == SelectionEnd ||
      state == SelectionBoth) {
    int startPos, endPos;
    getLineLayoutItem().selectionStartEnd(startPos, endPos);
    // The position after a hard line break is considered to be past its end.
    // See the corresponding code in InlineTextBox::isSelected.
    int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);

    // FIXME: Remove -webkit-line-break: LineBreakAfterWhiteSpace.
    int endOfLineAdjustmentForCSSLineBreak =
        getLineLayoutItem().style()->getLineBreak() == LineBreakAfterWhiteSpace
            ? -1
            : 0;
    bool start =
        (state != SelectionEnd && startPos >= m_start &&
         startPos <= m_start + m_len + endOfLineAdjustmentForCSSLineBreak);
    bool end = (state != SelectionStart && endPos > m_start &&
                endPos <= lastSelectable);
    if (start && end)
      state = SelectionBoth;
    else if (start)
      state = SelectionStart;
    else if (end)
      state = SelectionEnd;
    else if ((state == SelectionEnd || startPos < m_start) &&
             (state == SelectionStart || endPos > lastSelectable))
      state = SelectionInside;
    else if (state == SelectionBoth)
      state = SelectionNone;
  }

  // If there are ellipsis following, make sure their selection is updated.
  if (m_truncation != cNoTruncation && root().ellipsisBox()) {
    EllipsisBox* ellipsis = root().ellipsisBox();
    if (state != SelectionNone) {
      int start, end;
      selectionStartEnd(start, end);
      // The ellipsis should be considered to be selected if the end of the
      // selection is past the beginning of the truncation and the beginning of
      // the selection is before or at the beginning of the truncation.
      ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation
                                      ? SelectionInside
                                      : SelectionNone);
    } else {
      ellipsis->setSelectionState(SelectionNone);
    }
  }

  return state;
}
예제 #6
0
TextRun InlineTextBox::constructTextRun(const ComputedStyle& style, const Font& font, StringBuilder* charactersWithHyphen) const
{
    ASSERT(getLineLayoutItem().text());

    StringView string = getLineLayoutItem().text().createView();
    unsigned startPos = start();
    unsigned length = len();

    if (string.length() != length || startPos)
        string.narrow(startPos, length);

    return constructTextRun(style, font, string, getLineLayoutItem().textLength() - startPos, charactersWithHyphen);
}
예제 #7
0
bool InlineTextBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/)
{
    if (isLineBreak() || m_truncation == cFullTruncation)
        return false;

    LayoutPoint boxOrigin = locationIncludingFlipping();
    boxOrigin.moveBy(accumulatedOffset);
    LayoutRect rect(boxOrigin, size());
    if (visibleToHitTestRequest(result.hitTestRequest()) && locationInContainer.intersects(rect)) {
        getLineLayoutItem().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset)));
        if (result.addNodeToListBasedTestResult(getLineLayoutItem().node(), locationInContainer, rect) == StopHitTesting)
            return true;
    }
    return false;
}
예제 #8
0
void InlineTextBox::attachLine()
{
    if (!extracted())
        return;

    getLineLayoutItem().attachTextBox(this);
}
예제 #9
0
void InlineTextBox::extractLine()
{
    if (extracted())
        return;

    getLineLayoutItem().extractTextBox(this);
}
예제 #10
0
TextRun SVGInlineTextBox::constructTextRun(
    const ComputedStyle& style,
    const SVGTextFragment& fragment) const {
  LineLayoutText text = getLineLayoutItem();
  CHECK(!text.needsLayout());

  TextRun run(
      // characters, will be set below if non-zero.
      static_cast<const LChar*>(nullptr),
      0,  // length, will be set below if non-zero.
      0,  // xPos, only relevant with allowTabs=true
      0,  // padding, only relevant for justified text, not relevant for SVG
      TextRun::AllowTrailingExpansion, direction(),
      dirOverride() ||
          style.rtlOrdering() == VisualOrder /* directionalOverride */);

  if (fragment.length) {
    if (text.is8Bit())
      run.setText(text.characters8() + fragment.characterOffset,
                  fragment.length);
    else
      run.setText(text.characters16() + fragment.characterOffset,
                  fragment.length);
  }

  // We handle letter & word spacing ourselves.
  run.disableSpacing();

  // Propagate the maximum length of the characters buffer to the TextRun, even
  // when we're only processing a substring.
  run.setCharactersLength(text.textLength() - fragment.characterOffset);
  ASSERT(run.charactersLength() >= run.length());
  return run;
}
예제 #11
0
LayoutRect SVGInlineTextBox::localSelectionRect(int startPosition,
                                                int endPosition) const {
  int boxStart = start();
  startPosition = std::max(startPosition - boxStart, 0);
  endPosition = std::min(endPosition - boxStart, static_cast<int>(len()));
  if (startPosition >= endPosition)
    return LayoutRect();

  const ComputedStyle& style = getLineLayoutItem().styleRef();

  FloatRect selectionRect;
  int fragmentStartPosition = 0;
  int fragmentEndPosition = 0;

  unsigned textFragmentsSize = m_textFragments.size();
  for (unsigned i = 0; i < textFragmentsSize; ++i) {
    const SVGTextFragment& fragment = m_textFragments.at(i);

    fragmentStartPosition = startPosition;
    fragmentEndPosition = endPosition;
    if (!mapStartEndPositionsIntoFragmentCoordinates(
            fragment, fragmentStartPosition, fragmentEndPosition))
      continue;

    FloatRect fragmentRect = selectionRectForTextFragment(
        fragment, fragmentStartPosition, fragmentEndPosition, style);
    if (fragment.isTransformed())
      fragmentRect = fragment.buildFragmentTransform().mapRect(fragmentRect);

    selectionRect.unite(fragmentRect);
  }

  return LayoutRect(enclosingIntRect(selectionRect));
}
예제 #12
0
bool InlineTextBox::getEmphasisMarkPosition(
    const ComputedStyle& style,
    TextEmphasisPosition& emphasisPosition) const {
  // This function returns true if there are text emphasis marks and they are
  // suppressed by ruby text.
  if (style.getTextEmphasisMark() == TextEmphasisMarkNone)
    return false;

  emphasisPosition = style.getTextEmphasisPosition();
  // Ruby text is always over, so it cannot suppress emphasis marks under.
  if (emphasisPosition == TextEmphasisPositionUnder)
    return true;

  LineLayoutBox containingBlock = getLineLayoutItem().containingBlock();
  // This text is not inside a ruby base, so it does not have ruby text over it.
  if (!containingBlock.isRubyBase())
    return true;

  // Cannot get the ruby text.
  if (!containingBlock.parent().isRubyRun())
    return true;

  LineLayoutRubyText rubyText =
      LineLayoutRubyRun(containingBlock.parent()).rubyText();

  // The emphasis marks over are suppressed only if there is a ruby text box and
  // it not empty.
  return !rubyText || !rubyText.firstLineBox();
}
예제 #13
0
LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) const
{
    int sPos = std::max(startPos - m_start, 0);
    int ePos = std::min(endPos - m_start, (int)m_len);

    if (sPos > ePos)
        return LayoutRect();

    FontCachePurgePreventer fontCachePurgePreventer;

    LayoutUnit selTop = root().selectionTop();
    LayoutUnit selHeight = root().selectionHeight();
    const ComputedStyle& styleToUse = getLineLayoutItem().styleRef(isFirstLineStyle());
    const Font& font = styleToUse.font();

    StringBuilder charactersWithHyphen;
    bool respectHyphen = ePos == m_len && hasHyphen();
    TextRun textRun = constructTextRun(styleToUse, font, respectHyphen ? &charactersWithHyphen : 0);

    LayoutPoint startingPoint = LayoutPoint(logicalLeft(), selTop);
    LayoutRect r;
    if (sPos || ePos != static_cast<int>(m_len)) {
        r = LayoutRect(enclosingIntRect(font.selectionRectForText(textRun, FloatPoint(startingPoint), selHeight, sPos, ePos)));
    } else { // Avoid computing the font width when the entire line box is selected as an optimization.
        // FIXME: the call to rawValue() below is temporary and should be removed once the transition
        // to LayoutUnit-based types is complete (crbug.com/321237)
        r = LayoutRect(enclosingIntRect(LayoutRect(startingPoint, LayoutSize(m_logicalWidth, selHeight))));
    }

    LayoutUnit logicalWidth = r.width();
    if (r.x() > logicalRight())
        logicalWidth = LayoutUnit();
    else if (r.maxX() > logicalRight())
        logicalWidth = logicalRight() - r.x();

    LayoutPoint topPoint;
    LayoutUnit width;
    LayoutUnit height;
    if (isHorizontal()) {
        topPoint = LayoutPoint(r.x(), selTop);
        width = logicalWidth;
        height = selHeight;
        if (hasWrappedSelectionNewline()) {
            if (!isLeftToRightDirection())
                topPoint.setX(LayoutUnit(topPoint.x() - newlineSpaceWidth()));
            width += newlineSpaceWidth();
        }
    } else {
        topPoint = LayoutPoint(selTop, r.x());
        width = selHeight;
        height = logicalWidth;
        // TODO(wkorman): RTL text embedded in top-to-bottom text can create
        // bottom-to-top situations. Add tests and ensure we handle correctly.
        if (hasWrappedSelectionNewline())
            height += newlineSpaceWidth();
    }

    return LayoutRect(topPoint, LayoutSize(width, height));
}
예제 #14
0
void InlineTextBox::characterWidths(Vector<float>& widths) const {
  if (!m_len)
    return;

  FontCachePurgePreventer fontCachePurgePreventer;
  ASSERT(getLineLayoutItem().text());

  const ComputedStyle& styleToUse =
      getLineLayoutItem().styleRef(isFirstLineStyle());
  const Font& font = styleToUse.font();

  TextRun textRun = constructTextRun(styleToUse);
  Vector<CharacterRange> ranges = font.individualCharacterRanges(textRun);
  DCHECK_EQ(ranges.size(), m_len);

  widths.resize(ranges.size());
  for (unsigned i = 0; i < ranges.size(); i++)
    widths[i] = ranges[i].width();
}
예제 #15
0
void InlineTextBox::characterWidths(Vector<float>& widths) const
{
    if (!m_len)
        return;

    FontCachePurgePreventer fontCachePurgePreventer;
    ASSERT(getLineLayoutItem().text());

    const ComputedStyle& styleToUse = getLineLayoutItem().styleRef(isFirstLineStyle());
    const Font& font = styleToUse.font();

    float lastWidth = 0;
    widths.resize(m_len);
    for (unsigned i = 0; i < m_len; i++) {
        StringView substringView = getLineLayoutItem().text().createView();
        substringView.narrow(start(), 1 + i);
        TextRun textRun = constructTextRun(styleToUse, font, substringView, m_len);
        widths[i] = font.width(textRun, nullptr, nullptr) - lastWidth;
        lastWidth = font.width(textRun, nullptr, nullptr);
    }
}
예제 #16
0
int InlineTextBox::offsetForPosition(LayoutUnit lineOffset, bool includePartialGlyphs) const
{
    if (isLineBreak())
        return 0;

    if (lineOffset - logicalLeft() > logicalWidth())
        return isLeftToRightDirection() ? len() : 0;
    if (lineOffset - logicalLeft() < 0)
        return isLeftToRightDirection() ? 0 : len();

    LineLayoutText text = getLineLayoutItem();
    const ComputedStyle& style = text.styleRef(isFirstLineStyle());
    const Font& font = style.font();
    return font.offsetForPosition(constructTextRun(style, font), (lineOffset - logicalLeft()).toFloat(), includePartialGlyphs);
}
예제 #17
0
void InlineTextBox::showBox(int printedCharacters) const
{
    String value = text();
    value.replaceWithLiteral('\\', "\\\\");
    value.replaceWithLiteral('\n', "\\n");
    printedCharacters += fprintf(stderr, "%s %p", boxName(), this);
    for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
        fputc(' ', stderr);
    const LineLayoutText obj = getLineLayoutItem();
    printedCharacters = fprintf(stderr, "\t%s %p", obj.name(), obj.debugPointer());
    const int layoutObjectCharacterOffset = 75;
    for (; printedCharacters < layoutObjectCharacterOffset; printedCharacters++)
        fputc(' ', stderr);
    fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), value.utf8().data());
}
예제 #18
0
LayoutUnit InlineTextBox::positionForOffset(int offset) const
{
    ASSERT(offset >= m_start);
    ASSERT(offset <= m_start + m_len);

    if (isLineBreak())
        return logicalLeft();

    LineLayoutText text = getLineLayoutItem();
    const ComputedStyle& styleToUse = text.styleRef(isFirstLineStyle());
    const Font& font = styleToUse.font();
    int from = !isLeftToRightDirection() ? offset - m_start : 0;
    int to = !isLeftToRightDirection() ? m_len : offset - m_start;
    // FIXME: Do we need to add rightBearing here?
    return LayoutUnit(font.selectionRectForText(constructTextRun(styleToUse, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX());
}
예제 #19
0
bool InlineTextBox::hasWrappedSelectionNewline() const {
  // TODO(wkorman): We shouldn't need layout at this point and it should be
  // enforced by DocumentLifecycle. http://crbug.com/537821
  // Bail out as currently looking up selection state can cause the editing code
  // can force a re-layout while scrutinizing the editing position, and
  // InlineTextBox instances are not guaranteed to survive a re-layout.
  if (getLineLayoutItem().needsLayout())
    return false;

  SelectionState state = getSelectionState();
  return (state == SelectionStart || state == SelectionInside)
         // Checking last leaf child can be slow, so we make sure to do this
         // only after the other simple conditionals.
         && (root().lastLeafChild() == this)
         // It's possible to have mixed LTR/RTL on a single line, and we only
         // want to paint a newline when we're the last leaf child and we make
         // sure there isn't a differently-directioned box following us.
         && ((!isLeftToRightDirection() && root().firstSelectedBox() == this) ||
             (isLeftToRightDirection() && root().lastSelectedBox() == this));
}
예제 #20
0
void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) const {
  int startPos, endPos;
  if (getLineLayoutItem().getSelectionState() == SelectionInside) {
    startPos = 0;
    endPos = getLineLayoutItem().textLength();
  } else {
    getLineLayoutItem().selectionStartEnd(startPos, endPos);
    if (getLineLayoutItem().getSelectionState() == SelectionStart)
      endPos = getLineLayoutItem().textLength();
    else if (getLineLayoutItem().getSelectionState() == SelectionEnd)
      startPos = 0;
  }

  sPos = std::max(startPos - m_start, 0);
  ePos = std::min(endPos - m_start, (int)m_len);
}
예제 #21
0
String InlineTextBox::text() const {
  return getLineLayoutItem().text().substring(start(), len());
}
예제 #22
0
bool InlineTextBox::isLineBreak() const {
  return getLineLayoutItem().isBR() ||
         (getLineLayoutItem().style()->preserveNewline() && len() == 1 &&
          (*getLineLayoutItem().text().impl())[start()] == '\n');
}
예제 #23
0
LayoutUnit InlineTextBox::placeEllipsisBox(bool flowIsLTR,
                                           LayoutUnit visibleLeftEdge,
                                           LayoutUnit visibleRightEdge,
                                           LayoutUnit ellipsisWidth,
                                           LayoutUnit& truncatedWidth,
                                           bool& foundBox) {
  if (foundBox) {
    setTruncation(cFullTruncation);
    return LayoutUnit(-1);
  }

  // For LTR this is the left edge of the box, for RTL, the right edge in parent
  // coordinates.
  LayoutUnit ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth
                                   : visibleLeftEdge + ellipsisWidth;

  // Criteria for full truncation:
  // LTR: the left edge of the ellipsis is to the left of our text run.
  // RTL: the right edge of the ellipsis is to the right of our text run.
  bool ltrFullTruncation = flowIsLTR && ellipsisX <= logicalLeft();
  bool rtlFullTruncation =
      !flowIsLTR && ellipsisX >= logicalLeft() + logicalWidth();
  if (ltrFullTruncation || rtlFullTruncation) {
    // Too far.  Just set full truncation, but return -1 and let the ellipsis
    // just be placed at the edge of the box.
    setTruncation(cFullTruncation);
    foundBox = true;
    return LayoutUnit(-1);
  }

  bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < logicalRight());
  bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > logicalLeft());
  if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) {
    foundBox = true;

    // The inline box may have different directionality than it's parent. Since
    // truncation behavior depends both on both the parent and the inline
    // block's directionality, we must keep track of these separately.
    bool ltr = isLeftToRightDirection();
    if (ltr != flowIsLTR) {
      // Width in pixels of the visible portion of the box, excluding the
      // ellipsis.
      int visibleBoxWidth =
          (visibleRightEdge - visibleLeftEdge - ellipsisWidth).toInt();
      ellipsisX = flowIsLTR ? logicalLeft() + visibleBoxWidth
                            : logicalRight() - visibleBoxWidth;
    }

    // The box's width includes partial glyphs, so respect that when placing
    // the ellipsis.
    int offset = offsetForPosition(ellipsisX);
    if (offset == 0 && ltr == flowIsLTR) {
      // No characters should be laid out.  Set ourselves to full truncation and
      // place the ellipsis at the min of our start and the ellipsis edge.
      setTruncation(cFullTruncation);
      truncatedWidth += ellipsisWidth;
      return std::min(ellipsisX, logicalLeft());
    }

    // Set the truncation index on the text run.
    setTruncation(offset);

    // If we got here that means that we were only partially truncated and we
    // need to return the pixel offset at which to place the ellipsis. Where the
    // text and its flow have opposite directions then our offset into the text
    // is at the start of the part that will be visible.
    LayoutUnit widthOfVisibleText(getLineLayoutItem().width(
        ltr == flowIsLTR ? m_start : m_start + offset,
        ltr == flowIsLTR ? offset : m_len - offset, textPos(),
        flowIsLTR ? LTR : RTL, isFirstLineStyle()));

    // The ellipsis needs to be placed just after the last visible character.
    // Where "after" is defined by the flow directionality, not the inline
    // box directionality.
    // e.g. In the case of an LTR inline box truncated in an RTL flow then we
    // can have a situation such as |Hello| -> |...He|
    truncatedWidth += widthOfVisibleText + ellipsisWidth;
    if (flowIsLTR)
      return logicalLeft() + widthOfVisibleText;
    return logicalRight() - widthOfVisibleText - ellipsisWidth;
  }
  truncatedWidth += logicalWidth();
  return LayoutUnit(-1);
}
예제 #24
0
void InlineTextBox::deleteLine() {
  getLineLayoutItem().removeTextBox(this);
  destroy();
}
예제 #25
0
float InlineTextBox::newlineSpaceWidth() const {
  const ComputedStyle& styleToUse =
      getLineLayoutItem().styleRef(isFirstLineStyle());
  return styleToUse.font().spaceWidth();
}