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