Esempio n. 1
0
void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
{
    if (paintingDisabled())
        return;

    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
    bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
    bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));

    // FIXME: This ownership should be reversed. We should pass BidiRunList
    // to BidiResolver in createBidiRunsForLine.
    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
    if (!bidiRuns.runCount())
        return;

    FloatPoint currPoint = point;
    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
    while (bidiRun) {
        TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
        bool isRTL = bidiRun->level() % 2;
        subrun.setDirection(isRTL ? RTL : LTR);
        subrun.setDirectionalOverride(bidiRun->dirOverride(false));

        font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);

        bidiRun = bidiRun->next();
        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
        if (bidiRun)
            currPoint.move(font.width(subrun), 0);
    }

    bidiRuns.deleteRuns();
}
Esempio n. 2
0
void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
                           const FloatPoint& point, int from, int to) const
{
    if (!run.length())
        return;

    SkCanvas* canvas = gc->platformContext()->canvas();
    int textMode = gc->platformContext()->getTextDrawingMode();
    bool fill = textMode & cTextFill;
    bool stroke = (textMode & cTextStroke)
               && gc->platformContext()->getStrokeStyle() != NoStroke
               && gc->platformContext()->getStrokeThickness() > 0;

    if (!fill && !stroke)
        return;

    SkPaint strokePaint, fillPaint;
    if (fill) {
        gc->platformContext()->setupPaintForFilling(&fillPaint);
        setupForTextPainting(&fillPaint, gc->fillColor().rgb());
    }
    if (stroke) {
        gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
        setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
    }

    TextRunWalker walker(run, point.x(), this);
    walker.setWordSpacingAdjustment(wordSpacing());
    walker.setLetterSpacingAdjustment(letterSpacing());
    walker.setPadding(run.padding());

    while (walker.nextScriptRun()) {
        if (fill) {
            walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
            adjustTextRenderMode(&fillPaint, gc->platformContext());
            canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint);
        }

        if (stroke) {
            walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
            adjustTextRenderMode(&strokePaint, gc->platformContext());
            canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint);
        }
    }
}
Esempio n. 3
0
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h,
                                            int from, int to) const
{
    UniscribeController it(this, run);
    it.advance(from);
    float beforeWidth = it.runWidthSoFar();
    it.advance(to);
    float afterWidth = it.runWidthSoFar();

    // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning
    if (run.rtl()) {
        it.advance(run.length());
        float totalWidth = it.runWidthSoFar();
        return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h);
    } 
    
    return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
}
Esempio n. 4
0
static bool isOneLeftToRightRun(const TextRun& run)
{
    for (int i = 0; i < run.length(); i++) {
        WTF::Unicode::Direction direction = WTF::Unicode::direction(run[i]);
        if (direction == WTF::Unicode::RightToLeft || direction > WTF::Unicode::OtherNeutral)
            return false;
    }
    return true;
}
Esempio n. 5
0
// Return the rectangle for selecting the given range of code-points in the TextRun.
FloatRect Font::selectionRectForComplexText(const TextRun& run,
                                            const FloatPoint& point, int height,
                                            int from, int to) const
{
    ComplexTextController controller(this, run, 0, 0);
    if (run.rtl())
        controller.setupForRTL();
    return controller.selectionRect(point, height, from, to);
}
Esempio n. 6
0
FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
{
    GlyphBuffer glyphBuffer;
    WidthIterator it(this, run);
    it.advance(from, &glyphBuffer);
    float beforeWidth = it.m_runWidthSoFar;
    it.advance(to, &glyphBuffer);
    float afterWidth = it.m_runWidthSoFar;

    // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning.
    if (run.rtl()) {
        it.advance(run.length(), &glyphBuffer);
        float totalWidth = it.m_runWidthSoFar;
        return FloatRect(floorf(point.x() + totalWidth - afterWidth), point.y(), roundf(point.x() + totalWidth - beforeWidth) - floorf(point.x() + totalWidth - afterWidth), h);
    }

    return FloatRect(floorf(point.x() + beforeWidth), point.y(), roundf(point.x() + afterWidth) - floorf(point.x() + beforeWidth), h);
}
void Font::drawComplexText(GraphicsContext* gc, TextRun const& run,
                           FloatPoint const& point, int, int) const
{
    SkCanvas*   canvas = gc->platformContext()->mCanvas;
    SkPaint     paint;

    if (!setupForText(&paint, gc, primaryFont())) {
        return;
    }

    // go to chars, instead of glyphs, which was set by setupForText()
    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);

    canvas->drawText(run.characters(), run.length() << 1,
                     SkFloatToScalar(point.x()), SkFloatToScalar(point.y()),
                     paint);

}
Esempio n. 8
0
FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
{
    to = (to == -1 ? run.length() : to);

    if (codePath(run) != Complex)
        return selectionRectForSimpleText(run, point, h, from, to);

    return selectionRectForComplexText(run, point, h, from, to);
}
Esempio n. 9
0
void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const
{
    if (loadingCustomFonts())
        return;

    if (to < 0)
        to = run.length();

    CodePath codePathToUse = codePath(run);
    // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
    if (codePathToUse != Complex && typesettingFeatures() && (from || to != run.length()))
        codePathToUse = Complex;

    if (codePathToUse != Complex)
        drawEmphasisMarksForSimpleText(context, run, mark, point, from, to);
    else
        drawEmphasisMarksForComplexText(context, run, mark, point, from, to);
}
CharacterRange CachingWordShaper::getCharacterRange(const Font* font,
    const TextRun& run, unsigned from, unsigned to)
{
    ShapeResultBuffer buffer;
    float totalWidth = shapeResultsForRun(m_shapeCache, font, run, nullptr,
        &buffer);

    return buffer.getCharacterRange(run.direction(), totalWidth, from, to);
}
Esempio n. 11
0
void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
{
    // Don't draw anything while we are using custom fonts that are in the process of loading.
    if (loadingCustomFonts())
        return;
    
    to = (to == -1 ? run.length() : to);

    CodePath codePathToUse = codePath(run);
    // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
    if (codePathToUse != Complex && typesettingFeatures() && (from || to != run.length()))
        codePathToUse = Complex;

    if (codePathToUse != Complex)
        return drawSimpleText(context, run, point, from, to);

    return drawComplexText(context, run, point, from, to);
}
Esempio n. 12
0
void copyTextRunTo(const TextRun& run, UChar *word)
{
    int word_size = run.length() - run.from(), j = 0;
    for (int i = run.from(); i < run.length(); i++)
    {
        if (Font::treatAsSpace(run[i]))
        {
            word[j] = ' ';
            j++;
        }
        else
        {
            word[j] = run[i];
            j++;
        }
    }
    word[j]='\0';
}
static bool isOneLeftToRightRun(const TextRun& run)
{
    for (int i = 0; i < run.length(); i++) {
        UCharDirection direction = u_charDirection(run[i]);
        if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL)
            return false;
    }
    return true;
}
Esempio n. 14
0
TextRun constructTextRun(const Font& font,
                         const LineLayoutText text,
                         unsigned offset,
                         unsigned length,
                         const ComputedStyle& style) {
  ASSERT(offset + length <= text.textLength());
  if (text.hasEmptyText())
    return constructTextRunInternal(font, static_cast<const LChar*>(nullptr), 0,
                                    style, LTR);
  if (text.is8Bit())
    return constructTextRunInternal(font, text.characters8() + offset, length,
                                    style, LTR);

  TextRun run = constructTextRunInternal(font, text.characters16() + offset,
                                         length, style, LTR);
  run.setDirection(directionForRun(run));
  return run;
}
Esempio n. 15
0
void ShapeResult::applySpacing(ShapeResultSpacing& spacing,
                               const TextRun& textRun) {
  float offsetX, offsetY;
  float& offset = spacing.isVerticalOffset() ? offsetY : offsetX;
  float totalSpace = 0;
  for (auto& run : m_runs) {
    if (!run)
      continue;
    float totalSpaceForRun = 0;
    for (size_t i = 0; i < run->m_glyphData.size(); i++) {
      HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i];

      // Skip if it's not a grapheme cluster boundary.
      if (i + 1 < run->m_glyphData.size() &&
          glyphData.characterIndex == run->m_glyphData[i + 1].characterIndex) {
        // In RTL, marks need the same letter-spacing offset as the base.
        if (textRun.rtl() && spacing.letterSpacing()) {
          offsetX = offsetY = 0;
          offset = spacing.letterSpacing();
          glyphData.offset.expand(offsetX, offsetY);
        }
      } else {
        offsetX = offsetY = 0;
        float space = spacing.computeSpacing(
            textRun, run->m_startIndex + glyphData.characterIndex, offset);
        glyphData.advance += space;
        totalSpaceForRun += space;
        if (textRun.rtl()) {
          // In RTL, spacing should be added to left side of glyphs.
          offset += space;
        }
        glyphData.offset.expand(offsetX, offsetY);
      }
      m_hasVerticalOffsets |= (glyphData.offset.height() != 0);
    }
    run->m_width += totalSpaceForRun;
    totalSpace += totalSpaceForRun;
  }
  m_width += totalSpace;
  if (spacing.isVerticalOffset())
    m_glyphBoundingBox.setHeight(m_glyphBoundingBox.height() + totalSpace);
  else
    m_glyphBoundingBox.setWidth(m_glyphBoundingBox.width() + totalSpace);
}
Esempio n. 16
0
int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
{
    PangoLayout* layout = getDefaultPangoLayout(run);
    setPangoAttributes(this, run, layout);

    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
    pango_layout_set_text(layout, utf8, -1);

    int index, trailing;
    pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing);
    glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index);
    if (includePartialGlyphs)
        offset += trailing;

    g_free(utf8);
    g_object_unref(layout);

    return offset;
}
Esempio n. 17
0
float Font::floatWidthForComplexText(const TextRun& run) const
{
    if (run.length() == 0)
        return 0.0f;

    PangoLayout* layout = getDefaultPangoLayout(run);
    setPangoAttributes(this, run, layout);

    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
    pango_layout_set_text(layout, utf8, -1);

    int width;
    pango_layout_get_pixel_size(layout, &width, 0);

    g_free(utf8);
    g_object_unref(layout);

    return width;
}
Esempio n. 18
0
float Font::floatWidthForComplexText(const TextRun& run, const TextStyle& style) const
{
    if (run.length() == 0)
        return 0.0f;

    PangoLayout* layout = getDefaultPangoLayout(run);
    setPangoAttributes(this, run, layout, style.rtl());

    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
    pango_layout_set_text(layout, utf8, -1);
    g_free(utf8);

    int layoutWidth;
    pango_layout_get_size(layout, &layoutWidth, 0);
    float width = (float)layoutWidth / (double)PANGO_SCALE;
    g_object_unref(layout);

    return width;
}
FloatRect CachingWordShaper::selectionRect(const Font* font, const TextRun& run,
    const FloatPoint& point, int height, unsigned from, unsigned to)
{
    Vector<RefPtr<ShapeResult>> results;
    float totalWidth = shapeResultsForRun(m_shapeCache, font, run, nullptr,
        &results);

    return ShapeResult::selectionRect(results, run.direction(), totalWidth,
        point, height, from, to);
}
void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun)
{
    releasePaintingResource(context, /* path */0);

#if ENABLE(SVG_FONTS)
    TextRun::RenderingContext* renderingContext = textRun.renderingContext();
    if (renderingContext)
        static_cast<SVGTextRunRenderingContext*>(renderingContext)->setActivePaintingResource(0);
#endif
}
Esempio n. 21
0
float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
{
    if (!primaryFont()->platformData().size())
        return 0;

    if (!run.length())
        return 0;

    String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    QString string = fromRawDataWithoutRef(sanitized);

    int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);

    // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
    if (treatAsSpace(run[0]))
        w -= m_wordSpacing;

    return w + run.expansion();
}
Esempio n. 22
0
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const
{
   float scalingFactor = renderer().scalingFactor();
    ASSERT(scalingFactor);

    RenderStyle* style = renderer().style();
    ASSERT(style);

    TextRun textRun = constructTextRun(style, fragment);

    // Eventually handle lengthAdjust="spacingAndGlyphs".
    // FIXME: Handle vertical text.
    AffineTransform fragmentTransform;
    fragment.buildFragmentTransform(fragmentTransform);
    if (!fragmentTransform.isIdentity())
        textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale()));

    return fragment.characterOffset - start() + renderer().scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs);
}
Esempio n. 23
0
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
{
#if USE(FREETYPE)
    if (!primaryFont()->platformData().m_pattern)
        return selectionRectForSimpleText(run, point, h, from, to);
#endif

    PangoLayout* layout = getDefaultPangoLayout(run);
    setPangoAttributes(this, run, layout);

    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
    pango_layout_set_text(layout, utf8, -1);

    char* start = g_utf8_offset_to_pointer(utf8, from);
    char* end = g_utf8_offset_to_pointer(start, to - from);

    if (run.ltr()) {
        from = start - utf8;
        to = end - utf8;
    } else {
        from = end - utf8;
        to = start - utf8;
    }

    PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0);
    int xPos;

    xPos = 0;
    if (from < layoutLine->length)
        pango_layout_line_index_to_x(layoutLine, from, FALSE, &xPos);
    float beforeWidth = PANGO_PIXELS_FLOOR(xPos);

    xPos = 0;
    if (run.ltr() || to < layoutLine->length)
        pango_layout_line_index_to_x(layoutLine, to, FALSE, &xPos);
    float afterWidth = PANGO_PIXELS(xPos);

    g_free(utf8);
    g_object_unref(layout);

    return FloatRect(point.x() + beforeWidth, point.y(), afterWidth - beforeWidth, h);
}
Esempio n. 24
0
int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const
{
    // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers
    // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem.
    int position = static_cast<int>(xFloat);

    TextRunComponents components;
    int w = generateComponents(&components, *this, run);

    if (position >= w)
        return run.length();

    int offset = 0;
    if (run.rtl()) {
        for (size_t i = 0; i < components.size(); ++i) {
            const TextRunComponent& comp = components.at(i);
            int xe = w - comp.m_offset;
            int xs = xe - comp.m_width;
            if (position >= xs)
                return offset + (comp.isSpace()
                    ? static_cast<int>((position - xe) * comp.m_spaces / std::max(1.f, comp.m_width) + 0.5)
                    : offsetForPositionForSimpleText(comp.m_textRun, position - xs, includePartialGlyphs));

            offset += comp.textLength();
        }
    } else {
        for (size_t i = 0; i < components.size(); ++i) {
            const TextRunComponent& comp = components.at(i);
            int xs = comp.m_offset;
            int xe = xs + comp.m_width;
            if (position <= xe) {
                if (position - xs >= xe)
                    return offset + comp.textLength();
                return offset + (comp.isSpace()
                    ? static_cast<int>((position - xs) * comp.m_spaces / std::max(1.f, comp.m_width) + 0.5)
                    : offsetForPositionForSimpleText(comp.m_textRun, position - xs, includePartialGlyphs));
            }
            offset += comp.textLength();
        }
    }
    return run.length();
}
Esempio n. 25
0
void Font::drawComplexText(GraphicsContext* gc, TextRun const& run,
                           FloatPoint const& point, int, int) const
{
    if (!run.length())
        return;

    int mode = gc->textDrawingMode();
    bool fill = mode & TextModeFill;
    bool stroke = mode & TextModeStroke;
    if (!fill && !stroke)
        return;

    SkPaint fillPaint, strokePaint;
    if (fill)
        setupFill(&fillPaint, gc, primaryFont());
    if (stroke)
        setupStroke(&strokePaint, gc, primaryFont());

    SkCanvas* canvas = gc->platformContext()->recordingCanvas();

    bool haveMultipleLayers = isCanvasMultiLayered(canvas);
    TextRunWalker walker(run, point.x(), point.y(), this);
    walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
    walker.setPadding(run.expansion());

    while (walker.nextScriptRun()) {
        if (fill) {
            walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
            adjustTextRenderMode(&fillPaint, haveMultipleLayers);
            canvas->drawPosText(walker.glyphs(), walker.length() << 1,
                                walker.positions(), fillPaint);
        }
        if (stroke) {
            walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
            adjustTextRenderMode(&strokePaint, haveMultipleLayers);
            canvas->drawPosText(walker.glyphs(), walker.length() << 1,
                                walker.positions(), strokePaint);
        }
    }

    gc->platformContext()->endRecording();
}
Esempio n. 26
0
SVGTextMetrics::SVGTextMetrics(RenderSVGInlineText* textRenderer, const TextRun& run)
{
    ASSERT(textRenderer);

    float scalingFactor = textRenderer->scalingFactor();
    ASSERT(scalingFactor);

    const Font& scaledFont = textRenderer->scaledFont();
    int length = 0;

    // Calculate width/height using the scaled font, divide this result by the scalingFactor afterwards.
    m_width = scaledFont.width(run, length, m_glyph.name) / scalingFactor;
    m_height = scaledFont.fontMetrics().floatHeight() / scalingFactor;

    m_glyph.unicodeString = run.is8Bit() ? String(run.characters8(), length) : String(run.characters16(), length);
    m_glyph.isValid = true;

    ASSERT(length >= 0);
    m_length = static_cast<unsigned>(length);
}
Esempio n. 27
0
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts) const
{
#if OS(WINDOWS)
    UniscribeController controller(this, run, fallbackFonts);
    controller.advance(run.length());
    return controller.runWidthSoFar();
#else
    notImplemented();
    return 0;
#endif
}
Esempio n. 28
0
void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to, CustomFontNotReadyAction customFontNotReadyAction) const
{
    // Don't draw anything while we are using custom fonts that are in the process of loading,
    // except if the 'force' argument is set to true (in which case it will use a fallback
    // font).
    if (loadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady)
        return;
    
    to = (to == -1 ? run.length() : to);

    CodePath codePathToUse = codePath(run);
    // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
    if (codePathToUse != Complex && typesettingFeatures() && (from || to != run.length()))
        codePathToUse = Complex;

    if (codePathToUse != Complex)
        return drawSimpleText(context, run, point, from, to);

    return drawComplexText(context, run, point, from, to);
}
Esempio n. 29
0
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, FloatWillBeLayoutUnit position, bool includePartialGlyphs) const
{
    LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(this->layoutObject());

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

    const ComputedStyle& style = textLayoutObject.styleRef();

    TextRun textRun = constructTextRun(style, fragment);

    // Eventually handle lengthAdjust="spacingAndGlyphs".
    // FIXME: Handle vertical text.
    AffineTransform fragmentTransform;
    fragment.buildFragmentTransform(fragmentTransform);
    if (!fragmentTransform.isIdentity())
        textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale()));

    return fragment.characterOffset - start() + textLayoutObject.scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs);
}
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, LayoutUnit position, bool includePartialGlyphs) const
{
    LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->lineLayoutItem());

    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(narrowPrecisionToFloat(fragmentTransform.xScale()));
    }

    return fragment.characterOffset - start() + lineLayoutItem.scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs);
}