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