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