Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2006, 2007 Apple Computer, Inc.
      3  * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "config.h"
     33 #include "Font.h"
     34 
     35 #include "FontFallbackList.h"
     36 #include "GlyphBuffer.h"
     37 #include "NotImplemented.h"
     38 #include "PlatformBridge.h"
     39 #include "PlatformContextSkia.h"
     40 #include "SimpleFontData.h"
     41 #include "SkiaFontWin.h"
     42 #include "SkiaUtils.h"
     43 #include "TransparencyWin.h"
     44 #include "UniscribeHelperTextRun.h"
     45 
     46 #include "skia/ext/platform_canvas.h"
     47 #include "skia/ext/skia_utils_win.h"  // FIXME: remove this dependency.
     48 
     49 #include <windows.h>
     50 
     51 namespace WebCore {
     52 
     53 namespace {
     54 
     55 bool canvasHasMultipleLayers(const SkCanvas* canvas)
     56 {
     57     SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
     58     iter.next();  // There is always at least one layer.
     59     return !iter.done();  // There is > 1 layer if the the iterator can stil advance.
     60 }
     61 
     62 class TransparencyAwareFontPainter {
     63 public:
     64     TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&);
     65     ~TransparencyAwareFontPainter();
     66 
     67 protected:
     68     // Called by our subclass' constructor to initialize GDI if necessary. This
     69     // is a separate function so it can be called after the subclass finishes
     70     // construction (it calls virtual functions).
     71     void init();
     72 
     73     virtual IntRect estimateTextBounds() = 0;
     74 
     75     // Use the context from the transparency helper when drawing with GDI. It
     76     // may point to a temporary one.
     77     GraphicsContext* m_graphicsContext;
     78     PlatformGraphicsContext* m_platformContext;
     79 
     80     FloatPoint m_point;
     81 
     82     // Set when Windows can handle the type of drawing we're doing.
     83     bool m_useGDI;
     84 
     85     // These members are valid only when m_useGDI is set.
     86     HDC m_hdc;
     87     TransparencyWin m_transparency;
     88 
     89 private:
     90     // Call when we're using GDI mode to initialize the TransparencyWin to help
     91     // us draw GDI text.
     92     void initializeForGDI();
     93 
     94     bool m_createdTransparencyLayer;  // We created a layer to give the font some alpha.
     95 };
     96 
     97 TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context,
     98                                                            const FloatPoint& point)
     99     : m_graphicsContext(context)
    100     , m_platformContext(context->platformContext())
    101     , m_point(point)
    102     , m_useGDI(windowsCanHandleTextDrawing(context))
    103     , m_hdc(0)
    104     , m_createdTransparencyLayer(false)
    105 {
    106 }
    107 
    108 void TransparencyAwareFontPainter::init()
    109 {
    110     if (m_useGDI)
    111         initializeForGDI();
    112 }
    113 
    114 void TransparencyAwareFontPainter::initializeForGDI()
    115 {
    116     m_graphicsContext->save();
    117     SkColor color = m_platformContext->effectiveFillColor();
    118     // Used only when m_createdTransparencyLayer is true.
    119     float layerAlpha = 0.0f;
    120     if (SkColorGetA(color) != 0xFF) {
    121         // When the font has some transparency, apply it by creating a new
    122         // transparency layer with that opacity applied. We'll actually create
    123         // a new transparency layer after we calculate the bounding box.
    124         m_createdTransparencyLayer = true;
    125         layerAlpha = SkColorGetA(color) / 255.0f;
    126         // The color should be opaque now.
    127         color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
    128     }
    129 
    130     TransparencyWin::LayerMode layerMode;
    131     IntRect layerRect;
    132     if (m_platformContext->isDrawingToImageBuffer()) {
    133         // Assume if we're drawing to an image buffer that the background
    134         // is not opaque and we have to undo ClearType. We may want to
    135         // enhance this to actually check, since it will often be opaque
    136         // and we could do ClearType in that case.
    137         layerMode = TransparencyWin::TextComposite;
    138         layerRect = estimateTextBounds();
    139         m_graphicsContext->clip(layerRect);
    140         if (m_createdTransparencyLayer)
    141             m_graphicsContext->beginTransparencyLayer(layerAlpha);
    142 
    143         // The transparency helper requires that we draw text in black in
    144         // this mode and it will apply the color.
    145         m_transparency.setTextCompositeColor(color);
    146         color = SkColorSetRGB(0, 0, 0);
    147     } else if (m_createdTransparencyLayer || canvasHasMultipleLayers(m_platformContext->canvas())) {
    148         // When we're drawing a web page, we know the background is opaque,
    149         // but if we're drawing to a layer, we still need extra work.
    150         layerMode = TransparencyWin::OpaqueCompositeLayer;
    151         layerRect = estimateTextBounds();
    152         m_graphicsContext->clip(layerRect);
    153         if (m_createdTransparencyLayer)
    154             m_graphicsContext->beginTransparencyLayer(layerAlpha);
    155     } else {
    156         // Common case of drawing onto the bottom layer of a web page: we
    157         // know everything is opaque so don't need to do anything special.
    158         layerMode = TransparencyWin::NoLayer;
    159     }
    160 
    161     // Bug 26088 - init() might fail if layerRect is invalid. Given this, we
    162     // need to be careful to check for null pointers everywhere after this call
    163     m_transparency.init(m_graphicsContext, layerMode,
    164                         TransparencyWin::KeepTransform, layerRect);
    165 
    166     // Set up the DC, using the one from the transparency helper.
    167     if (m_transparency.platformContext()) {
    168         m_hdc = skia::BeginPlatformPaint(m_transparency.platformContext()->canvas());
    169         SetTextColor(m_hdc, skia::SkColorToCOLORREF(color));
    170         SetBkMode(m_hdc, TRANSPARENT);
    171     }
    172 }
    173 
    174 TransparencyAwareFontPainter::~TransparencyAwareFontPainter()
    175 {
    176     if (!m_useGDI || !m_graphicsContext || !m_platformContext)
    177         return;  // Nothing to do.
    178     m_transparency.composite();
    179     if (m_createdTransparencyLayer)
    180         m_graphicsContext->endTransparencyLayer();
    181     m_graphicsContext->restore();
    182     skia::EndPlatformPaint(m_platformContext->canvas());
    183 }
    184 
    185 // Specialization for simple GlyphBuffer painting.
    186 class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter {
    187  public:
    188     TransparencyAwareGlyphPainter(GraphicsContext*,
    189                                   const SimpleFontData*,
    190                                   const GlyphBuffer&,
    191                                   int from, int numGlyphs,
    192                                   const FloatPoint&);
    193     ~TransparencyAwareGlyphPainter();
    194 
    195     // Draws the partial string of glyphs, starting at |startAdvance| to the
    196     // left of m_point. We express it this way so that if we're using the Skia
    197     // drawing path we can use floating-point positioning, even though we have
    198     // to use integer positioning in the GDI path.
    199     bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, float startAdvance) const;
    200 
    201  private:
    202     virtual IntRect estimateTextBounds();
    203 
    204     const SimpleFontData* m_font;
    205     const GlyphBuffer& m_glyphBuffer;
    206     int m_from;
    207     int m_numGlyphs;
    208 
    209     // When m_useGdi is set, this stores the previous HFONT selected into the
    210     // m_hdc so we can restore it.
    211     HGDIOBJ m_oldFont;  // For restoring the DC to its original state.
    212 };
    213 
    214 TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter(
    215     GraphicsContext* context,
    216     const SimpleFontData* font,
    217     const GlyphBuffer& glyphBuffer,
    218     int from, int numGlyphs,
    219     const FloatPoint& point)
    220     : TransparencyAwareFontPainter(context, point)
    221     , m_font(font)
    222     , m_glyphBuffer(glyphBuffer)
    223     , m_from(from)
    224     , m_numGlyphs(numGlyphs)
    225     , m_oldFont(0)
    226 {
    227     init();
    228 
    229     if (m_hdc)
    230         m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont());
    231 }
    232 
    233 TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter()
    234 {
    235     if (m_useGDI && m_hdc)
    236         ::SelectObject(m_hdc, m_oldFont);
    237 }
    238 
    239 
    240 // Estimates the bounding box of the given text. This is copied from
    241 // FontCGWin.cpp, it is possible, but a lot more work, to get the precide
    242 // bounds.
    243 IntRect TransparencyAwareGlyphPainter::estimateTextBounds()
    244 {
    245     int totalWidth = 0;
    246     for (int i = 0; i < m_numGlyphs; i++)
    247         totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i));
    248 
    249     const FontMetrics& fontMetrics = m_font->fontMetrics();
    250     return IntRect(m_point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2,
    251                    m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
    252                    totalWidth + fontMetrics.ascent() + fontMetrics.descent(),
    253                    fontMetrics.lineSpacing());
    254 }
    255 
    256 bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs,
    257                                                const WORD* glyphs,
    258                                                const int* advances,
    259                                                float startAdvance) const
    260 {
    261     if (!m_useGDI) {
    262         SkPoint origin = m_point;
    263         origin.fX += SkFloatToScalar(startAdvance);
    264         return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(),
    265                              numGlyphs, glyphs, advances, 0, &origin);
    266     }
    267 
    268     if (!m_graphicsContext || !m_hdc)
    269         return true;
    270 
    271     // Windows' origin is the top-left of the bounding box, so we have
    272     // to subtract off the font ascent to get it.
    273     int x = lroundf(m_point.x() + startAdvance);
    274     int y = lroundf(m_point.y() - m_font->fontMetrics().ascent());
    275 
    276     // If there is a non-blur shadow and both the fill color and shadow color
    277     // are opaque, handle without skia.
    278     FloatSize shadowOffset;
    279     float shadowBlur;
    280     Color shadowColor;
    281     ColorSpace shadowColorSpace;
    282     if (m_graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace)) {
    283         // If there is a shadow and this code is reached, windowsCanHandleDrawTextShadow()
    284         // will have already returned true during the ctor initiatization of m_useGDI
    285         ASSERT(shadowColor.alpha() == 255);
    286         ASSERT(m_graphicsContext->fillColor().alpha() == 255);
    287         ASSERT(shadowBlur == 0);
    288         COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue()));
    289         COLORREF savedTextColor = GetTextColor(m_hdc);
    290         SetTextColor(m_hdc, textColor);
    291         ExtTextOut(m_hdc, x + shadowOffset.width(), y + shadowOffset.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
    292         SetTextColor(m_hdc, savedTextColor);
    293     }
    294 
    295     return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
    296 }
    297 
    298 class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter {
    299  public:
    300     TransparencyAwareUniscribePainter(GraphicsContext*,
    301                                       const Font*,
    302                                       const TextRun&,
    303                                       int from, int to,
    304                                       const FloatPoint&);
    305     ~TransparencyAwareUniscribePainter();
    306 
    307     // Uniscibe will draw directly into our buffer, so we need to expose our DC.
    308     HDC hdc() const { return m_hdc; }
    309 
    310  private:
    311     virtual IntRect estimateTextBounds();
    312 
    313     const Font* m_font;
    314     const TextRun& m_run;
    315     int m_from;
    316     int m_to;
    317 };
    318 
    319 TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter(
    320     GraphicsContext* context,
    321     const Font* font,
    322     const TextRun& run,
    323     int from, int to,
    324     const FloatPoint& point)
    325     : TransparencyAwareFontPainter(context, point)
    326     , m_font(font)
    327     , m_run(run)
    328     , m_from(from)
    329     , m_to(to)
    330 {
    331     init();
    332 }
    333 
    334 TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter()
    335 {
    336 }
    337 
    338 IntRect TransparencyAwareUniscribePainter::estimateTextBounds()
    339 {
    340     // This case really really sucks. There is no convenient way to estimate
    341     // the bounding box. So we run Uniscribe twice. If we find this happens a
    342     // lot, the way to fix it is to make the extra layer after the
    343     // UniscribeHelper has measured the text.
    344     IntPoint intPoint(lroundf(m_point.x()),
    345                       lroundf(m_point.y()));
    346 
    347     UniscribeHelperTextRun state(m_run, *m_font);
    348     int left = lroundf(m_point.x()) + state.characterToX(m_from);
    349     int right = lroundf(m_point.x()) + state.characterToX(m_to);
    350 
    351     // Adjust for RTL script since we just want to know the text bounds.
    352     if (left > right)
    353         std::swap(left, right);
    354 
    355     // This algorithm for estimating how much extra space we need (the text may
    356     // go outside the selection rect) is based roughly on
    357     // TransparencyAwareGlyphPainter::estimateTextBounds above.
    358     const FontMetrics& fontMetrics = m_font->fontMetrics();
    359     return IntRect(left - (fontMetrics.ascent() + fontMetrics.descent()) / 2,
    360                    m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
    361                    (right - left) + fontMetrics.ascent() + fontMetrics.descent(),
    362                    fontMetrics.lineSpacing());
    363 }
    364 
    365 }  // namespace
    366 
    367 bool Font::canReturnFallbackFontsForComplexText()
    368 {
    369     return false;
    370 }
    371 
    372 bool Font::canExpandAroundIdeographsInComplexText()
    373 {
    374     return false;
    375 }
    376 
    377 static void drawGlyphsWin(GraphicsContext* graphicsContext,
    378                           const SimpleFontData* font,
    379                           const GlyphBuffer& glyphBuffer,
    380                           int from,
    381                           int numGlyphs,
    382                           const FloatPoint& point) {
    383     graphicsContext->platformContext()->prepareForSoftwareDraw();
    384 
    385     TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
    386 
    387     // We draw the glyphs in chunks to avoid having to do a heap allocation for
    388     // the arrays of characters and advances. Since ExtTextOut is the
    389     // lowest-level text output function on Windows, there should be little
    390     // penalty for splitting up the text. On the other hand, the buffer cannot
    391     // be bigger than 4094 or the function will fail.
    392     const int kMaxBufferLength = 256;
    393     Vector<WORD, kMaxBufferLength> glyphs;
    394     Vector<int, kMaxBufferLength> advances;
    395     int glyphIndex = 0;  // The starting glyph of the current chunk.
    396 
    397     // In order to round all offsets to the correct pixel boundary, this code keeps track of the absolute position
    398     // of each glyph in floating point units and rounds to integer advances at the last possible moment.
    399 
    400     float horizontalOffset = point.x(); // The floating point offset of the left side of the current glyph.
    401     int lastHorizontalOffsetRounded = lroundf(horizontalOffset); // The rounded offset of the left side of the last glyph rendered.
    402     while (glyphIndex < numGlyphs) {
    403         // How many chars will be in this chunk?
    404         int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex);
    405         glyphs.resize(curLen);
    406         advances.resize(curLen);
    407 
    408         float currentWidth = 0;
    409         for (int i = 0; i < curLen; ++i, ++glyphIndex) {
    410             glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex);
    411             horizontalOffset += glyphBuffer.advanceAt(from + glyphIndex);
    412             advances[i] = lroundf(horizontalOffset) - lastHorizontalOffsetRounded;
    413             lastHorizontalOffsetRounded += advances[i];
    414             currentWidth += glyphBuffer.advanceAt(from + glyphIndex);
    415 
    416             // Bug 26088 - very large positive or negative runs can fail to
    417             // render so we clamp the size here. In the specs, negative
    418             // letter-spacing is implementation-defined, so this should be
    419             // fine, and it matches Safari's implementation. The call actually
    420             // seems to crash if kMaxNegativeRun is set to somewhere around
    421             // -32830, so we give ourselves a little breathing room.
    422             const int maxNegativeRun = -32768;
    423             const int maxPositiveRun =  32768;
    424             if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun))
    425                 advances[i] = 0;
    426         }
    427 
    428         // Actually draw the glyphs (with retry on failure).
    429         bool success = false;
    430         for (int executions = 0; executions < 2; ++executions) {
    431             success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], horizontalOffset - point.x() - currentWidth);
    432             if (!success && executions == 0) {
    433                 // Ask the browser to load the font for us and retry.
    434                 PlatformBridge::ensureFontLoaded(font->platformData().hfont());
    435                 continue;
    436             }
    437             break;
    438         }
    439 
    440         if (!success)
    441             LOG_ERROR("Unable to draw the glyphs after second attempt");
    442     }
    443 }
    444 
    445 void Font::drawGlyphs(GraphicsContext* graphicsContext,
    446                       const SimpleFontData* font,
    447                       const GlyphBuffer& glyphBuffer,
    448                       int from,
    449                       int numGlyphs,
    450                       const FloatPoint& point) const
    451 {
    452     SkColor color = graphicsContext->platformContext()->effectiveFillColor();
    453     unsigned char alpha = SkColorGetA(color);
    454     // Skip 100% transparent text; no need to draw anything.
    455     if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke && !graphicsContext->hasShadow())
    456         return;
    457 
    458     drawGlyphsWin(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
    459 }
    460 
    461 FloatRect Font::selectionRectForComplexText(const TextRun& run,
    462                                             const FloatPoint& point,
    463                                             int h,
    464                                             int from,
    465                                             int to) const
    466 {
    467     UniscribeHelperTextRun state(run, *this);
    468     float left = static_cast<float>(point.x() + state.characterToX(from));
    469     float right = static_cast<float>(point.x() + state.characterToX(to));
    470 
    471     // If the text is RTL, left will actually be after right.
    472     if (left < right)
    473         return FloatRect(left, point.y(),
    474                        right - left, static_cast<float>(h));
    475 
    476     return FloatRect(right, point.y(),
    477                      left - right, static_cast<float>(h));
    478 }
    479 
    480 void Font::drawComplexText(GraphicsContext* graphicsContext,
    481                            const TextRun& run,
    482                            const FloatPoint& point,
    483                            int from,
    484                            int to) const
    485 {
    486     PlatformGraphicsContext* context = graphicsContext->platformContext();
    487     UniscribeHelperTextRun state(run, *this);
    488 
    489     SkColor color = graphicsContext->platformContext()->effectiveFillColor();
    490     unsigned char alpha = SkColorGetA(color);
    491     // Skip 100% transparent text; no need to draw anything.
    492     if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke)
    493         return;
    494 
    495     TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point);
    496 
    497     HDC hdc = painter.hdc();
    498     if (windowsCanHandleTextDrawing(graphicsContext) && !hdc)
    499         return;
    500 
    501     // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency.
    502     // Enforce non-transparent color.
    503     color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
    504     if (hdc) {
    505         SetTextColor(hdc, skia::SkColorToCOLORREF(color));
    506         SetBkMode(hdc, TRANSPARENT);
    507     }
    508 
    509     // If there is a non-blur shadow and both the fill color and shadow color
    510     // are opaque, handle without skia.
    511     FloatSize shadowOffset;
    512     float shadowBlur;
    513     Color shadowColor;
    514     ColorSpace shadowColorSpace;
    515     if (graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace) && windowsCanHandleDrawTextShadow(graphicsContext)) {
    516         COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue()));
    517         COLORREF savedTextColor = GetTextColor(hdc);
    518         SetTextColor(hdc, textColor);
    519         state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowOffset.width(),
    520                    static_cast<int>(point.y() - fontMetrics().ascent()) + shadowOffset.height(), from, to);
    521         SetTextColor(hdc, savedTextColor);
    522     }
    523 
    524     // Uniscribe counts the coordinates from the upper left, while WebKit uses
    525     // the baseline, so we have to subtract off the ascent.
    526     state.draw(graphicsContext, hdc, lroundf(point.x()), lroundf(point.y() - fontMetrics().ascent()), from, to);
    527 
    528     skia::EndPlatformPaint(context->canvas());
    529 }
    530 
    531 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
    532 {
    533     notImplemented();
    534 }
    535 
    536 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const
    537 {
    538     UniscribeHelperTextRun state(run, *this);
    539     return static_cast<float>(state.width());
    540 }
    541 
    542 int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat,
    543                                           bool includePartialGlyphs) const
    544 {
    545     // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers
    546     // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem.
    547     int x = static_cast<int>(xFloat);
    548 
    549     // Mac code ignores includePartialGlyphs, and they don't know what it's
    550     // supposed to do, so we just ignore it as well.
    551     UniscribeHelperTextRun state(run, *this);
    552     int charIndex = state.xToCharacter(x);
    553 
    554     // XToCharacter will return -1 if the position is before the first
    555     // character (we get called like this sometimes).
    556     if (charIndex < 0)
    557         charIndex = 0;
    558     return charIndex;
    559 }
    560 
    561 } // namespace WebCore
    562