TextRun SVGTextMetrics::constructTextRun(LineLayoutSVGInlineText textLayoutItem, unsigned position, unsigned length, TextDirection textDirection)
{
    const ComputedStyle& style = textLayoutItem.styleRef();

    TextRun run(static_cast<const LChar*>(nullptr) // characters, will be set below if non-zero.
                , 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
                , textDirection
                , isOverride(style.unicodeBidi()) /* directionalOverride */);

    if (length) {
        if (textLayoutItem.is8Bit())
            run.setText(textLayoutItem.characters8() + position, length);
        else
            run.setText(textLayoutItem.characters16() + position, 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(textLayoutItem.textLength() - position);
    ASSERT(run.charactersLength() >= run.length());
    return run;
}
Example #2
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);
}
void SVGTextMetricsCalculator::setupBidiRuns()
{
    const ComputedStyle& style = m_text.styleRef();
    m_textDirection = style.direction();
    if (isOverride(style.unicodeBidi()))
        return;

    BidiStatus status(LTR, false);
    status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
    m_bidiResolver.setStatus(status);
    m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0));
    const bool hardLineBreak = false;
    const bool reorderRuns = false;
    m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns);
    BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs();
    m_bidiRun = bidiRuns.firstRun();
}
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);
}
Example #5
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);
  }
}