Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (c) 2006, 2007, 2008, 2009, 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 "UniscribeHelper.h"
     33 
     34 #include <windows.h>
     35 
     36 #include "FontUtilsChromiumWin.h"
     37 #include "PlatformContextSkia.h"
     38 #include "SkiaFontWin.h"
     39 #include "SkPoint.h"
     40 #include <wtf/Assertions.h>
     41 
     42 namespace WebCore {
     43 
     44 // This function is used to see where word spacing should be applied inside
     45 // runs. Note that this must match Font::treatAsSpace so we all agree where
     46 // and how much space this is, so we don't want to do more general Unicode
     47 // "is this a word break" thing.
     48 static bool treatAsSpace(UChar c)
     49 {
     50     return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
     51 }
     52 
     53 // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
     54 // and blank glyphs. Just because ScriptShape succeeds does not mean
     55 // that a text run is rendered correctly. Some characters may be rendered
     56 // with default/invalid/blank glyphs. Therefore, we need to check if the glyph
     57 // array returned by ScriptShape contains any of those glyphs to make
     58 // sure that the text run is rendered successfully.
     59 static bool containsMissingGlyphs(WORD *glyphs,
     60                                   int length,
     61                                   SCRIPT_FONTPROPERTIES* properties)
     62 {
     63     for (int i = 0; i < length; ++i) {
     64         if (glyphs[i] == properties->wgDefault
     65             || (glyphs[i] == properties->wgInvalid
     66             && glyphs[i] != properties->wgBlank))
     67             return true;
     68     }
     69 
     70     return false;
     71 }
     72 
     73 // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
     74 // handle and we can't directly query it to make a new HFONT sharing
     75 // its characteristics (height, style, etc) except for family name.
     76 // This function uses GetObject to convert HFONT back to LOGFONT,
     77 // resets the fields of LOGFONT and calculates style to use later
     78 // for the creation of a font identical to HFONT other than family name.
     79 static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
     80 {
     81     ASSERT(hfont && logfont);
     82     if (!hfont || !logfont)
     83         return;
     84 
     85     GetObject(hfont, sizeof(LOGFONT), logfont);
     86     // We reset these fields to values appropriate for CreateFontIndirect.
     87     // while keeping lfHeight, which is the most important value in creating
     88     // a new font similar to hfont.
     89     logfont->lfWidth = 0;
     90     logfont->lfEscapement = 0;
     91     logfont->lfOrientation = 0;
     92     logfont->lfCharSet = DEFAULT_CHARSET;
     93     logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
     94     logfont->lfQuality = DEFAULT_QUALITY;  // Honor user's desktop settings.
     95     logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
     96     if (style)
     97         *style = getStyleFromLogfont(logfont);
     98 }
     99 
    100 UniscribeHelper::UniscribeHelper(const UChar* input,
    101                                 int inputLength,
    102                                 bool isRtl,
    103                                 HFONT hfont,
    104                                 SCRIPT_CACHE* scriptCache,
    105                                 SCRIPT_FONTPROPERTIES* fontProperties)
    106     : m_input(input)
    107     , m_inputLength(inputLength)
    108     , m_isRtl(isRtl)
    109     , m_hfont(hfont)
    110     , m_scriptCache(scriptCache)
    111     , m_fontProperties(fontProperties)
    112     , m_directionalOverride(false)
    113     , m_inhibitLigate(false)
    114     , m_letterSpacing(0)
    115     , m_spaceWidth(0)
    116     , m_wordSpacing(0)
    117     , m_ascent(0)
    118     , m_disableFontFallback(false)
    119 
    120 {
    121     m_logfont.lfFaceName[0] = 0;
    122 }
    123 
    124 UniscribeHelper::~UniscribeHelper()
    125 {
    126 }
    127 
    128 void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection)
    129 {
    130     // We cap the input length and just don't do anything. We'll allocate a lot
    131     // of things of the size of the number of characters, so the allocated
    132     // memory will be several times the input length. Plus shaping such a large
    133     // buffer may be a form of denial of service. No legitimate text should be
    134     // this long.  It also appears that Uniscribe flatly rejects very long
    135     // strings, so we don't lose anything by doing this.
    136     //
    137     // The input length protection may be disabled by the unit tests to cause
    138     // an error condition.
    139     static const int kMaxInputLength = 65535;
    140     if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength))
    141         return;
    142 
    143     fillRuns();
    144     fillShapes();
    145     fillScreenOrder();
    146 }
    147 
    148 int UniscribeHelper::width() const
    149 {
    150     int width = 0;
    151     for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++)
    152         width += advanceForItem(itemIndex);
    153     return width;
    154 }
    155 
    156 void UniscribeHelper::justify(int additionalSpace)
    157 {
    158     // Count the total number of glyphs we have so we know how big to make the
    159     // buffers below.
    160     int totalGlyphs = 0;
    161     for (size_t run = 0; run < m_runs.size(); run++) {
    162         int runIndex = m_screenOrder[run];
    163         totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength());
    164     }
    165     if (totalGlyphs == 0)
    166         return;  // Nothing to do.
    167 
    168     // We make one big buffer in screen order of all the glyphs we are drawing
    169     // across runs so that the justification function will adjust evenly across
    170     // all glyphs.
    171     Vector<SCRIPT_VISATTR, 64> visualAttributes;
    172     visualAttributes.resize(totalGlyphs);
    173     Vector<int, 64> advances;
    174     advances.resize(totalGlyphs);
    175     Vector<int, 64> justify;
    176     justify.resize(totalGlyphs);
    177 
    178     // Build the packed input.
    179     int destIndex = 0;
    180     for (size_t run = 0; run < m_runs.size(); run++) {
    181         int runIndex = m_screenOrder[run];
    182         const Shaping& shaping = m_shapes[runIndex];
    183 
    184         for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) {
    185             memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i],
    186                    sizeof(SCRIPT_VISATTR));
    187             advances[destIndex] = shaping.m_advance[i];
    188         }
    189     }
    190 
    191     // The documentation for Scriptjustify is wrong, the parameter is the space
    192     // to add and not the width of the column you want.
    193     const int minKashida = 1;  // How do we decide what this should be?
    194     ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs,
    195                   additionalSpace, minKashida, &justify[0]);
    196 
    197     // Now we have to unpack the justification amounts back into the runs so
    198     // the glyph indices match.
    199     int globalGlyphIndex = 0;
    200     for (size_t run = 0; run < m_runs.size(); run++) {
    201         int runIndex = m_screenOrder[run];
    202         Shaping& shaping = m_shapes[runIndex];
    203 
    204         shaping.m_justify.resize(shaping.glyphLength());
    205         for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
    206             shaping.m_justify[i] = justify[globalGlyphIndex];
    207     }
    208 }
    209 
    210 int UniscribeHelper::characterToX(int offset) const
    211 {
    212     HRESULT hr;
    213     ASSERT(offset <= m_inputLength);
    214 
    215     // Our algorithm is to traverse the items in screen order from left to
    216     // right, adding in each item's screen width until we find the item with
    217     // the requested character in it.
    218     int width = 0;
    219     for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
    220         // Compute the length of this run.
    221         int itemIndex = m_screenOrder[screenIndex];
    222         const SCRIPT_ITEM& item = m_runs[itemIndex];
    223         const Shaping& shaping = m_shapes[itemIndex];
    224         int itemLength = shaping.charLength();
    225 
    226         if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
    227             // Character offset is in this run.
    228             int charLength = offset - item.iCharPos;
    229 
    230             int curX = 0;
    231             hr = ScriptCPtoX(charLength, FALSE, itemLength,
    232                              shaping.glyphLength(),
    233                              &shaping.m_logs[0], &shaping.m_visualAttributes[0],
    234                              shaping.effectiveAdvances(), &item.a, &curX);
    235             if (FAILED(hr))
    236                 return 0;
    237 
    238             width += curX + shaping.m_prePadding;
    239             ASSERT(width >= 0);
    240             return width;
    241         }
    242 
    243         // Move to the next item.
    244         width += advanceForItem(itemIndex);
    245     }
    246     ASSERT(width >= 0);
    247     return width;
    248 }
    249 
    250 int UniscribeHelper::xToCharacter(int x) const
    251 {
    252     // We iterate in screen order until we find the item with the given pixel
    253     // position in it. When we find that guy, we ask Uniscribe for the
    254     // character index.
    255     HRESULT hr;
    256     for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
    257         int itemIndex = m_screenOrder[screenIndex];
    258         int itemAdvance = advanceForItem(itemIndex);
    259 
    260         // Note that the run may be empty if shaping failed, so we want to skip
    261         // over it.
    262         const Shaping& shaping = m_shapes[itemIndex];
    263         int itemLength = shaping.charLength();
    264         if (x <= itemAdvance && itemLength > 0) {
    265             // The requested offset is within this item.
    266             const SCRIPT_ITEM& item = m_runs[itemIndex];
    267 
    268             // Account for the leading space we've added to this run that
    269             // Uniscribe doesn't know about.
    270             x -= shaping.m_prePadding;
    271 
    272             int charX = 0;
    273             int trailing;
    274             hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
    275                              &shaping.m_logs[0], &shaping.m_visualAttributes[0],
    276                              shaping.effectiveAdvances(), &item.a, &charX,
    277                              &trailing);
    278 
    279             // The character offset is within the item. We need to add the
    280             // item's offset to transform it into the space of the TextRun
    281             return charX + item.iCharPos;
    282         }
    283 
    284         // The offset is beyond this item, account for its length and move on.
    285         x -= itemAdvance;
    286     }
    287 
    288     // Error condition, we don't know what to do if we don't have that X
    289     // position in any of our items.
    290     return 0;
    291 }
    292 
    293 void UniscribeHelper::draw(GraphicsContext* graphicsContext,
    294                            HDC dc, int x, int y, int from, int to)
    295 {
    296     HGDIOBJ oldFont = 0;
    297     int curX = x;
    298     bool firstRun = true;
    299     bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext);
    300 
    301     for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
    302         int itemIndex = m_screenOrder[screenIndex];
    303         const SCRIPT_ITEM& item = m_runs[itemIndex];
    304         const Shaping& shaping = m_shapes[itemIndex];
    305 
    306         // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
    307         // be negative, etc. The code below handles this.
    308         int fromChar = from - item.iCharPos;
    309         int toChar = to - item.iCharPos;
    310 
    311         // See if we need to draw any characters in this item.
    312         if (shaping.charLength() == 0 ||
    313             fromChar >= shaping.charLength() || toChar <= 0) {
    314             // No chars in this item to display.
    315             curX += advanceForItem(itemIndex);
    316             continue;
    317         }
    318 
    319         // Compute the starting glyph within this span. |from| and |to| are
    320         // global offsets that may intersect arbitrarily with our local run.
    321         int fromGlyph, afterGlyph;
    322         if (item.a.fRTL) {
    323             // To compute the first glyph when going RTL, we use |to|.
    324             if (toChar >= shaping.charLength())
    325                 // The end of the text is after (to the left) of us.
    326                 fromGlyph = 0;
    327             else {
    328                 // Since |to| is exclusive, the first character we draw on the
    329                 // left is actually the one right before (to the right) of
    330                 // |to|.
    331                 fromGlyph = shaping.m_logs[toChar - 1];
    332             }
    333 
    334             // The last glyph is actually the first character in the range.
    335             if (fromChar <= 0) {
    336                 // The first character to draw is before (to the right) of this
    337                 // span, so draw all the way to the end.
    338                 afterGlyph = shaping.glyphLength();
    339             } else {
    340                 // We want to draw everything up until the character to the
    341                 // right of |from|. To the right is - 1, so we look that up
    342                 // (remember our character could be more than one glyph, so we
    343                 // can't look up our glyph and add one).
    344                 afterGlyph = shaping.m_logs[fromChar - 1];
    345             }
    346         } else {
    347             // Easy case, everybody agrees about directions. We only need to
    348             // handle boundary conditions to get a range inclusive at the
    349             // beginning, and exclusive at the ending. We have to do some
    350             // computation to see the glyph one past the end.
    351             fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
    352             if (toChar >= shaping.charLength())
    353                 afterGlyph = shaping.glyphLength();
    354             else
    355                 afterGlyph = shaping.m_logs[toChar];
    356         }
    357 
    358         // Account for the characters that were skipped in this run. When
    359         // WebKit asks us to draw a subset of the run, it actually tells us
    360         // to draw at the X offset of the beginning of the run, since it
    361         // doesn't know the internal position of any of our characters.
    362         const int* effectiveAdvances = shaping.effectiveAdvances();
    363         int innerOffset = 0;
    364         for (int i = 0; i < fromGlyph; i++)
    365             innerOffset += effectiveAdvances[i];
    366 
    367         // Actually draw the glyphs we found.
    368         int glyphCount = afterGlyph - fromGlyph;
    369         if (fromGlyph >= 0 && glyphCount > 0) {
    370             // Account for the preceding space we need to add to this run. We
    371             // don't need to count for the following space because that will be
    372             // counted in advanceForItem below when we move to the next run.
    373             innerOffset += shaping.m_prePadding;
    374 
    375             // Pass 0 in when there is no justification.
    376             const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph];
    377 
    378             if (useWindowsDrawing) {
    379                 if (firstRun) {
    380                     oldFont = SelectObject(dc, shaping.m_hfont);
    381                     firstRun = false;
    382                 } else
    383                     SelectObject(dc, shaping.m_hfont);
    384             }
    385 
    386             // Fonts with different ascents can be used to render different
    387             // runs.  'Across-runs' y-coordinate correction needs to be
    388             // adjusted for each font.
    389             bool textOutOk = false;
    390             for (int executions = 0; executions < 2; ++executions) {
    391                 if (useWindowsDrawing) {
    392                     HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache,
    393                                                curX + innerOffset,
    394                                                y - shaping.m_ascentOffset,
    395                                                0, 0, &item.a, 0, 0,
    396                                                &shaping.m_glyphs[fromGlyph],
    397                                                glyphCount,
    398                                                &shaping.m_advance[fromGlyph],
    399                                                justify,
    400                                                &shaping.m_offsets[fromGlyph]);
    401                     ASSERT(S_OK == hr);
    402                     textOutOk = (hr == S_OK);
    403                 } else {
    404                     SkPoint origin;
    405                     origin.fX = curX + + innerOffset;
    406                     origin.fY = y + m_ascent;
    407                     textOutOk = paintSkiaText(graphicsContext,
    408                                               shaping.m_hfont,
    409                                               glyphCount,
    410                                               &shaping.m_glyphs[fromGlyph],
    411                                               &shaping.m_advance[fromGlyph],
    412                                               &shaping.m_offsets[fromGlyph],
    413                                               &origin);
    414                 }
    415 
    416                 if (!textOutOk && 0 == executions) {
    417                     // If TextOut is called from the renderer it might fail
    418                     // because the sandbox is preventing it from opening the
    419                     // font files.  If we are running in the renderer,
    420                     // TryToPreloadFont is overridden to ask the browser to
    421                     // preload the font for us so we can access it.
    422                     tryToPreloadFont(shaping.m_hfont);
    423                     continue;
    424                 }
    425                 break;
    426             }
    427         }
    428 
    429         curX += advanceForItem(itemIndex);
    430     }
    431 
    432     if (oldFont)
    433         SelectObject(dc, oldFont);
    434 }
    435 
    436 WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const
    437 {
    438     // Find the run for the given character.
    439     for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
    440         int firstChar = m_runs[i].iCharPos;
    441         const Shaping& shaping = m_shapes[i];
    442         int localOffset = charOffset - firstChar;
    443         if (localOffset >= 0 && localOffset < shaping.charLength()) {
    444             // The character is in this run, return the first glyph for it
    445             // (should generally be the only glyph). It seems Uniscribe gives
    446             // glyph 0 for empty, which is what we want to return in the
    447             // "missing" case.
    448             size_t glyphIndex = shaping.m_logs[localOffset];
    449             if (glyphIndex >= shaping.m_glyphs.size()) {
    450                 // The glyph should be in this run, but the run has too few
    451                 // actual characters. This can happen when shaping the run
    452                 // fails, in which case, we should have no data in the logs at
    453                 // all.
    454                 ASSERT(shaping.m_glyphs.size() == 0);
    455                 return 0;
    456             }
    457             return shaping.m_glyphs[glyphIndex];
    458         }
    459     }
    460 
    461     return 0;
    462 }
    463 
    464 void UniscribeHelper::fillRuns()
    465 {
    466     HRESULT hr;
    467     m_runs.resize(UNISCRIBE_HELPER_STACK_RUNS);
    468 
    469     SCRIPT_STATE inputState;
    470     inputState.uBidiLevel = m_isRtl;
    471     inputState.fOverrideDirection = m_directionalOverride;
    472     inputState.fInhibitSymSwap = false;
    473     inputState.fCharShape = false;  // Not implemented in Uniscribe
    474     inputState.fDigitSubstitute = false;  // Do we want this for Arabic?
    475     inputState.fInhibitLigate = m_inhibitLigate;
    476     inputState.fDisplayZWG = false;  // Don't draw control characters.
    477     inputState.fArabicNumContext = m_isRtl;  // Do we want this for Arabic?
    478     inputState.fGcpClusters = false;
    479     inputState.fReserved = 0;
    480     inputState.fEngineReserved = 0;
    481     // The psControl argument to ScriptItemize should be non-0 for RTL text,
    482     // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
    483     // SCRIPT_CONTROL that is set to all zeros.  Zero as a locale ID means the
    484     // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx
    485     static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage    :16;
    486                                            0, // fContextDigits      :1;
    487                                            0, // fInvertPreBoundDir  :1;
    488                                            0, // fInvertPostBoundDir :1;
    489                                            0, // fLinkStringBefore   :1;
    490                                            0, // fLinkStringAfter    :1;
    491                                            0, // fNeutralOverride    :1;
    492                                            0, // fNumericOverride    :1;
    493                                            0, // fLegacyBidiClass    :1;
    494                                            0, // fMergeNeutralItems  :1;
    495                                            0};// fReserved           :7;
    496     // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState)
    497     // here would be appropriate if we wanted to set the language ID, and get
    498     // local digit substitution behavior.  For now, don't do it.
    499 
    500     while (true) {
    501         int numberOfItems = 0;
    502 
    503         // Ideally, we would have a way to know the runs before and after this
    504         // one, and put them into the control parameter of ScriptItemize. This
    505         // would allow us to shape characters properly that cross style
    506         // boundaries (WebKit bug 6148).
    507         //
    508         // We tell ScriptItemize that the output list of items is one smaller
    509         // than it actually is. According to Mozilla bug 366643, if there is
    510         // not enough room in the array on pre-SP2 systems, ScriptItemize will
    511         // write one past the end of the buffer.
    512         //
    513         // ScriptItemize is very strange. It will often require a much larger
    514         // ITEM buffer internally than it will give us as output. For example,
    515         // it will say a 16-item buffer is not big enough, and will write
    516         // interesting numbers into all those items. But when we give it a 32
    517         // item buffer and it succeeds, it only has one item output.
    518         //
    519         // It seems to be doing at least two passes, the first where it puts a
    520         // lot of intermediate data into our items, and the second where it
    521         // collates them.
    522         hr = ScriptItemize(m_input, m_inputLength,
    523                            static_cast<int>(m_runs.size()) - 1, &inputControl,
    524                            &inputState,
    525                            &m_runs[0], &numberOfItems);
    526         if (SUCCEEDED(hr)) {
    527             m_runs.resize(numberOfItems);
    528             break;
    529         }
    530         if (hr != E_OUTOFMEMORY) {
    531             // Some kind of unexpected error.
    532             m_runs.resize(0);
    533             break;
    534         }
    535         // There was not enough items for it to write into, expand.
    536         m_runs.resize(m_runs.size() * 2);
    537     }
    538 }
    539 
    540 bool UniscribeHelper::shape(const UChar* input,
    541                             int itemLength,
    542                             int numGlyphs,
    543                             SCRIPT_ITEM& run,
    544                             Shaping& shaping)
    545 {
    546     HFONT hfont = m_hfont;
    547     SCRIPT_CACHE* scriptCache = m_scriptCache;
    548     SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
    549     int ascent = m_ascent;
    550     HDC tempDC = 0;
    551     HGDIOBJ oldFont = 0;
    552     HRESULT hr;
    553     // When used to fill up glyph pages for simple scripts in non-BMP,
    554     // we don't want any font fallback in this class. The simple script
    555     // font path can take care of font fallback.
    556     bool lastFallbackTried = m_disableFontFallback;
    557     bool result;
    558 
    559     int generatedGlyphs = 0;
    560 
    561     // In case HFONT passed in ctor cannot render this run, we have to scan
    562     // other fonts from the beginning of the font list.
    563     resetFontIndex();
    564 
    565     // Compute shapes.
    566     while (true) {
    567         shaping.m_logs.resize(itemLength);
    568         shaping.m_glyphs.resize(numGlyphs);
    569         shaping.m_visualAttributes.resize(numGlyphs);
    570 
    571 #ifdef PURIFY
    572         // http://code.google.com/p/chromium/issues/detail?id=5309
    573         // Purify isn't able to track the assignments that ScriptShape makes to
    574         // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it
    575         // writes, will be considered un-initialized data.
    576         //
    577         // This hack avoid the false-positive UMRs by marking the buffer as
    578         // initialized.
    579         //
    580         // FIXME: A better solution would be to use Purify's API and mark only
    581         // the populated range as initialized:
    582         //
    583         //     PurifyMarkAsInitialized(
    584         //         &shaping.m_glyphs[0],
    585         //         sizeof(shaping.m_glyphs[0] * generatedGlyphs);
    586 
    587         ZeroMemory(&shaping.m_glyphs[0],
    588                    sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
    589 #endif
    590 
    591         // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
    592         // here. Is that what we want? It will display control characters.
    593         hr = ScriptShape(tempDC, scriptCache, input, itemLength,
    594                          numGlyphs, &run.a,
    595                          &shaping.m_glyphs[0], &shaping.m_logs[0],
    596                          &shaping.m_visualAttributes[0], &generatedGlyphs);
    597         if (hr == E_PENDING) {
    598             // Allocate the DC.
    599             tempDC = GetDC(0);
    600             oldFont = SelectObject(tempDC, hfont);
    601             continue;
    602         } else if (hr == E_OUTOFMEMORY) {
    603             numGlyphs *= 2;
    604             continue;
    605         } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(&shaping.m_glyphs[0], generatedGlyphs, fontProperties)))
    606             break;
    607 
    608         // The current font can't render this run. clear DC and try
    609         // next font.
    610         if (tempDC) {
    611             SelectObject(tempDC, oldFont);
    612             ReleaseDC(0, tempDC);
    613             tempDC = 0;
    614         }
    615 
    616         if (!m_disableFontFallback &&
    617             nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) {
    618             // The primary font does not support this run. Try next font.
    619             // In case of web page rendering, they come from fonts specified in
    620             // CSS stylesheets.
    621             continue;
    622         } else if (!lastFallbackTried) {
    623             lastFallbackTried = true;
    624 
    625             // Generate a last fallback font based on the script of
    626             // a character to draw while inheriting size and styles
    627             // from the primary font
    628             if (!m_logfont.lfFaceName[0])
    629                 setLogFontAndStyle(m_hfont, &m_logfont, &m_style);
    630 
    631             // TODO(jungshik): generic type should come from webkit for
    632             // UniscribeHelperTextRun (a derived class used in webkit).
    633             const UChar *family = getFallbackFamily(input, itemLength,
    634                 FontDescription::StandardFamily, 0, 0);
    635             bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
    636                                               &ascent, &hfont, &scriptCache);
    637 
    638             if (!fontOk) {
    639                 // If this GetDerivedFontData is called from the renderer it
    640                 // might fail because the sandbox is preventing it from opening
    641                 // the font files.  If we are running in the renderer,
    642                 // TryToPreloadFont is overridden to ask the browser to preload
    643                 // the font for us so we can access it.
    644                 tryToPreloadFont(hfont);
    645 
    646                 // Try again.
    647                 fontOk = getDerivedFontData(family, m_style, &m_logfont,
    648                                              &ascent, &hfont, &scriptCache);
    649                 ASSERT(fontOk);
    650             }
    651 
    652             // TODO(jungshik) : Currently GetDerivedHFont always returns a
    653             // a valid HFONT, but in the future, I may change it to return 0.
    654             ASSERT(hfont);
    655 
    656             // We don't need a font_properties for the last resort fallback font
    657             // because we don't have anything more to try and are forced to
    658             // accept empty glyph boxes. If we tried a series of fonts as
    659             // 'last-resort fallback', we'd need it, but currently, we don't.
    660             continue;
    661         } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
    662             run.a.eScript = SCRIPT_UNDEFINED;
    663             continue;
    664         } else if (FAILED(hr)) {
    665             // Error shaping.
    666             generatedGlyphs = 0;
    667             result = false;
    668             goto cleanup;
    669         }
    670     }
    671 
    672     // Sets Windows font data for this run to those corresponding to
    673     // a font supporting this run. we don't need to store font_properties
    674     // because it's not used elsewhere.
    675     shaping.m_hfont = hfont;
    676     shaping.m_scriptCache = scriptCache;
    677 
    678     // The ascent of a font for this run can be different from
    679     // that of the primary font so that we need to keep track of
    680     // the difference per run and take that into account when calling
    681     // ScriptTextOut in |draw|. Otherwise, different runs rendered by
    682     // different fonts would not be aligned vertically.
    683     shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
    684     result = true;
    685 
    686   cleanup:
    687     shaping.m_glyphs.resize(generatedGlyphs);
    688     shaping.m_visualAttributes.resize(generatedGlyphs);
    689     shaping.m_advance.resize(generatedGlyphs);
    690     shaping.m_offsets.resize(generatedGlyphs);
    691     if (tempDC) {
    692         SelectObject(tempDC, oldFont);
    693         ReleaseDC(0, tempDC);
    694     }
    695     // On failure, our logs don't mean anything, so zero those out.
    696     if (!result)
    697         shaping.m_logs.clear();
    698 
    699     return result;
    700 }
    701 
    702 void UniscribeHelper::fillShapes()
    703 {
    704     m_shapes.resize(m_runs.size());
    705     for (size_t i = 0; i < m_runs.size(); i++) {
    706         int startItem = m_runs[i].iCharPos;
    707         int itemLength = m_inputLength - startItem;
    708         if (i < m_runs.size() - 1)
    709             itemLength = m_runs[i + 1].iCharPos - startItem;
    710 
    711         int numGlyphs;
    712         if (itemLength < UNISCRIBE_HELPER_STACK_CHARS) {
    713             // We'll start our buffer sizes with the current stack space
    714             // available in our buffers if the current input fits. As long as
    715             // it doesn't expand past that we'll save a lot of time mallocing.
    716             numGlyphs = UNISCRIBE_HELPER_STACK_CHARS;
    717         } else {
    718             // When the input doesn't fit, give up with the stack since it will
    719             // almost surely not be enough room (unless the input actually
    720             // shrinks, which is unlikely) and just start with the length
    721             // recommended by the Uniscribe documentation as a "usually fits"
    722             // size.
    723             numGlyphs = itemLength * 3 / 2 + 16;
    724         }
    725 
    726         // Convert a string to a glyph string trying the primary font, fonts in
    727         // the fallback list and then script-specific last resort font.
    728         Shaping& shaping = m_shapes[i];
    729         if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping))
    730             continue;
    731 
    732         // At the moment, the only time m_disableFontFallback is set is
    733         // when we look up glyph indices for non-BMP code ranges. So,
    734         // we can skip the glyph placement. When that becomes not the case
    735         // any more, we have to add a new flag to control glyph placement.
    736         if (m_disableFontFallback)
    737           continue;
    738 
    739         // Compute placements. Note that offsets is documented incorrectly
    740         // and is actually an array.
    741 
    742         // DC that we lazily create if Uniscribe commands us to.
    743         // (this does not happen often because scriptCache is already
    744         //  updated when calling ScriptShape).
    745         HDC tempDC = 0;
    746         HGDIOBJ oldFont = 0;
    747         HRESULT hr;
    748         while (true) {
    749             shaping.m_prePadding = 0;
    750             hr = ScriptPlace(tempDC, shaping.m_scriptCache,
    751                              &shaping.m_glyphs[0],
    752                              static_cast<int>(shaping.m_glyphs.size()),
    753                              &shaping.m_visualAttributes[0], &m_runs[i].a,
    754                              &shaping.m_advance[0], &shaping.m_offsets[0],
    755                              &shaping.m_abc);
    756             if (hr != E_PENDING)
    757                 break;
    758 
    759             // Allocate the DC and run the loop again.
    760             tempDC = GetDC(0);
    761             oldFont = SelectObject(tempDC, shaping.m_hfont);
    762         }
    763 
    764         if (FAILED(hr)) {
    765             // Some error we don't know how to handle. Nuke all of our data
    766             // since we can't deal with partially valid data later.
    767             m_runs.clear();
    768             m_shapes.clear();
    769             m_screenOrder.clear();
    770         }
    771 
    772         if (tempDC) {
    773             SelectObject(tempDC, oldFont);
    774             ReleaseDC(0, tempDC);
    775         }
    776     }
    777 
    778     adjustSpaceAdvances();
    779 
    780     if (m_letterSpacing != 0 || m_wordSpacing != 0)
    781         applySpacing();
    782 }
    783 
    784 void UniscribeHelper::fillScreenOrder()
    785 {
    786     m_screenOrder.resize(m_runs.size());
    787 
    788     // We assume that the input has only one text direction in it.
    789     // TODO(brettw) are we sure we want to keep this restriction?
    790     if (m_isRtl) {
    791         for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
    792             m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
    793     } else {
    794         for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
    795             m_screenOrder[i] = i;
    796     }
    797 }
    798 
    799 void UniscribeHelper::adjustSpaceAdvances()
    800 {
    801     if (m_spaceWidth == 0)
    802         return;
    803 
    804     int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
    805 
    806     // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
    807     for (size_t run = 0; run < m_runs.size(); run++) {
    808         Shaping& shaping = m_shapes[run];
    809 
    810         for (int i = 0; i < shaping.charLength(); i++) {
    811             if (!treatAsSpace(m_input[m_runs[run].iCharPos + i]))
    812                 continue;
    813 
    814             int glyphIndex = shaping.m_logs[i];
    815             int currentAdvance = shaping.m_advance[glyphIndex];
    816 
    817             // currentAdvance does not include additional letter-spacing, but
    818             // space_width does. Here we find out how off we are from the
    819             // correct width for the space not including letter-spacing, then
    820             // just subtract that diff.
    821             int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
    822             // The shaping can consist of a run of text, so only subtract the
    823             // difference in the width of the glyph.
    824             shaping.m_advance[glyphIndex] -= diff;
    825             shaping.m_abc.abcB -= diff;
    826         }
    827     }
    828 }
    829 
    830 void UniscribeHelper::applySpacing()
    831 {
    832     for (size_t run = 0; run < m_runs.size(); run++) {
    833         Shaping& shaping = m_shapes[run];
    834         bool isRtl = m_runs[run].a.fRTL;
    835 
    836         if (m_letterSpacing != 0) {
    837             // RTL text gets padded to the left of each character. We increment
    838             // the run's advance to make this happen. This will be balanced out
    839             // by NOT adding additional advance to the last glyph in the run.
    840             if (isRtl)
    841                 shaping.m_prePadding += m_letterSpacing;
    842 
    843             // Go through all the glyphs in this run and increase the "advance"
    844             // to account for letter spacing. We adjust letter spacing only on
    845             // cluster boundaries.
    846             //
    847             // This works for most scripts, but may have problems with some
    848             // indic scripts. This behavior is better than Firefox or IE for
    849             // Hebrew.
    850             for (int i = 0; i < shaping.glyphLength(); i++) {
    851                 if (shaping.m_visualAttributes[i].fClusterStart) {
    852                     // Ick, we need to assign the extra space so that the glyph
    853                     // comes first, then is followed by the space. This is
    854                     // opposite for RTL.
    855                     if (isRtl) {
    856                         if (i != shaping.glyphLength() - 1) {
    857                             // All but the last character just get the spacing
    858                             // applied to their advance. The last character
    859                             // doesn't get anything,
    860                             shaping.m_advance[i] += m_letterSpacing;
    861                             shaping.m_abc.abcB += m_letterSpacing;
    862                         }
    863                     } else {
    864                         // LTR case is easier, we just add to the advance.
    865                         shaping.m_advance[i] += m_letterSpacing;
    866                         shaping.m_abc.abcB += m_letterSpacing;
    867                     }
    868                 }
    869             }
    870         }
    871 
    872         // Go through all the characters to find whitespace and insert the
    873         // extra wordspacing amount for the glyphs they correspond to.
    874         if (m_wordSpacing != 0) {
    875             for (int i = 0; i < shaping.charLength(); i++) {
    876                 if (!treatAsSpace(m_input[m_runs[run].iCharPos + i]))
    877                     continue;
    878 
    879                 // The char in question is a word separator...
    880                 int glyphIndex = shaping.m_logs[i];
    881 
    882                 // Spaces will not have a glyph in Uniscribe, it will just add
    883                 // additional advance to the character to the left of the
    884                 // space. The space's corresponding glyph will be the character
    885                 // following it in reading order.
    886                 if (isRtl) {
    887                     // In RTL, the glyph to the left of the space is the same
    888                     // as the first glyph of the following character, so we can
    889                     // just increment it.
    890                     shaping.m_advance[glyphIndex] += m_wordSpacing;
    891                     shaping.m_abc.abcB += m_wordSpacing;
    892                 } else {
    893                     // LTR is actually more complex here, we apply it to the
    894                     // previous character if there is one, otherwise we have to
    895                     // apply it to the leading space of the run.
    896                     if (glyphIndex == 0)
    897                         shaping.m_prePadding += m_wordSpacing;
    898                     else {
    899                         shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
    900                         shaping.m_abc.abcB += m_wordSpacing;
    901                     }
    902                 }
    903             }
    904         }  // m_wordSpacing != 0
    905 
    906         // Loop for next run...
    907     }
    908 }
    909 
    910 // The advance is the ABC width of the run
    911 int UniscribeHelper::advanceForItem(int itemIndex) const
    912 {
    913     int accum = 0;
    914     const Shaping& shaping = m_shapes[itemIndex];
    915 
    916     if (shaping.m_justify.size() == 0) {
    917         // Easy case with no justification, the width is just the ABC width of
    918         // the run. (The ABC width is the sum of the advances).
    919         return shaping.m_abc.abcA + shaping.m_abc.abcB +
    920                shaping.m_abc.abcC + shaping.m_prePadding;
    921     }
    922 
    923     // With justification, we use the justified amounts instead. The
    924     // justification array contains both the advance and the extra space
    925     // added for justification, so is the width we want.
    926     int justification = 0;
    927     for (size_t i = 0; i < shaping.m_justify.size(); i++)
    928         justification += shaping.m_justify[i];
    929 
    930     return shaping.m_prePadding + justification;
    931 }
    932 
    933 }  // namespace WebCore
    934