Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (c) 2006, 2007, 2008, 2009, 2012 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 "platform/fonts/win/UniscribeHelper.h"
     33 
     34 #include "platform/fonts/Font.h"
     35 #include "platform/fonts/skia/SkiaFontWin.h"
     36 #include "platform/fonts/FontFallbackWin.h"
     37 #include "platform/graphics/GraphicsContext.h"
     38 #include "platform/win/HWndDC.h"
     39 #include "third_party/skia/include/core/SkPoint.h"
     40 #include "wtf/Assertions.h"
     41 
     42 namespace WebCore {
     43 
     44 // The function types for ScriptItemizeOpenType() and ScriptShapeOpenType().
     45 // We want to use these functions for OpenType feature support, but we can't
     46 // call them directly because usp10.dll does not always have them.
     47 // Instead, we use GetProcAddress() to check whether we can actually use these
     48 // function. If we can't use these functions, we substitute ScriptItemze() and
     49 // ScriptShape().
     50 typedef HRESULT (WINAPI *ScriptItemizeOpenTypeFunc)(const WCHAR*, int, int,
     51                                                     const SCRIPT_CONTROL*,
     52                                                     const SCRIPT_STATE*,
     53                                                     SCRIPT_ITEM*,
     54                                                     OPENTYPE_TAG*, int*);
     55 typedef HRESULT (WINAPI *ScriptShapeOpenTypeFunc)(HDC, SCRIPT_CACHE*,
     56                                                   SCRIPT_ANALYSIS*,
     57                                                   OPENTYPE_TAG, OPENTYPE_TAG,
     58                                                   int*, TEXTRANGE_PROPERTIES**,
     59                                                   int, const WCHAR*, int, int,
     60                                                   WORD*, SCRIPT_CHARPROP*,
     61                                                   WORD*, SCRIPT_GLYPHPROP*,
     62                                                   int*);
     63 
     64 static ScriptItemizeOpenTypeFunc gScriptItemizeOpenTypeFunc = 0;
     65 static ScriptShapeOpenTypeFunc gScriptShapeOpenTypeFunc = 0;
     66 static bool gOpenTypeFunctionsLoaded = false;
     67 
     68 static void loadOpenTypeFunctions()
     69 {
     70     HMODULE hModule = GetModuleHandle(L"usp10");
     71     if (hModule) {
     72         gScriptItemizeOpenTypeFunc = reinterpret_cast<ScriptItemizeOpenTypeFunc>(GetProcAddress(hModule, "ScriptItemizeOpenType"));
     73         gScriptShapeOpenTypeFunc = reinterpret_cast<ScriptShapeOpenTypeFunc>(GetProcAddress(hModule, "ScriptShapeOpenType"));
     74     }
     75     if (!gScriptItemizeOpenTypeFunc || !gScriptShapeOpenTypeFunc) {
     76         gScriptItemizeOpenTypeFunc = 0;
     77         gScriptShapeOpenTypeFunc = 0;
     78     }
     79     gOpenTypeFunctionsLoaded = true;
     80 }
     81 
     82 enum {
     83     FontStyleNormal = 0,
     84     FontStyleBold = 1,
     85     FontStyleItalic = 2,
     86     FontStyleUnderlined = 4
     87 };
     88 
     89 int getStyleFromLogfont(const LOGFONT* logfont)
     90 {
     91     // FIXME: consider defining UNDEFINED or INVALID for style and
     92     //                  returning it when logfont is 0
     93     if (!logfont) {
     94         ASSERT_NOT_REACHED();
     95         return FontStyleNormal;
     96     }
     97     return (logfont->lfItalic ? FontStyleItalic : FontStyleNormal) |
     98         (logfont->lfUnderline ? FontStyleUnderlined : FontStyleNormal) |
     99         (logfont->lfWeight >= 700 ? FontStyleBold : FontStyleNormal);
    100 }
    101 
    102 
    103 // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
    104 // handle and we can't directly query it to make a new HFONT sharing
    105 // its characteristics (height, style, etc) except for family name.
    106 // This function uses GetObject to convert HFONT back to LOGFONT,
    107 // resets the fields of LOGFONT and calculates style to use later
    108 // for the creation of a font identical to HFONT other than family name.
    109 static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
    110 {
    111     ASSERT(hfont && logfont);
    112     if (!hfont || !logfont)
    113         return;
    114 
    115     GetObject(hfont, sizeof(LOGFONT), logfont);
    116     // We reset these fields to values appropriate for CreateFontIndirect.
    117     // while keeping lfHeight, which is the most important value in creating
    118     // a new font similar to hfont.
    119     logfont->lfWidth = 0;
    120     logfont->lfEscapement = 0;
    121     logfont->lfOrientation = 0;
    122     logfont->lfCharSet = DEFAULT_CHARSET;
    123     logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
    124     logfont->lfQuality = DEFAULT_QUALITY;  // Honor user's desktop settings.
    125     logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
    126     if (style)
    127         *style = getStyleFromLogfont(logfont);
    128 }
    129 
    130 // This memory DC will NOT be released but it's OK
    131 // since we want to keep it for the whole life span of the process.
    132 HDC UniscribeHelper::m_cachedDC = 0;
    133 
    134 static bool canUseGlyphIndex(const SCRIPT_ITEM& run)
    135 {
    136     // On early version of Uniscribe, ScriptShape() sets run.a.fNoGlyphIndex
    137     // to TRUE when it can't shape the run with glyph indexes. This could
    138     // occur when we use CFF webfonts(See http://crbug.com/39017).
    139     // We don't use the font in that case and try to use fallback fonts.
    140     return !run.a.fNoGlyphIndex;
    141 }
    142 
    143 UniscribeHelper::UniscribeHelper(const UChar* input,
    144                                 int inputLength,
    145                                 bool isRtl,
    146                                 HFONT hfont,
    147                                 SCRIPT_CACHE* scriptCache,
    148                                 SCRIPT_FONTPROPERTIES* fontProperties,
    149                                 WORD spaceGlyph)
    150     : m_input(input)
    151     , m_inputLength(inputLength)
    152     , m_isRtl(isRtl)
    153     , m_hfont(hfont)
    154     , m_scriptCache(scriptCache)
    155     , m_fontProperties(fontProperties)
    156     , m_spaceGlyph(spaceGlyph)
    157     , m_directionalOverride(false)
    158     , m_inhibitLigate(false)
    159     , m_letterSpacing(0)
    160     , m_spaceWidth(0)
    161     , m_wordSpacing(0)
    162     , m_ascent(0)
    163     , m_disableFontFallback(false)
    164 
    165 {
    166     m_logfont.lfFaceName[0] = 0;
    167     if (!gOpenTypeFunctionsLoaded)
    168         loadOpenTypeFunctions();
    169 }
    170 
    171 UniscribeHelper::~UniscribeHelper()
    172 {
    173 }
    174 
    175 void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection)
    176 {
    177     // We cap the input length and just don't do anything. We'll allocate a lot
    178     // of things of the size of the number of characters, so the allocated
    179     // memory will be several times the input length. Plus shaping such a large
    180     // buffer may be a form of denial of service. No legitimate text should be
    181     // this long.  It also appears that Uniscribe flatly rejects very long
    182     // strings, so we don't lose anything by doing this.
    183     //
    184     // The input length protection may be disabled by the unit tests to cause
    185     // an error condition.
    186     static const int kMaxInputLength = 65535;
    187     if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength))
    188         return;
    189 
    190     fillRuns();
    191     fillShapes();
    192     fillScreenOrder();
    193 }
    194 
    195 int UniscribeHelper::width() const
    196 {
    197     int width = 0;
    198     for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++)
    199         width += advanceForItem(itemIndex);
    200     return width;
    201 }
    202 
    203 void UniscribeHelper::justify(int additionalSpace)
    204 {
    205     // Count the total number of glyphs we have so we know how big to make the
    206     // buffers below.
    207     int totalGlyphs = 0;
    208     for (size_t run = 0; run < m_runs.size(); run++) {
    209         int runIndex = m_screenOrder[run];
    210         totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength());
    211     }
    212     if (totalGlyphs == 0)
    213         return;  // Nothing to do.
    214 
    215     // We make one big buffer in screen order of all the glyphs we are drawing
    216     // across runs so that the justification function will adjust evenly across
    217     // all glyphs.
    218     Vector<SCRIPT_VISATTR, 64> visualAttributes;
    219     visualAttributes.resize(totalGlyphs);
    220     Vector<int, 64> advances;
    221     advances.resize(totalGlyphs);
    222     Vector<int, 64> justify;
    223     justify.resize(totalGlyphs);
    224 
    225     // Build the packed input.
    226     int destIndex = 0;
    227     for (size_t run = 0; run < m_runs.size(); run++) {
    228         int runIndex = m_screenOrder[run];
    229         const Shaping& shaping = m_shapes[runIndex];
    230 
    231         for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) {
    232             memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i],
    233                    sizeof(SCRIPT_VISATTR));
    234             advances[destIndex] = shaping.m_advance[i];
    235         }
    236     }
    237 
    238     // The documentation for Scriptjustify is wrong, the parameter is the space
    239     // to add and not the width of the column you want.
    240     int minKashida;
    241     // Disable kashida justification based on
    242     // http://blogs.msdn.com/b/michkap/archive/2010/08/31/10056140.aspx.
    243     for (int i = 0; i < totalGlyphs; ++i) {
    244         if (visualAttributes[i].uJustification == SCRIPT_JUSTIFY_ARABIC_KASHIDA)
    245             visualAttributes[i].uJustification = SCRIPT_JUSTIFY_NONE;
    246     }
    247     minKashida = 0;
    248     ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs,
    249                   additionalSpace, minKashida, &justify[0]);
    250 
    251     // Now we have to unpack the justification amounts back into the runs so
    252     // the glyph indices match.
    253     int globalGlyphIndex = 0;
    254     for (size_t run = 0; run < m_runs.size(); run++) {
    255         int runIndex = m_screenOrder[run];
    256         Shaping& shaping = m_shapes[runIndex];
    257 
    258         shaping.m_justify.resize(shaping.glyphLength());
    259         for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
    260             shaping.m_justify[i] = justify[globalGlyphIndex];
    261     }
    262 }
    263 
    264 int UniscribeHelper::characterToX(int offset) const
    265 {
    266     HRESULT hr;
    267     ASSERT(offset <= m_inputLength);
    268 
    269     // Our algorithm is to traverse the items in screen order from left to
    270     // right, adding in each item's screen width until we find the item with
    271     // the requested character in it.
    272     int width = 0;
    273     for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
    274         // Compute the length of this run.
    275         int itemIndex = m_screenOrder[screenIndex];
    276         const SCRIPT_ITEM& item = m_runs[itemIndex];
    277         const Shaping& shaping = m_shapes[itemIndex];
    278         int itemLength = shaping.charLength();
    279 
    280         if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
    281             // Character offset is in this run.
    282             int charLength = offset - item.iCharPos;
    283 
    284             int curX = 0;
    285             hr = ScriptCPtoX(charLength, FALSE, itemLength,
    286                              shaping.glyphLength(),
    287                              &shaping.m_logs[0], &shaping.m_visualAttributes[0],
    288                              shaping.effectiveAdvances(), &item.a, &curX);
    289             if (FAILED(hr))
    290                 return 0;
    291 
    292             width += curX + shaping.m_prePadding;
    293             ASSERT(width >= 0);
    294             return width;
    295         }
    296 
    297         // Move to the next item.
    298         width += advanceForItem(itemIndex);
    299     }
    300     ASSERT(width >= 0);
    301     return width;
    302 }
    303 
    304 int UniscribeHelper::xToCharacter(int x) const
    305 {
    306     // We iterate in screen order until we find the item with the given pixel
    307     // position in it. When we find that guy, we ask Uniscribe for the
    308     // character index.
    309     HRESULT hr;
    310     for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
    311         int itemIndex = m_screenOrder[screenIndex];
    312         int itemAdvance = advanceForItem(itemIndex);
    313 
    314         // Note that the run may be empty if shaping failed, so we want to skip
    315         // over it.
    316         const Shaping& shaping = m_shapes[itemIndex];
    317         int itemLength = shaping.charLength();
    318         if (x <= itemAdvance && itemLength > 0) {
    319             // The requested offset is within this item.
    320             const SCRIPT_ITEM& item = m_runs[itemIndex];
    321 
    322             // Account for the leading space we've added to this run that
    323             // Uniscribe doesn't know about.
    324             x -= shaping.m_prePadding;
    325 
    326             int charX = 0;
    327             int trailing;
    328             hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
    329                              &shaping.m_logs[0], &shaping.m_visualAttributes[0],
    330                              shaping.effectiveAdvances(), &item.a, &charX,
    331                              &trailing);
    332 
    333             // The character offset is within the item. We need to add the
    334             // item's offset to transform it into the space of the TextRun
    335             return charX + item.iCharPos;
    336         }
    337 
    338         // The offset is beyond this item, account for its length and move on.
    339         x -= itemAdvance;
    340     }
    341 
    342     // Error condition, we don't know what to do if we don't have that X
    343     // position in any of our items.
    344     return 0;
    345 }
    346 
    347 void UniscribeHelper::draw(GraphicsContext* graphicsContext,
    348     const FontPlatformData& fontPlatformData, HDC dc, int x, int y,
    349     const FloatRect& textRect, int from, int to)
    350 {
    351     HGDIOBJ oldFont = 0;
    352     int curX = x;
    353     bool firstRun = true;
    354 
    355     for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
    356         int itemIndex = m_screenOrder[screenIndex];
    357         const SCRIPT_ITEM& item = m_runs[itemIndex];
    358         const Shaping& shaping = m_shapes[itemIndex];
    359 
    360         // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
    361         // be negative, etc. The code below handles this.
    362         int fromChar = from - item.iCharPos;
    363         int toChar = to - item.iCharPos;
    364 
    365         // See if we need to draw any characters in this item.
    366         if (shaping.charLength() == 0 ||
    367             fromChar >= shaping.charLength() || toChar <= 0) {
    368             // No chars in this item to display.
    369             curX += advanceForItem(itemIndex);
    370             continue;
    371         }
    372 
    373         // Compute the starting glyph within this span. |from| and |to| are
    374         // global offsets that may intersect arbitrarily with our local run.
    375         int fromGlyph, afterGlyph;
    376         if (item.a.fRTL) {
    377             // To compute the first glyph when going RTL, we use |to|.
    378             if (toChar >= shaping.charLength())
    379                 // The end of the text is after (to the left) of us.
    380                 fromGlyph = 0;
    381             else {
    382                 // Since |to| is exclusive, the first character we draw on the
    383                 // left is actually the one right before (to the right) of
    384                 // |to|.
    385                 fromGlyph = shaping.m_logs[toChar - 1];
    386             }
    387 
    388             // The last glyph is actually the first character in the range.
    389             if (fromChar <= 0) {
    390                 // The first character to draw is before (to the right) of this
    391                 // span, so draw all the way to the end.
    392                 afterGlyph = shaping.glyphLength();
    393             } else {
    394                 // We want to draw everything up until the character to the
    395                 // right of |from|. To the right is - 1, so we look that up
    396                 // (remember our character could be more than one glyph, so we
    397                 // can't look up our glyph and add one).
    398                 afterGlyph = shaping.m_logs[fromChar - 1];
    399             }
    400         } else {
    401             // Easy case, everybody agrees about directions. We only need to
    402             // handle boundary conditions to get a range inclusive at the
    403             // beginning, and exclusive at the ending. We have to do some
    404             // computation to see the glyph one past the end.
    405             fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
    406             if (toChar >= shaping.charLength())
    407                 afterGlyph = shaping.glyphLength();
    408             else
    409                 afterGlyph = shaping.m_logs[toChar];
    410         }
    411 
    412         // Account for the characters that were skipped in this run. When
    413         // WebKit asks us to draw a subset of the run, it actually tells us
    414         // to draw at the X offset of the beginning of the run, since it
    415         // doesn't know the internal position of any of our characters.
    416         const int* effectiveAdvances = shaping.effectiveAdvances();
    417         int innerOffset = 0;
    418         for (int i = 0; i < fromGlyph; i++)
    419             innerOffset += effectiveAdvances[i];
    420 
    421         // Actually draw the glyphs we found.
    422         int glyphCount = afterGlyph - fromGlyph;
    423         if (fromGlyph >= 0 && glyphCount > 0) {
    424             // Account for the preceding space we need to add to this run. We
    425             // don't need to count for the following space because that will be
    426             // counted in advanceForItem below when we move to the next run.
    427             innerOffset += shaping.m_prePadding;
    428 
    429             // Pass 0 in when there is no justification.
    430             const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph];
    431 
    432             const int* advances = shaping.m_justify.size() ?
    433                                       &shaping.m_justify[fromGlyph]
    434                                     : &shaping.m_advance[fromGlyph];
    435 
    436             // Fonts with different ascents can be used to render different
    437             // runs.  'Across-runs' y-coordinate correction needs to be
    438             // adjusted for each font.
    439             bool textOutOk = false;
    440             for (int executions = 0; executions < 2; ++executions) {
    441                 SkPoint origin;
    442                 origin.fX = curX + + innerOffset;
    443                 origin.fY = y + m_ascent;
    444                 paintSkiaText(graphicsContext,
    445                     fontPlatformData,
    446                     shaping.m_hfont,
    447                     glyphCount,
    448                     &shaping.m_glyphs[fromGlyph],
    449                     advances,
    450                     &shaping.m_offsets[fromGlyph],
    451                     origin,
    452                     textRect);
    453                 textOutOk = true;
    454 
    455                 if (!textOutOk && 0 == executions) {
    456                     // If TextOut is called from the renderer it might fail
    457                     // because the sandbox is preventing it from opening the
    458                     // font files.  If we are running in the renderer,
    459                     // TryToPreloadFont is overridden to ask the browser to
    460                     // preload the font for us so we can access it.
    461                     tryToPreloadFont(shaping.m_hfont);
    462                     continue;
    463                 }
    464                 break;
    465             }
    466         }
    467 
    468         curX += advanceForItem(itemIndex);
    469     }
    470 
    471     if (oldFont)
    472         SelectObject(dc, oldFont);
    473 }
    474 
    475 WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const
    476 {
    477     // Find the run for the given character.
    478     for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
    479         int firstChar = m_runs[i].iCharPos;
    480         const Shaping& shaping = m_shapes[i];
    481         int localOffset = charOffset - firstChar;
    482         if (localOffset >= 0 && localOffset < shaping.charLength()) {
    483             // The character is in this run, return the first glyph for it
    484             // (should generally be the only glyph). It seems Uniscribe gives
    485             // glyph 0 for empty, which is what we want to return in the
    486             // "missing" case.
    487             size_t glyphIndex = shaping.m_logs[localOffset];
    488             if (glyphIndex >= shaping.m_glyphs.size()) {
    489                 // The glyph should be in this run, but the run has too few
    490                 // actual characters. This can happen when shaping the run
    491                 // fails, in which case, we should have no data in the logs at
    492                 // all.
    493                 ASSERT(shaping.m_glyphs.size() == 0);
    494                 return 0;
    495             }
    496             return shaping.m_glyphs[glyphIndex];
    497         }
    498     }
    499 
    500     return 0;
    501 }
    502 
    503 void UniscribeHelper::fillRuns()
    504 {
    505     HRESULT hr;
    506     m_runs.resize(cUniscribeHelperStackRuns);
    507     m_scriptTags.resize(cUniscribeHelperStackRuns);
    508 
    509     SCRIPT_STATE inputState;
    510     inputState.uBidiLevel = m_isRtl;
    511     inputState.fOverrideDirection = m_directionalOverride;
    512     inputState.fInhibitSymSwap = false;
    513     inputState.fCharShape = false;  // Not implemented in Uniscribe
    514     inputState.fDigitSubstitute = false;  // Do we want this for Arabic?
    515     inputState.fInhibitLigate = m_inhibitLigate;
    516     inputState.fDisplayZWG = false;  // Don't draw control characters.
    517     inputState.fArabicNumContext = m_isRtl;  // Do we want this for Arabic?
    518     inputState.fGcpClusters = false;
    519     inputState.fReserved = 0;
    520     inputState.fEngineReserved = 0;
    521     // The psControl argument to ScriptItemize should be non-0 for RTL text,
    522     // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
    523     // SCRIPT_CONTROL that is set to all zeros.  Zero as a locale ID means the
    524     // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx
    525     static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage    :16;
    526                                            0, // fContextDigits      :1;
    527                                            0, // fInvertPreBoundDir  :1;
    528                                            0, // fInvertPostBoundDir :1;
    529                                            0, // fLinkStringBefore   :1;
    530                                            0, // fLinkStringAfter    :1;
    531                                            0, // fNeutralOverride    :1;
    532                                            0, // fNumericOverride    :1;
    533                                            0, // fLegacyBidiClass    :1;
    534                                            0, // fMergeNeutralItems  :1;
    535                                            0};// fReserved           :7;
    536     // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState)
    537     // here would be appropriate if we wanted to set the language ID, and get
    538     // local digit substitution behavior.  For now, don't do it.
    539 
    540     while (true) {
    541         int numberOfItems = 0;
    542 
    543         // Ideally, we would have a way to know the runs before and after this
    544         // one, and put them into the control parameter of ScriptItemize. This
    545         // would allow us to shape characters properly that cross style
    546         // boundaries (WebKit bug 6148).
    547         //
    548         // We tell ScriptItemize that the output list of items is one smaller
    549         // than it actually is. According to Mozilla bug 366643, if there is
    550         // not enough room in the array on pre-SP2 systems, ScriptItemize will
    551         // write one past the end of the buffer.
    552         //
    553         // ScriptItemize is very strange. It will often require a much larger
    554         // ITEM buffer internally than it will give us as output. For example,
    555         // it will say a 16-item buffer is not big enough, and will write
    556         // interesting numbers into all those items. But when we give it a 32
    557         // item buffer and it succeeds, it only has one item output.
    558         //
    559         // It seems to be doing at least two passes, the first where it puts a
    560         // lot of intermediate data into our items, and the second where it
    561         // collates them.
    562         if (gScriptItemizeOpenTypeFunc) {
    563             hr = gScriptItemizeOpenTypeFunc(m_input, m_inputLength,
    564                                             static_cast<int>(m_runs.size()) - 1,
    565                                             &inputControl, &inputState,
    566                                             &m_runs[0], &m_scriptTags[0],
    567                                             &numberOfItems);
    568 
    569             if (SUCCEEDED(hr)) {
    570                 // Pack consecutive runs, the script tag of which are
    571                 // SCRIPT_TAG_UNKNOWN, to reduce the number of runs.
    572                 for (int i = 0; i < numberOfItems; ++i) {
    573                     // Do not pack with whitespace characters at the head.
    574                     // Otherwise whole the run is rendered as a whitespace.
    575                     WCHAR ch = m_input[m_runs[i].iCharPos];
    576                     if (m_scriptTags[i] == SCRIPT_TAG_UNKNOWN && !Font::treatAsSpace(ch) && !Font::treatAsZeroWidthSpace(ch)) {
    577                         int j = 1;
    578                         while (i + j < numberOfItems && m_scriptTags[i + j] == SCRIPT_TAG_UNKNOWN)
    579                             ++j;
    580                         if (--j) {
    581                             m_runs.remove(i + 1, j);
    582                             m_scriptTags.remove(i + 1, j);
    583                             numberOfItems -= j;
    584                         }
    585                     }
    586                 }
    587                 m_scriptTags.resize(numberOfItems);
    588             }
    589         } else {
    590             hr = ScriptItemize(m_input, m_inputLength,
    591                                static_cast<int>(m_runs.size()) - 1,
    592                                &inputControl, &inputState, &m_runs[0],
    593                                &numberOfItems);
    594         }
    595         if (SUCCEEDED(hr)) {
    596             m_runs.resize(numberOfItems);
    597             break;
    598         }
    599         if (hr != E_OUTOFMEMORY) {
    600             // Some kind of unexpected error.
    601             m_runs.resize(0);
    602             break;
    603         }
    604         // There was not enough items for it to write into, expand.
    605         m_runs.resize(m_runs.size() * 2);
    606         m_scriptTags.resize(m_runs.size());
    607     }
    608 }
    609 
    610 const int kUndefinedAscent = std::numeric_limits<int>::min();
    611 
    612 // Given an HFONT, return the ascent. If GetTextMetrics fails,
    613 // kUndefinedAscent is returned, instead.
    614 int getAscent(HFONT hfont)
    615 {
    616     HWndDC dc(0);
    617     HGDIOBJ oldFont = SelectObject(dc, hfont);
    618     TEXTMETRIC tm;
    619     BOOL gotMetrics = GetTextMetrics(dc, &tm);
    620     SelectObject(dc, oldFont);
    621     return gotMetrics ? tm.tmAscent : kUndefinedAscent;
    622 }
    623 
    624 const WORD kUnsupportedGlyph = 0xffff;
    625 
    626 WORD getSpaceGlyph(HFONT hfont)
    627 {
    628     HWndDC dc(0);
    629     HGDIOBJ oldFont = SelectObject(dc, hfont);
    630     WCHAR space = L' ';
    631     WORD spaceGlyph = kUnsupportedGlyph;
    632     GetGlyphIndices(dc, &space, 1, &spaceGlyph, GGI_MARK_NONEXISTING_GLYPHS);
    633     SelectObject(dc, oldFont);
    634     return spaceGlyph;
    635 }
    636 
    637 struct ShaperFontData {
    638     ShaperFontData()
    639         : hfont(0)
    640         , ascent(kUndefinedAscent)
    641         , scriptCache(0)
    642         , spaceGlyph(0)
    643     {
    644     }
    645 
    646     HFONT hfont;
    647     int ascent;
    648     mutable SCRIPT_CACHE scriptCache;
    649     WORD spaceGlyph;
    650 };
    651 
    652 // Again, using hash_map does not earn us much here. page_cycler_test intl2
    653 // gave us a 'better' result with map than with hash_map even though they're
    654 // well-within 1-sigma of each other so that the difference is not significant.
    655 // On the other hand, some pages in intl2 seem to take longer to load with map
    656 // in the 1st pass. Need to experiment further.
    657 typedef HashMap<String, ShaperFontData> ShaperFontDataCache;
    658 
    659 // Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|,
    660 // calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE
    661 // in ShaperFontData.
    662 // |style| is only used for cache key generation. |style| is
    663 // bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and
    664 // should match what's contained in LOGFONT. It should be calculated
    665 // by calling GetStyleFromLogFont.
    666 // Returns false if the font is not accessible, in which case |ascent| field
    667 // of |ShaperFontData| is set to kUndefinedAscent.
    668 // Be aware that this is not thread-safe.
    669 // FIXME: Instead of having three out params, we'd better have one
    670 // (|*ShaperFontData|), but somehow it mysteriously messes up the layout for
    671 // certain complex script pages (e.g. hi.wikipedia.org) and also crashes
    672 // at the start-up if recently visited page list includes pages with complex
    673 // scripts in their title. Moreover, somehow the very first-pass of
    674 // intl2 page-cycler test is noticeably slower with one out param than
    675 // the current version although the subsequent 9 passes take about the
    676 // same time.
    677 // Be aware that this is not thread-safe.
    678 static bool getDerivedFontData(const UChar* family, int style, LOGFONT* logfont,
    679     int* ascent, HFONT* hfont, SCRIPT_CACHE** scriptCache, WORD* spaceGlyph)
    680 {
    681     ASSERT(logfont);
    682     ASSERT(family);
    683     ASSERT(*family);
    684 
    685     // It does not matter that we leak font data when we exit.
    686     static ShaperFontDataCache* gFontDataCache = 0;
    687     if (!gFontDataCache)
    688         gFontDataCache = new ShaperFontDataCache();
    689 
    690     // FIXME: This comes up pretty high in the profile so that
    691     // we need to measure whether using SHA256 (after coercing all the
    692     // fields to char*) is faster than String::format.
    693     String fontKey = String::format("%1d:%d:%ls", style, logfont->lfHeight, family);
    694     ShaperFontDataCache::iterator iter = gFontDataCache->find(fontKey);
    695     ShaperFontData* derived;
    696     if (iter == gFontDataCache->end()) {
    697         ASSERT(wcslen(family) < LF_FACESIZE);
    698         wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
    699         // FIXME: CreateFontIndirect always comes up with
    700         // a font even if there's no font matching the name. Need to
    701         // check it against what we actually want (as is done in
    702         // FontCacheWin.cpp)
    703         ShaperFontDataCache::AddResult entry = gFontDataCache->add(fontKey, ShaperFontData());
    704         derived = &entry.iterator->value;
    705         derived->hfont = CreateFontIndirect(logfont);
    706         // GetAscent may return kUndefinedAscent, but we still want to
    707         // cache it so that we won't have to call CreateFontIndirect once
    708         // more for HFONT next time.
    709         derived->ascent = getAscent(derived->hfont);
    710         derived->spaceGlyph = getSpaceGlyph(derived->hfont);
    711     } else {
    712         derived = &iter->value;
    713         // Last time, getAscent or getSpaceGlyph failed so that only HFONT was
    714         // cached. Try once more assuming that TryPreloadFont
    715         // was called by a caller between calls.
    716         if (kUndefinedAscent == derived->ascent)
    717             derived->ascent = getAscent(derived->hfont);
    718         if (kUnsupportedGlyph == derived->spaceGlyph)
    719             derived->spaceGlyph = getSpaceGlyph(derived->hfont);
    720     }
    721     *hfont = derived->hfont;
    722     *ascent = derived->ascent;
    723     *scriptCache = &(derived->scriptCache);
    724     *spaceGlyph = derived->spaceGlyph;
    725     return *ascent != kUndefinedAscent && *spaceGlyph != kUnsupportedGlyph;
    726 }
    727 
    728 bool UniscribeHelper::shape(const UChar* input,
    729                             int itemLength,
    730                             int numGlyphs,
    731                             SCRIPT_ITEM& run,
    732                             OPENTYPE_TAG scriptTag,
    733                             Shaping& shaping)
    734 {
    735     HFONT hfont = m_hfont;
    736     SCRIPT_CACHE* scriptCache = m_scriptCache;
    737     SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
    738     Vector<SCRIPT_CHARPROP, cUniscribeHelperStackChars> charProps;
    739     Vector<SCRIPT_GLYPHPROP, cUniscribeHelperStackChars> glyphProps;
    740     int ascent = m_ascent;
    741     WORD spaceGlyph = m_spaceGlyph;
    742     HRESULT hr;
    743     // When used to fill up glyph pages for simple scripts in non-BMP,
    744     // we don't want any font fallback in this class. The simple script
    745     // font path can take care of font fallback.
    746     bool lastFallbackTried = m_disableFontFallback;
    747     bool result;
    748 
    749     int generatedGlyphs = 0;
    750 
    751     // In case HFONT passed in ctor cannot render this run, we have to scan
    752     // other fonts from the beginning of the font list.
    753     resetFontIndex();
    754 
    755     // Compute shapes.
    756     while (true) {
    757         shaping.m_logs.resize(itemLength);
    758         shaping.m_glyphs.resize(numGlyphs);
    759         shaping.m_visualAttributes.resize(numGlyphs);
    760         charProps.resize(itemLength);
    761         glyphProps.resize(numGlyphs);
    762         run.a.fNoGlyphIndex = FALSE;
    763 
    764 #ifdef PURIFY
    765         // http://code.google.com/p/chromium/issues/detail?id=5309
    766         // Purify isn't able to track the assignments that ScriptShape makes to
    767         // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it
    768         // writes, will be considered un-initialized data.
    769         //
    770         // This hack avoid the false-positive UMRs by marking the buffer as
    771         // initialized.
    772         //
    773         // FIXME: A better solution would be to use Purify's API and mark only
    774         // the populated range as initialized:
    775         //
    776         //     PurifyMarkAsInitialized(
    777         //         &shaping.m_glyphs[0],
    778         //         sizeof(shaping.m_glyphs[0] * generatedGlyphs);
    779 
    780         ZeroMemory(&shaping.m_glyphs[0],
    781                    sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
    782 #endif
    783         // If our DC is already created, select the font in it so we can use it now.
    784         // Otherwise, we'll create it as needed afterward...
    785         if (m_cachedDC)
    786             SelectObject(m_cachedDC, hfont);
    787 
    788         // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
    789         // here. Is that what we want? It will display control characters.
    790         if (gScriptShapeOpenTypeFunc) {
    791             TEXTRANGE_PROPERTIES* rangeProps = m_featureRecords.size() ? &m_rangeProperties : 0;
    792             hr = gScriptShapeOpenTypeFunc(m_cachedDC, scriptCache, &run.a,
    793                                           scriptTag, 0, &itemLength,
    794                                           &rangeProps, rangeProps ? 1 : 0,
    795                                           input, itemLength, numGlyphs,
    796                                           &shaping.m_logs[0], &charProps[0],
    797                                           &shaping.m_glyphs[0], &glyphProps[0],
    798                                           &generatedGlyphs);
    799             if (SUCCEEDED(hr)) {
    800                 // If we use ScriptShapeOpenType(), visual attributes
    801                 // information for each characters are stored in
    802                 // |glyphProps[i].sva|.
    803                 for (int i = 0; i < generatedGlyphs; ++i)
    804                     memcpy(&shaping.m_visualAttributes[i], &glyphProps[i].sva, sizeof(SCRIPT_VISATTR));
    805             }
    806         } else {
    807             hr = ScriptShape(m_cachedDC, scriptCache, input, itemLength,
    808                              numGlyphs, &run.a,
    809                              &shaping.m_glyphs[0], &shaping.m_logs[0],
    810                              &shaping.m_visualAttributes[0], &generatedGlyphs);
    811         }
    812         // We receive E_PENDING when we need to try again with a Drawing Context,
    813         // but we don't want to retry again if we already tried with non-zero DC.
    814         if (hr == E_PENDING && !m_cachedDC) {
    815             EnsureCachedDCCreated();
    816             continue;
    817         }
    818         if (hr == E_OUTOFMEMORY) {
    819             numGlyphs *= 2;
    820             continue;
    821         }
    822         if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(shaping, run, fontProperties) && canUseGlyphIndex(run)))
    823             break;
    824 
    825         // The current font can't render this run, try next font.
    826         if (!m_disableFontFallback &&
    827             nextWinFontData(hfont, scriptCache, fontProperties, ascent, spaceGlyph)) {
    828             // The primary font does not support this run. Try next font.
    829             // In case of web page rendering, they come from fonts specified in
    830             // CSS stylesheets.
    831             continue;
    832         } else if (!lastFallbackTried) {
    833             lastFallbackTried = true;
    834 
    835             // Generate a last fallback font based on the script of
    836             // a character to draw while inheriting size and styles
    837             // from the primary font
    838             if (!m_logfont.lfFaceName[0])
    839                 setLogFontAndStyle(m_hfont, &m_logfont, &m_style);
    840 
    841             // TODO(jungshik): generic type should come from webkit for
    842             // UniscribeHelperTextRun (a derived class used in webkit).
    843             const UChar *family = getFallbackFamilyForFirstNonCommonCharacter(input, itemLength,
    844                 FontDescription::StandardFamily);
    845             bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
    846                                              &ascent, &hfont, &scriptCache,
    847                                              &spaceGlyph);
    848 
    849 
    850             if (!fontOk) {
    851                 // If this GetDerivedFontData is called from the renderer it
    852                 // might fail because the sandbox is preventing it from opening
    853                 // the font files.  If we are running in the renderer,
    854                 // TryToPreloadFont is overridden to ask the browser to preload
    855                 // the font for us so we can access it.
    856                 tryToPreloadFont(hfont);
    857 
    858                 // Try again.
    859                 fontOk = getDerivedFontData(family, m_style, &m_logfont,
    860                                             &ascent, &hfont, &scriptCache,
    861                                             &spaceGlyph);
    862                 ASSERT(fontOk);
    863             }
    864 
    865             // TODO(jungshik) : Currently GetDerivedHFont always returns a
    866             // a valid HFONT, but in the future, I may change it to return 0.
    867             ASSERT(hfont);
    868 
    869             // We don't need a font_properties for the last resort fallback font
    870             // because we don't have anything more to try and are forced to
    871             // accept empty glyph boxes. If we tried a series of fonts as
    872             // 'last-resort fallback', we'd need it, but currently, we don't.
    873             continue;
    874         } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
    875             run.a.eScript = SCRIPT_UNDEFINED;
    876             continue;
    877         } else if (FAILED(hr)) {
    878             // Error shaping.
    879             generatedGlyphs = 0;
    880             result = false;
    881             goto cleanup;
    882         }
    883     }
    884 
    885     // Sets Windows font data for this run to those corresponding to
    886     // a font supporting this run. we don't need to store font_properties
    887     // because it's not used elsewhere.
    888     shaping.m_hfont = hfont;
    889     shaping.m_scriptCache = scriptCache;
    890     shaping.m_spaceGlyph = spaceGlyph;
    891 
    892     // The ascent of a font for this run can be different from
    893     // that of the primary font so that we need to keep track of
    894     // the difference per run and take that into account when calling
    895     // ScriptTextOut in |draw|. Otherwise, different runs rendered by
    896     // different fonts would not be aligned vertically.
    897     shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
    898     result = true;
    899 
    900   cleanup:
    901     shaping.m_glyphs.resize(generatedGlyphs);
    902     shaping.m_visualAttributes.resize(generatedGlyphs);
    903     shaping.m_advance.resize(generatedGlyphs);
    904     shaping.m_offsets.resize(generatedGlyphs);
    905 
    906     // On failure, our logs don't mean anything, so zero those out.
    907     if (!result)
    908         shaping.m_logs.clear();
    909 
    910     return result;
    911 }
    912 
    913 void UniscribeHelper::EnsureCachedDCCreated()
    914 {
    915     if (m_cachedDC)
    916         return;
    917     // Allocate a memory DC that is compatible with the Desktop DC since we don't have any window,
    918     // and we don't want to use the Desktop DC directly since it can have nasty side effects
    919     // as identified in Chrome Issue http://crbug.com/59315.
    920     HWndDC screenDC(0);
    921     m_cachedDC = ::CreateCompatibleDC(screenDC);
    922     ASSERT(m_cachedDC);
    923 }
    924 
    925 void UniscribeHelper::fillShapes()
    926 {
    927     m_shapes.resize(m_runs.size());
    928     for (size_t i = 0; i < m_runs.size(); i++) {
    929         int startItem = m_runs[i].iCharPos;
    930         int itemLength = m_inputLength - startItem;
    931         if (i < m_runs.size() - 1)
    932             itemLength = m_runs[i + 1].iCharPos - startItem;
    933 
    934         int numGlyphs;
    935         if (itemLength < cUniscribeHelperStackChars) {
    936             // We'll start our buffer sizes with the current stack space
    937             // available in our buffers if the current input fits. As long as
    938             // it doesn't expand past that we'll save a lot of time mallocing.
    939             numGlyphs = cUniscribeHelperStackChars;
    940         } else {
    941             // When the input doesn't fit, give up with the stack since it will
    942             // almost surely not be enough room (unless the input actually
    943             // shrinks, which is unlikely) and just start with the length
    944             // recommended by the Uniscribe documentation as a "usually fits"
    945             // size.
    946             numGlyphs = itemLength * 3 / 2 + 16;
    947         }
    948 
    949         // Convert a string to a glyph string trying the primary font, fonts in
    950         // the fallback list and then script-specific last resort font.
    951         Shaping& shaping = m_shapes[i];
    952         if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], m_scriptTags[i], shaping))
    953             continue;
    954 
    955         // At the moment, the only time m_disableFontFallback is set is
    956         // when we look up glyph indices for non-BMP code ranges. So,
    957         // we can skip the glyph placement. When that becomes not the case
    958         // any more, we have to add a new flag to control glyph placement.
    959         if (m_disableFontFallback)
    960           continue;
    961 
    962         // Compute placements. Note that offsets is documented incorrectly
    963         // and is actually an array.
    964         EnsureCachedDCCreated();
    965         SelectObject(m_cachedDC, shaping.m_hfont);
    966         shaping.m_prePadding = 0;
    967         if (FAILED(ScriptPlace(m_cachedDC, shaping.m_scriptCache,
    968                                &shaping.m_glyphs[0],
    969                                static_cast<int>(shaping.m_glyphs.size()),
    970                                &shaping.m_visualAttributes[0], &m_runs[i].a,
    971                                &shaping.m_advance[0], &shaping.m_offsets[0],
    972                                &shaping.m_abc))) {
    973             // Some error we don't know how to handle. Nuke all of our data
    974             // since we can't deal with partially valid data later.
    975             m_runs.clear();
    976             m_scriptTags.clear();
    977             m_shapes.clear();
    978             m_screenOrder.clear();
    979         }
    980     }
    981 
    982     adjustSpaceAdvances();
    983 
    984     if (m_letterSpacing != 0 || m_wordSpacing != 0)
    985         applySpacing();
    986 }
    987 
    988 void UniscribeHelper::fillScreenOrder()
    989 {
    990     m_screenOrder.resize(m_runs.size());
    991 
    992     // We assume that the input has only one text direction in it.
    993     // TODO(brettw) are we sure we want to keep this restriction?
    994     if (m_isRtl) {
    995         for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
    996             m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
    997     } else {
    998         for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
    999             m_screenOrder[i] = i;
   1000     }
   1001 }
   1002 
   1003 void UniscribeHelper::adjustSpaceAdvances()
   1004 {
   1005     if (m_spaceWidth == 0)
   1006         return;
   1007 
   1008     int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
   1009 
   1010     // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
   1011     for (size_t run = 0; run < m_runs.size(); run++) {
   1012         Shaping& shaping = m_shapes[run];
   1013 
   1014         // FIXME: This loop is not UTF-16-safe. Unicode 6.0 has a couple
   1015         // of complex script blocks in Plane 1.
   1016         for (int i = 0; i < shaping.charLength(); i++) {
   1017             UChar c = m_input[m_runs[run].iCharPos + i];
   1018             bool treatAsSpace = Font::treatAsSpace(c);
   1019             if (!treatAsSpace && !Font::treatAsZeroWidthSpaceInComplexScript(c))
   1020                 continue;
   1021 
   1022             int glyphIndex = shaping.m_logs[i];
   1023             int currentAdvance = shaping.m_advance[glyphIndex];
   1024 
   1025             shaping.m_glyphs[glyphIndex] = shaping.m_spaceGlyph;
   1026 
   1027             if (treatAsSpace) {
   1028                 // currentAdvance does not include additional letter-spacing,
   1029                 // but m_spaceWidth does. Here we find out how off we are from
   1030                 // the correct width (spaceWidthWithoutLetterSpacing) and
   1031                 // just subtract that diff.
   1032                 int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
   1033                 // The shaping can consist of a run of text, so only subtract
   1034                 // the difference in the width of the glyph.
   1035                 shaping.m_advance[glyphIndex] -= diff;
   1036                 shaping.m_abc.abcB -= diff;
   1037                 continue;
   1038             }
   1039 
   1040             // For characters treated as zero-width space in complex
   1041             // scripts, set the advance width to zero, adjust
   1042             // |abcB| of the current run accordingly and set
   1043             // the glyph to m_spaceGlyph (invisible).
   1044             shaping.m_advance[glyphIndex] = 0;
   1045             shaping.m_abc.abcB -= currentAdvance;
   1046             shaping.m_offsets[glyphIndex].du = 0;
   1047             shaping.m_offsets[glyphIndex].dv = 0;
   1048         }
   1049     }
   1050 }
   1051 
   1052 void UniscribeHelper::applySpacing()
   1053 {
   1054     for (size_t run = 0; run < m_runs.size(); run++) {
   1055         Shaping& shaping = m_shapes[run];
   1056         bool isRtl = m_runs[run].a.fRTL;
   1057 
   1058         if (m_letterSpacing != 0) {
   1059             // RTL text gets padded to the left of each character. We increment
   1060             // the run's advance to make this happen. This will be balanced out
   1061             // by NOT adding additional advance to the last glyph in the run.
   1062             if (isRtl)
   1063                 shaping.m_prePadding += m_letterSpacing;
   1064 
   1065             // Go through all the glyphs in this run and increase the "advance"
   1066             // to account for letter spacing. We adjust letter spacing only on
   1067             // cluster boundaries.
   1068             //
   1069             // This works for most scripts, but may have problems with some
   1070             // indic scripts. This behavior is better than Firefox or IE for
   1071             // Hebrew.
   1072             for (int i = 0; i < shaping.glyphLength(); i++) {
   1073                 if (shaping.m_visualAttributes[i].fClusterStart) {
   1074                     // Ick, we need to assign the extra space so that the glyph
   1075                     // comes first, then is followed by the space. This is
   1076                     // opposite for RTL.
   1077                     if (isRtl) {
   1078                         if (i != shaping.glyphLength() - 1) {
   1079                             // All but the last character just get the spacing
   1080                             // applied to their advance. The last character
   1081                             // doesn't get anything,
   1082                             shaping.m_advance[i] += m_letterSpacing;
   1083                             shaping.m_abc.abcB += m_letterSpacing;
   1084                         }
   1085                     } else {
   1086                         // LTR case is easier, we just add to the advance.
   1087                         shaping.m_advance[i] += m_letterSpacing;
   1088                         shaping.m_abc.abcB += m_letterSpacing;
   1089                     }
   1090                 }
   1091             }
   1092         }
   1093 
   1094         // Go through all the characters to find whitespace and insert the
   1095         // extra wordspacing amount for the glyphs they correspond to.
   1096         if (m_wordSpacing != 0) {
   1097             for (int i = 0; i < shaping.charLength(); i++) {
   1098                 if (!Font::treatAsSpace(m_input[m_runs[run].iCharPos + i]))
   1099                     continue;
   1100 
   1101                 // The char in question is a word separator...
   1102                 int glyphIndex = shaping.m_logs[i];
   1103 
   1104                 // Spaces will not have a glyph in Uniscribe, it will just add
   1105                 // additional advance to the character to the left of the
   1106                 // space. The space's corresponding glyph will be the character
   1107                 // following it in reading order.
   1108                 if (isRtl) {
   1109                     // In RTL, the glyph to the left of the space is the same
   1110                     // as the first glyph of the following character, so we can
   1111                     // just increment it.
   1112                     shaping.m_advance[glyphIndex] += m_wordSpacing;
   1113                     shaping.m_abc.abcB += m_wordSpacing;
   1114                 } else {
   1115                     // LTR is actually more complex here, we apply it to the
   1116                     // previous character if there is one, otherwise we have to
   1117                     // apply it to the leading space of the run.
   1118                     if (glyphIndex == 0)
   1119                         shaping.m_prePadding += m_wordSpacing;
   1120                     else {
   1121                         shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
   1122                         shaping.m_abc.abcB += m_wordSpacing;
   1123                     }
   1124                 }
   1125             }
   1126         }  // m_wordSpacing != 0
   1127 
   1128         // Loop for next run...
   1129     }
   1130 }
   1131 
   1132 // The advance is the ABC width of the run
   1133 int UniscribeHelper::advanceForItem(int itemIndex) const
   1134 {
   1135     int accum = 0;
   1136     const Shaping& shaping = m_shapes[itemIndex];
   1137 
   1138     if (shaping.m_justify.size() == 0) {
   1139         // Easy case with no justification, the width is just the ABC width of
   1140         // the run. (The ABC width is the sum of the advances).
   1141         return shaping.m_abc.abcA + shaping.m_abc.abcB +
   1142                shaping.m_abc.abcC + shaping.m_prePadding;
   1143     }
   1144 
   1145     // With justification, we use the justified amounts instead. The
   1146     // justification array contains both the advance and the extra space
   1147     // added for justification, so is the width we want.
   1148     int justification = 0;
   1149     for (size_t i = 0; i < shaping.m_justify.size(); i++)
   1150         justification += shaping.m_justify[i];
   1151 
   1152     return shaping.m_prePadding + justification;
   1153 }
   1154 
   1155 // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
   1156 // and blank glyphs. Just because ScriptShape succeeds does not mean
   1157 // that a text run is rendered correctly. Some characters may be rendered
   1158 // with default/invalid/blank glyphs. Therefore, we need to check if the glyph
   1159 // array returned by ScriptShape contains any of those glyphs to make
   1160 // sure that the text run is rendered successfully.
   1161 // However, we should not subject zero-width characters to this test.
   1162 
   1163 bool UniscribeHelper::containsMissingGlyphs(const Shaping& shaping,
   1164                                             const SCRIPT_ITEM& run,
   1165                                             const SCRIPT_FONTPROPERTIES* properties) const
   1166 {
   1167     for (int i = 0; i < shaping.charLength(); i++) {
   1168         UChar c = m_input[run.iCharPos + i];
   1169         // Skip zero-width space characters because they're not considered to
   1170         // be missing in a font.
   1171         if (Font::treatAsZeroWidthSpaceInComplexScript(c))
   1172             continue;
   1173         int glyphIndex = shaping.m_logs[i];
   1174         WORD glyph = shaping.m_glyphs[glyphIndex];
   1175         // Note on the thrid condition: Windows Vista sometimes returns glyphs
   1176         // equal to wgBlank (instead of wgDefault), with fZeroWidth set. Treat
   1177         // such cases as having missing glyphs if the corresponding character
   1178         // is not a zero width whitespace.
   1179         if (glyph == properties->wgDefault
   1180             || (glyph == properties->wgInvalid && glyph != properties->wgBlank)
   1181             || (glyph == properties->wgBlank && shaping.m_visualAttributes[glyphIndex].fZeroWidth && !Font::treatAsZeroWidthSpace(c)))
   1182             return true;
   1183     }
   1184     return false;
   1185 }
   1186 
   1187 static OPENTYPE_TAG convertFeatureTag(const String& tag)
   1188 {
   1189     return ((tag[0] & 0xFF) | ((tag[1] & 0xFF) << 8) | ((tag[2] & 0xFF) << 16) | ((tag[3] & 0xFF) << 24));
   1190 }
   1191 
   1192 void UniscribeHelper::setRangeProperties(const FontFeatureSettings* featureSettings)
   1193 {
   1194     if (!featureSettings || !featureSettings->size()) {
   1195         m_featureRecords.resize(0);
   1196         return;
   1197     }
   1198 
   1199     m_featureRecords.resize(featureSettings->size());
   1200     for (unsigned i = 0; i < featureSettings->size(); ++i) {
   1201         m_featureRecords[i].lParameter = featureSettings->at(i).value();
   1202         m_featureRecords[i].tagFeature = convertFeatureTag(featureSettings->at(i).tag());
   1203     }
   1204     m_rangeProperties.potfRecords = &m_featureRecords[0];
   1205     m_rangeProperties.cotfRecords = m_featureRecords.size();
   1206 }
   1207 
   1208 }  // namespace WebCore
   1209