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