Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (c) 2007, 2008, Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "Font.h"
     33 
     34 #include "FloatRect.h"
     35 #include "GlyphBuffer.h"
     36 #include "GraphicsContext.h"
     37 #include "HarfbuzzSkia.h"
     38 #include "NotImplemented.h"
     39 #include "PlatformContextSkia.h"
     40 #include "SimpleFontData.h"
     41 
     42 #include "SkCanvas.h"
     43 #include "SkPaint.h"
     44 #include "SkTemplates.h"
     45 #include "SkTypeface.h"
     46 #include "SkUtils.h"
     47 
     48 #include <unicode/normlzr.h>
     49 #include <unicode/uchar.h>
     50 #include <wtf/OwnArrayPtr.h>
     51 #include <wtf/OwnPtr.h>
     52 
     53 namespace WebCore {
     54 
     55 bool Font::canReturnFallbackFontsForComplexText()
     56 {
     57     return false;
     58 }
     59 
     60 static bool isCanvasMultiLayered(SkCanvas* canvas)
     61 {
     62     SkCanvas::LayerIter layerIterator(canvas, false);
     63     layerIterator.next();
     64     return !layerIterator.done();
     65 }
     66 
     67 static bool adjustTextRenderMode(SkPaint* paint, bool isCanvasMultiLayered)
     68 {
     69     // Our layers only have a single alpha channel. This means that subpixel
     70     // rendered text cannot be compositied correctly when the layer is
     71     // collapsed. Therefore, subpixel text is disabled when we are drawing
     72     // onto a layer.
     73     if (isCanvasMultiLayered)
     74         paint->setLCDRenderText(false);
     75 }
     76 
     77 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
     78                       const GlyphBuffer& glyphBuffer,  int from, int numGlyphs,
     79                       const FloatPoint& point) const {
     80     SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert
     81 
     82     const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
     83     SkScalar x = SkFloatToScalar(point.x());
     84     SkScalar y = SkFloatToScalar(point.y());
     85 
     86     // FIXME: text rendering speed:
     87     // Android has code in their WebCore fork to special case when the
     88     // GlyphBuffer has no advances other than the defaults. In that case the
     89     // text drawing can proceed faster. However, it's unclear when those
     90     // patches may be upstreamed to WebKit so we always use the slower path
     91     // here.
     92     const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
     93     SkAutoSTMalloc<32, SkPoint> storage(numGlyphs);
     94     SkPoint* pos = storage.get();
     95 
     96     for (int i = 0; i < numGlyphs; i++) {
     97         pos[i].set(x, y);
     98         x += SkFloatToScalar(adv[i].width());
     99         y += SkFloatToScalar(adv[i].height());
    100     }
    101 
    102     SkCanvas* canvas = gc->platformContext()->canvas();
    103     int textMode = gc->platformContext()->getTextDrawingMode();
    104     bool haveMultipleLayers = isCanvasMultiLayered(canvas);
    105 
    106     // We draw text up to two times (once for fill, once for stroke).
    107     if (textMode & cTextFill) {
    108         SkPaint paint;
    109         gc->platformContext()->setupPaintForFilling(&paint);
    110         font->platformData().setupPaint(&paint);
    111         adjustTextRenderMode(&paint, haveMultipleLayers);
    112         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    113         paint.setColor(gc->fillColor().rgb());
    114         canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
    115     }
    116 
    117     if ((textMode & cTextStroke)
    118         && gc->platformContext()->getStrokeStyle() != NoStroke
    119         && gc->platformContext()->getStrokeThickness() > 0) {
    120 
    121         SkPaint paint;
    122         gc->platformContext()->setupPaintForStroking(&paint, 0, 0);
    123         font->platformData().setupPaint(&paint);
    124         adjustTextRenderMode(&paint, haveMultipleLayers);
    125         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    126         paint.setColor(gc->strokeColor().rgb());
    127 
    128         if (textMode & cTextFill) {
    129             // If we also filled, we don't want to draw shadows twice.
    130             // See comment in FontChromiumWin.cpp::paintSkiaText() for more details.
    131             paint.setLooper(0)->safeUnref();
    132         }
    133 
    134         canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
    135     }
    136 }
    137 
    138 // Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't
    139 // handle subpixel positioning so this function is used to truncate Harfbuzz
    140 // values to a number of pixels.
    141 static int truncateFixedPointToInteger(HB_Fixed value)
    142 {
    143     return value >> 6;
    144 }
    145 
    146 // TextRunWalker walks a TextRun and presents each script run in sequence. A
    147 // TextRun is a sequence of code-points with the same embedding level (i.e. they
    148 // are all left-to-right or right-to-left). A script run is a subsequence where
    149 // all the characters have the same script (e.g. Arabic, Thai etc). Shaping is
    150 // only ever done with script runs since the shapers only know how to deal with
    151 // a single script.
    152 //
    153 // After creating it, the script runs are either iterated backwards or forwards.
    154 // It defaults to backwards for RTL and forwards otherwise (which matches the
    155 // presentation order), however you can set it with |setBackwardsIteration|.
    156 //
    157 // Once you have setup the object, call |nextScriptRun| to get the first script
    158 // run. This will return false when the iteration is complete. At any time you
    159 // can call |reset| to start over again.
    160 class TextRunWalker {
    161 public:
    162     TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
    163         : m_font(font)
    164         , m_startingX(startingX)
    165         , m_offsetX(m_startingX)
    166         , m_run(getTextRun(run))
    167         , m_iterateBackwards(m_run.rtl())
    168     {
    169         // Do not use |run| inside this constructor. Use |m_run| instead.
    170 
    171         memset(&m_item, 0, sizeof(m_item));
    172         // We cannot know, ahead of time, how many glyphs a given script run
    173         // will produce. We take a guess that script runs will not produce more
    174         // than twice as many glyphs as there are code points and fallback if
    175         // we find that we are wrong.
    176         m_maxGlyphs = m_run.length() * 2;
    177         createGlyphArrays();
    178 
    179         m_item.log_clusters = new unsigned short[m_run.length()];
    180 
    181         m_item.face = 0;
    182         m_item.font = allocHarfbuzzFont();
    183 
    184         m_item.string = m_run.characters();
    185         m_item.stringLength = m_run.length();
    186         m_item.item.bidiLevel = m_run.rtl();
    187 
    188         reset();
    189     }
    190 
    191     ~TextRunWalker()
    192     {
    193         fastFree(m_item.font);
    194         deleteGlyphArrays();
    195         delete[] m_item.log_clusters;
    196     }
    197 
    198     void reset()
    199     {
    200         if (m_iterateBackwards)
    201             m_indexOfNextScriptRun = m_run.length() - 1;
    202         else
    203             m_indexOfNextScriptRun = 0;
    204         m_offsetX = m_startingX;
    205     }
    206 
    207     // Set the x offset for the next script run. This affects the values in
    208     // |xPositions|
    209     void setXOffsetToZero()
    210     {
    211         m_offsetX = 0;
    212     }
    213 
    214     bool rtl() const
    215     {
    216         return m_run.rtl();
    217     }
    218 
    219     void setBackwardsIteration(bool isBackwards)
    220     {
    221         m_iterateBackwards = isBackwards;
    222         reset();
    223     }
    224 
    225     // Advance to the next script run, returning false when the end of the
    226     // TextRun has been reached.
    227     bool nextScriptRun()
    228     {
    229         if (m_iterateBackwards) {
    230             // In right-to-left mode we need to render the shaped glyph backwards and
    231             // also render the script runs themselves backwards. So given a TextRun:
    232             //    AAAAAAACTTTTTTT   (A = Arabic, C = Common, T = Thai)
    233             // we render:
    234             //    TTTTTTCAAAAAAA
    235             // (and the glyphs in each A, C and T section are backwards too)
    236             if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
    237                 return false;
    238         } else {
    239             if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
    240                 return false;
    241         }
    242 
    243         setupFontForScriptRun();
    244 
    245         if (!shapeGlyphs())
    246             return false;
    247         setGlyphXPositions(rtl());
    248         return true;
    249     }
    250 
    251     const uint16_t* glyphs() const
    252     {
    253         return m_glyphs16;
    254     }
    255 
    256     // Return the length of the array returned by |glyphs|
    257     const unsigned length() const
    258     {
    259         return m_item.num_glyphs;
    260     }
    261 
    262     // Return the x offset for each of the glyphs. Note that this is translated
    263     // by the current x offset and that the x offset is updated for each script
    264     // run.
    265     const SkScalar* xPositions() const
    266     {
    267         return m_xPositions;
    268     }
    269 
    270     // Get the advances (widths) for each glyph.
    271     const HB_Fixed* advances() const
    272     {
    273         return m_item.advances;
    274     }
    275 
    276     // Return the width (in px) of the current script run.
    277     const unsigned width() const
    278     {
    279         return m_pixelWidth;
    280     }
    281 
    282     // Return the cluster log for the current script run. For example:
    283     //   script run: f i a n c   (fi gets ligatured)
    284     //   log clutrs: 0 0 1 2 3 4
    285     // So, for each input code point, the log tells you which output glyph was
    286     // generated for it.
    287     const unsigned short* logClusters() const
    288     {
    289         return m_item.log_clusters;
    290     }
    291 
    292     // return the number of code points in the current script run
    293     const unsigned numCodePoints() const
    294     {
    295         return m_numCodePoints;
    296     }
    297 
    298     const FontPlatformData* fontPlatformDataForScriptRun()
    299     {
    300         return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
    301     }
    302 
    303     float widthOfFullRun()
    304     {
    305         float widthSum = 0;
    306         while (nextScriptRun())
    307             widthSum += width();
    308 
    309         return widthSum;
    310     }
    311 
    312 private:
    313     const TextRun& getTextRun(const TextRun& originalRun)
    314     {
    315         // Normalize the text run in two ways:
    316         // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
    317         // (U+0300..) are used in the run. This conversion is necessary since most OpenType
    318         // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
    319         // their GSUB tables.
    320         //
    321         // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
    322         // the API returns FALSE (= not normalized) for complex runs that don't require NFC
    323         // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
    324         // Harfbuzz will do the same thing for us using the GSUB table.
    325         // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
    326         // for characters like '\n' otherwise.
    327         for (unsigned i = 0; i < originalRun.length(); ++i) {
    328             UChar ch = originalRun[i];
    329             UBlockCode block = ::ublock_getCode(ch);
    330             if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) {
    331                 return getNormalizedTextRun(originalRun);
    332             }
    333         }
    334         return originalRun;
    335     }
    336 
    337     const TextRun& getNormalizedTextRun(const TextRun& originalRun)
    338     {
    339         icu::UnicodeString normalizedString;
    340         UErrorCode error = U_ZERO_ERROR;
    341         icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error);
    342         if (U_FAILURE(error))
    343             return originalRun;
    344 
    345         m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]);
    346         normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error);
    347         ASSERT(U_SUCCESS(error));
    348 
    349         for (unsigned i = 0; i < normalizedString.length(); ++i) {
    350             if (Font::treatAsSpace(m_normalizedBuffer[i]))
    351                 m_normalizedBuffer[i] = ' ';
    352         }
    353 
    354         m_normalizedRun.set(new TextRun(originalRun));
    355         m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length());
    356         return *m_normalizedRun;
    357     }
    358 
    359     void setupFontForScriptRun()
    360     {
    361         const FontData* fontData = m_font->fontDataAt(0);
    362         if (!fontData->containsCharacters(m_item.string + m_item.item.pos, m_item.item.length))
    363             fontData = m_font->fontDataForCharacters(m_item.string + m_item.item.pos, m_item.item.length);
    364         const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData();
    365         m_item.face = platformData.harfbuzzFace();
    366         void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
    367         m_item.font->userData = opaquePlatformData;
    368     }
    369 
    370     HB_FontRec* allocHarfbuzzFont()
    371     {
    372         HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
    373         memset(font, 0, sizeof(HB_FontRec));
    374         font->klass = &harfbuzzSkiaClass;
    375         font->userData = 0;
    376         // The values which harfbuzzSkiaClass returns are already scaled to
    377         // pixel units, so we just set all these to one to disable further
    378         // scaling.
    379         font->x_ppem = 1;
    380         font->y_ppem = 1;
    381         font->x_scale = 1;
    382         font->y_scale = 1;
    383 
    384         return font;
    385     }
    386 
    387     void deleteGlyphArrays()
    388     {
    389         delete[] m_item.glyphs;
    390         delete[] m_item.attributes;
    391         delete[] m_item.advances;
    392         delete[] m_item.offsets;
    393         delete[] m_glyphs16;
    394         delete[] m_xPositions;
    395     }
    396 
    397     bool createGlyphArrays()
    398     {
    399         m_item.glyphs = new HB_Glyph[m_maxGlyphs];
    400         m_item.attributes = new HB_GlyphAttributes[m_maxGlyphs];
    401         m_item.advances = new HB_Fixed[m_maxGlyphs];
    402         m_item.offsets = new HB_FixedPoint[m_maxGlyphs];
    403         // HB_FixedPoint is a struct, so we must use memset to clear it.
    404         memset(m_item.offsets, 0, m_maxGlyphs * sizeof(HB_FixedPoint));
    405         m_glyphs16 = new uint16_t[m_maxGlyphs];
    406         m_xPositions = new SkScalar[m_maxGlyphs];
    407 
    408         return m_item.glyphs
    409             && m_item.attributes
    410             && m_item.advances
    411             && m_item.offsets
    412             && m_glyphs16
    413             && m_xPositions;
    414     }
    415 
    416     bool expandGlyphArrays()
    417     {
    418         deleteGlyphArrays();
    419         m_maxGlyphs <<= 1;
    420         return createGlyphArrays();
    421     }
    422 
    423     bool shapeGlyphs()
    424     {
    425         for (;;) {
    426             m_item.num_glyphs = m_maxGlyphs;
    427             HB_ShapeItem(&m_item);
    428             if (m_item.num_glyphs < m_maxGlyphs)
    429                 break;
    430 
    431             // We overflowed our arrays. Resize and retry.
    432             if (!expandGlyphArrays())
    433                 return false;
    434         }
    435 
    436         return true;
    437     }
    438 
    439     void setGlyphXPositions(bool isRTL)
    440     {
    441         double position = 0;
    442         for (int iter = 0; iter < m_item.num_glyphs; ++iter) {
    443             // Glyphs are stored in logical order, but for layout purposes we always go left to right.
    444             int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
    445 
    446             m_glyphs16[i] = m_item.glyphs[i];
    447             double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
    448             m_xPositions[i] = m_offsetX + position + offsetX;
    449 
    450             double advance = truncateFixedPointToInteger(m_item.advances[i]);
    451             position += advance;
    452         }
    453         m_pixelWidth = position;
    454         m_offsetX += m_pixelWidth;
    455     }
    456 
    457     const Font* const m_font;
    458     HB_ShaperItem m_item;
    459     uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
    460     SkScalar* m_xPositions; // A vector of x positions for each glyph.
    461     ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
    462     const unsigned m_startingX; // Offset in pixels of the first script run.
    463     unsigned m_offsetX; // Offset in pixels to the start of the next script run.
    464     unsigned m_pixelWidth; // Width (in px) of the current script run.
    465     unsigned m_numCodePoints; // Code points in current script run.
    466     unsigned m_maxGlyphs; // Current size of all the Harfbuzz arrays.
    467 
    468     OwnPtr<TextRun> m_normalizedRun;
    469     OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run.
    470     const TextRun& m_run;
    471     bool m_iterateBackwards;
    472 };
    473 
    474 static void setupForTextPainting(SkPaint* paint, SkColor color)
    475 {
    476     paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    477     paint->setColor(color);
    478 }
    479 
    480 void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
    481                            const FloatPoint& point, int from, int to) const
    482 {
    483     if (!run.length())
    484         return;
    485 
    486     SkCanvas* canvas = gc->platformContext()->canvas();
    487     int textMode = gc->platformContext()->getTextDrawingMode();
    488     bool fill = textMode & cTextFill;
    489     bool stroke = (textMode & cTextStroke)
    490                && gc->platformContext()->getStrokeStyle() != NoStroke
    491                && gc->platformContext()->getStrokeThickness() > 0;
    492 
    493     if (!fill && !stroke)
    494         return;
    495 
    496     SkPaint strokePaint, fillPaint;
    497     if (fill) {
    498         gc->platformContext()->setupPaintForFilling(&fillPaint);
    499         setupForTextPainting(&fillPaint, gc->fillColor().rgb());
    500     }
    501     if (stroke) {
    502         gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
    503         setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
    504     }
    505 
    506     TextRunWalker walker(run, point.x(), this);
    507     bool haveMultipleLayers = isCanvasMultiLayered(canvas);
    508 
    509     while (walker.nextScriptRun()) {
    510         if (fill) {
    511             walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
    512             adjustTextRenderMode(&fillPaint, haveMultipleLayers);
    513             canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint);
    514         }
    515 
    516         if (stroke) {
    517             walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
    518             adjustTextRenderMode(&strokePaint, haveMultipleLayers);
    519             canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint);
    520         }
    521     }
    522 }
    523 
    524 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const
    525 {
    526     TextRunWalker walker(run, 0, this);
    527     return walker.widthOfFullRun();
    528 }
    529 
    530 static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x)
    531 {
    532     const HB_Fixed* advances = walker.advances();
    533     int glyphIndex;
    534     if (walker.rtl()) {
    535         for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) {
    536             if (x < truncateFixedPointToInteger(advances[glyphIndex]))
    537                 break;
    538             x -= truncateFixedPointToInteger(advances[glyphIndex]);
    539         }
    540     } else {
    541         for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) {
    542             if (x < truncateFixedPointToInteger(advances[glyphIndex]))
    543                 break;
    544             x -= truncateFixedPointToInteger(advances[glyphIndex]);
    545         }
    546     }
    547 
    548     return glyphIndex;
    549 }
    550 
    551 // Return the code point index for the given |x| offset into the text run.
    552 int Font::offsetForPositionForComplexText(const TextRun& run, int x,
    553                                           bool includePartialGlyphs) const
    554 {
    555     // (Mac code ignores includePartialGlyphs, and they don't know what it's
    556     // supposed to do, so we just ignore it as well.)
    557     TextRunWalker walker(run, 0, this);
    558 
    559     // If this is RTL text, the first glyph from the left is actually the last
    560     // code point. So we need to know how many code points there are total in
    561     // order to subtract. This is different from the length of the TextRun
    562     // because UTF-16 surrogate pairs are a single code point, but 32-bits long.
    563     // In LTR we leave this as 0 so that we get the correct value for
    564     // |basePosition|, below.
    565     unsigned totalCodePoints = 0;
    566     if (walker.rtl()) {
    567         ssize_t offset = 0;
    568         while (offset < run.length()) {
    569             utf16_to_code_point(run.characters(), run.length(), &offset);
    570             totalCodePoints++;
    571         }
    572     }
    573 
    574     unsigned basePosition = totalCodePoints;
    575 
    576     // For RTL:
    577     //   code-point order:  abcd efg hijkl
    578     //   on screen:         lkjih gfe dcba
    579     //                                ^   ^
    580     //                                |   |
    581     //                  basePosition--|   |
    582     //                 totalCodePoints----|
    583     // Since basePosition is currently the total number of code-points, the
    584     // first thing we do is decrement it so that it's pointing to the start of
    585     // the current script-run.
    586     //
    587     // For LTR, basePosition is zero so it already points to the start of the
    588     // first script run.
    589     while (walker.nextScriptRun()) {
    590         if (walker.rtl())
    591             basePosition -= walker.numCodePoints();
    592 
    593         if (x < walker.width()) {
    594             // The x value in question is within this script run. We consider
    595             // each glyph in presentation order and stop when we find the one
    596             // covering this position.
    597             const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x);
    598 
    599             // Now that we have a glyph index, we have to turn that into a
    600             // code-point index. Because of ligatures, several code-points may
    601             // have gone into a single glyph. We iterate over the clusters log
    602             // and find the first code-point which contributed to the glyph.
    603 
    604             // Some shapers (i.e. Khmer) will produce cluster logs which report
    605             // that /no/ code points contributed to certain glyphs. Because of
    606             // this, we take any code point which contributed to the glyph in
    607             // question, or any subsequent glyph. If we run off the end, then
    608             // we take the last code point.
    609             const unsigned short* log = walker.logClusters();
    610             for (unsigned j = 0; j < walker.numCodePoints(); ++j) {
    611                 if (log[j] >= glyphIndex)
    612                     return basePosition + j;
    613             }
    614 
    615             return basePosition + walker.numCodePoints() - 1;
    616         }
    617 
    618         x -= walker.width();
    619 
    620         if (!walker.rtl())
    621             basePosition += walker.numCodePoints();
    622     }
    623 
    624     return basePosition;
    625 }
    626 
    627 // Return the rectangle for selecting the given range of code-points in the TextRun.
    628 FloatRect Font::selectionRectForComplexText(const TextRun& run,
    629                                             const IntPoint& point, int height,
    630                                             int from, int to) const
    631 {
    632     int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1;
    633     TextRunWalker walker(run, 0, this);
    634 
    635     // Base will point to the x offset for the current script run. Note that, in
    636     // the LTR case, width will be 0.
    637     int base = walker.rtl() ? walker.widthOfFullRun() : 0;
    638     const int leftEdge = base;
    639 
    640     // We want to enumerate the script runs in code point order in the following
    641     // code. This call also resets |walker|.
    642     walker.setBackwardsIteration(false);
    643 
    644     while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) {
    645         // TextRunWalker will helpfully accululate the x offsets for different
    646         // script runs for us. For this code, however, we always want the x offsets
    647         // to start from zero so we call this before each script run.
    648         walker.setXOffsetToZero();
    649 
    650         if (walker.rtl())
    651             base -= walker.width();
    652 
    653         if (fromX == -1 && from < walker.numCodePoints()) {
    654             // |from| is within this script run. So we index the clusters log to
    655             // find which glyph this code-point contributed to and find its x
    656             // position.
    657             int glyph = walker.logClusters()[from];
    658             fromX = base + walker.xPositions()[glyph];
    659             fromAdvance = walker.advances()[glyph];
    660         } else
    661             from -= walker.numCodePoints();
    662 
    663         if (toX == -1 && to < walker.numCodePoints()) {
    664             int glyph = walker.logClusters()[to];
    665             toX = base + walker.xPositions()[glyph];
    666             toAdvance = walker.advances()[glyph];
    667         } else
    668             to -= walker.numCodePoints();
    669 
    670         if (!walker.rtl())
    671             base += walker.width();
    672     }
    673 
    674     // The position in question might be just after the text.
    675     const int rightEdge = base;
    676     if (fromX == -1 && !from)
    677         fromX = leftEdge;
    678     else if (walker.rtl())
    679        fromX += truncateFixedPointToInteger(fromAdvance);
    680 
    681     if (toX == -1 && !to)
    682         toX = rightEdge;
    683 
    684     ASSERT(fromX != -1 && toX != -1);
    685 
    686     if (fromX < toX)
    687         return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
    688 
    689     return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
    690 }
    691 
    692 } // namespace WebCore
    693