| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <windows.h> | |
| 6 | |
| 7 #include "base/gfx/uniscribe.h" | |
| 8 | |
| 9 #include "base/gfx/font_utils.h" | |
| 10 #include "base/logging.h" | |
| 11 | |
| 12 namespace gfx { | |
| 13 | |
| 14 // This function is used to see where word spacing should be applied inside | |
| 15 // runs. Note that this must match Font::treatAsSpace so we all agree where | |
| 16 // and how much space this is, so we don't want to do more general Unicode | |
| 17 // "is this a word break" thing. | |
| 18 static bool TreatAsSpace(wchar_t c) { | |
| 19 return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; | |
| 20 } | |
| 21 | |
| 22 // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid | |
| 23 // and blank glyphs. Just because ScriptShape succeeds does not mean | |
| 24 // that a text run is rendered correctly. Some characters may be rendered | |
| 25 // with default/invalid/blank glyphs. Therefore, we need to check if the glyph | |
| 26 // array returned by ScriptShape contains any of those glyphs to make | |
| 27 // sure that the text run is rendered successfully. | |
| 28 static bool ContainsMissingGlyphs(WORD *glyphs, | |
| 29 int length, | |
| 30 SCRIPT_FONTPROPERTIES* properties) { | |
| 31 for (int i = 0; i < length; ++i) { | |
| 32 if (glyphs[i] == properties->wgDefault || | |
| 33 (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank)
) | |
| 34 return true; | |
| 35 } | |
| 36 | |
| 37 return false; | |
| 38 } | |
| 39 | |
| 40 // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque | |
| 41 // handle and we can't directly query it to make a new HFONT sharing | |
| 42 // its characteristics (height, style, etc) except for family name. | |
| 43 // This function uses GetObject to convert HFONT back to LOGFONT, | |
| 44 // resets the fields of LOGFONT and calculates style to use later | |
| 45 // for the creation of a font identical to HFONT other than family name. | |
| 46 static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) { | |
| 47 DCHECK(hfont && logfont); | |
| 48 if (!hfont || !logfont) | |
| 49 return; | |
| 50 | |
| 51 GetObject(hfont, sizeof(LOGFONT), logfont); | |
| 52 // We reset these fields to values appropriate for CreateFontIndirect. | |
| 53 // while keeping lfHeight, which is the most important value in creating | |
| 54 // a new font similar to hfont. | |
| 55 logfont->lfWidth = 0; | |
| 56 logfont->lfEscapement = 0; | |
| 57 logfont->lfOrientation = 0; | |
| 58 logfont->lfCharSet = DEFAULT_CHARSET; | |
| 59 logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; | |
| 60 logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings. | |
| 61 logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; | |
| 62 if (style) | |
| 63 *style = gfx::GetStyleFromLogfont(logfont); | |
| 64 } | |
| 65 | |
| 66 UniscribeState::UniscribeState(const wchar_t* input, | |
| 67 int input_length, | |
| 68 bool is_rtl, | |
| 69 HFONT hfont, | |
| 70 SCRIPT_CACHE* script_cache, | |
| 71 SCRIPT_FONTPROPERTIES* font_properties) | |
| 72 : input_(input), | |
| 73 input_length_(input_length), | |
| 74 is_rtl_(is_rtl), | |
| 75 hfont_(hfont), | |
| 76 script_cache_(script_cache), | |
| 77 font_properties_(font_properties), | |
| 78 directional_override_(false), | |
| 79 inhibit_ligate_(false), | |
| 80 letter_spacing_(0), | |
| 81 space_width_(0), | |
| 82 word_spacing_(0), | |
| 83 ascent_(0) { | |
| 84 logfont_.lfFaceName[0] = 0; | |
| 85 } | |
| 86 | |
| 87 UniscribeState::~UniscribeState() { | |
| 88 } | |
| 89 | |
| 90 void UniscribeState::InitWithOptionalLengthProtection(bool length_protection) { | |
| 91 // We cap the input length and just don't do anything. We'll allocate a lot | |
| 92 // of things of the size of the number of characters, so the allocated memory | |
| 93 // will be several times the input length. Plus shaping such a large buffer | |
| 94 // may be a form of denial of service. No legitimate text should be this long. | |
| 95 // It also appears that Uniscribe flatly rejects very long strings, so we | |
| 96 // don't lose anything by doing this. | |
| 97 // | |
| 98 // The input length protection may be disabled by the unit tests to cause | |
| 99 // an error condition. | |
| 100 static const int kMaxInputLength = 65535; | |
| 101 if (input_length_ == 0 || | |
| 102 (length_protection && input_length_ > kMaxInputLength)) | |
| 103 return; | |
| 104 | |
| 105 FillRuns(); | |
| 106 FillShapes(); | |
| 107 FillScreenOrder(); | |
| 108 } | |
| 109 | |
| 110 int UniscribeState::Width() const { | |
| 111 int width = 0; | |
| 112 for (int item_index = 0; item_index < static_cast<int>(runs_->size()); | |
| 113 item_index++) { | |
| 114 width += AdvanceForItem(item_index); | |
| 115 } | |
| 116 return width; | |
| 117 } | |
| 118 | |
| 119 void UniscribeState::Justify(int additional_space) { | |
| 120 // Count the total number of glyphs we have so we know how big to make the | |
| 121 // buffers below. | |
| 122 int total_glyphs = 0; | |
| 123 for (size_t run = 0; run < runs_->size(); run++) { | |
| 124 int run_idx = screen_order_[run]; | |
| 125 total_glyphs += static_cast<int>(shapes_[run_idx].glyph_length()); | |
| 126 } | |
| 127 if (total_glyphs == 0) | |
| 128 return; // Nothing to do. | |
| 129 | |
| 130 // We make one big buffer in screen order of all the glyphs we are drawing | |
| 131 // across runs so that the justification function will adjust evenly across | |
| 132 // all glyphs. | |
| 133 StackVector<SCRIPT_VISATTR, 64> visattr; | |
| 134 visattr->resize(total_glyphs); | |
| 135 StackVector<int, 64> advances; | |
| 136 advances->resize(total_glyphs); | |
| 137 StackVector<int, 64> justify; | |
| 138 justify->resize(total_glyphs); | |
| 139 | |
| 140 // Build the packed input. | |
| 141 int dest_index = 0; | |
| 142 for (size_t run = 0; run < runs_->size(); run++) { | |
| 143 int run_idx = screen_order_[run]; | |
| 144 const Shaping& shaping = shapes_[run_idx]; | |
| 145 | |
| 146 for (int i = 0; i < shaping.glyph_length(); i++, dest_index++) { | |
| 147 memcpy(&visattr[dest_index], &shaping.visattr[i], sizeof(SCRIPT_VISATTR)); | |
| 148 advances[dest_index] = shaping.advance[i]; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 // The documentation for ScriptJustify is wrong, the parameter is the space | |
| 153 // to add and not the width of the column you want. | |
| 154 const int min_kashida = 1; // How do we decide what this should be? | |
| 155 ScriptJustify(&visattr[0], &advances[0], total_glyphs, additional_space, | |
| 156 min_kashida, &justify[0]); | |
| 157 | |
| 158 // Now we have to unpack the justification amounts back into the runs so | |
| 159 // the glyph indices match. | |
| 160 int global_glyph_index = 0; | |
| 161 for (size_t run = 0; run < runs_->size(); run++) { | |
| 162 int run_idx = screen_order_[run]; | |
| 163 Shaping& shaping = shapes_[run_idx]; | |
| 164 | |
| 165 shaping.justify->resize(shaping.glyph_length()); | |
| 166 for (int i = 0; i < shaping.glyph_length(); i++, global_glyph_index++) | |
| 167 shaping.justify[i] = justify[global_glyph_index]; | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 int UniscribeState::CharacterToX(int offset) const { | |
| 172 HRESULT hr; | |
| 173 DCHECK(offset <= input_length_); | |
| 174 | |
| 175 // Our algorithm is to traverse the items in screen order from left to | |
| 176 // right, adding in each item's screen width until we find the item with | |
| 177 // the requested character in it. | |
| 178 int width = 0; | |
| 179 for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) { | |
| 180 // Compute the length of this run. | |
| 181 int item_idx = screen_order_[screen_idx]; | |
| 182 const SCRIPT_ITEM& item = runs_[item_idx]; | |
| 183 const Shaping& shaping = shapes_[item_idx]; | |
| 184 int item_length = shaping.char_length(); | |
| 185 | |
| 186 if (offset >= item.iCharPos && offset <= item.iCharPos + item_length) { | |
| 187 // Character offset is in this run. | |
| 188 int char_len = offset - item.iCharPos; | |
| 189 | |
| 190 int cur_x = 0; | |
| 191 hr = ScriptCPtoX(char_len, FALSE, item_length, shaping.glyph_length(), | |
| 192 &shaping.logs[0], &shaping.visattr[0], | |
| 193 shaping.effective_advances(), &item.a, &cur_x); | |
| 194 if (FAILED(hr)) | |
| 195 return 0; | |
| 196 | |
| 197 width += cur_x + shaping.pre_padding; | |
| 198 DCHECK(width >= 0); | |
| 199 return width; | |
| 200 } | |
| 201 | |
| 202 // Move to the next item. | |
| 203 width += AdvanceForItem(item_idx); | |
| 204 } | |
| 205 DCHECK(width >= 0); | |
| 206 return width; | |
| 207 } | |
| 208 | |
| 209 int UniscribeState::XToCharacter(int x) const { | |
| 210 // We iterate in screen order until we find the item with the given pixel | |
| 211 // position in it. When we find that guy, we ask Uniscribe for the | |
| 212 // character index. | |
| 213 HRESULT hr; | |
| 214 for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) { | |
| 215 int item_idx = screen_order_[screen_idx]; | |
| 216 int advance_for_item = AdvanceForItem(item_idx); | |
| 217 | |
| 218 // Note that the run may be empty if shaping failed, so we want to skip | |
| 219 // over it. | |
| 220 const Shaping& shaping = shapes_[item_idx]; | |
| 221 int item_length = shaping.char_length(); | |
| 222 if (x <= advance_for_item && item_length > 0) { | |
| 223 // The requested offset is within this item. | |
| 224 const SCRIPT_ITEM& item = runs_[item_idx]; | |
| 225 | |
| 226 // Account for the leading space we've added to this run that Uniscribe | |
| 227 // doesn't know about. | |
| 228 x -= shaping.pre_padding; | |
| 229 | |
| 230 int char_x = 0; | |
| 231 int trailing; | |
| 232 hr = ScriptXtoCP(x, item_length, shaping.glyph_length(), | |
| 233 &shaping.logs[0], &shaping.visattr[0], | |
| 234 shaping.effective_advances(), &item.a, &char_x, | |
| 235 &trailing); | |
| 236 | |
| 237 // The character offset is within the item. We need to add the item's | |
| 238 // offset to transform it into the space of the TextRun | |
| 239 return char_x + item.iCharPos; | |
| 240 } | |
| 241 | |
| 242 // The offset is beyond this item, account for its length and move on. | |
| 243 x -= advance_for_item; | |
| 244 } | |
| 245 | |
| 246 // Error condition, we don't know what to do if we don't have that X | |
| 247 // position in any of our items. | |
| 248 return 0; | |
| 249 } | |
| 250 | |
| 251 void UniscribeState::Draw(HDC dc, int x, int y, int from, int to) { | |
| 252 HGDIOBJ old_font = 0; | |
| 253 int cur_x = x; | |
| 254 bool first_run = true; | |
| 255 | |
| 256 for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) { | |
| 257 int item_idx = screen_order_[screen_idx]; | |
| 258 const SCRIPT_ITEM& item = runs_[item_idx]; | |
| 259 const Shaping& shaping = shapes_[item_idx]; | |
| 260 | |
| 261 // Character offsets within this run. THESE MAY NOT BE IN RANGE and may | |
| 262 // be negative, etc. The code below handles this. | |
| 263 int from_char = from - item.iCharPos; | |
| 264 int to_char = to - item.iCharPos; | |
| 265 | |
| 266 // See if we need to draw any characters in this item. | |
| 267 if (shaping.char_length() == 0 || | |
| 268 from_char >= shaping.char_length() || to_char <= 0) { | |
| 269 // No chars in this item to display. | |
| 270 cur_x += AdvanceForItem(item_idx); | |
| 271 continue; | |
| 272 } | |
| 273 | |
| 274 // Compute the starting glyph within this span. |from| and |to| are | |
| 275 // global offsets that may intersect arbitrarily with our local run. | |
| 276 int from_glyph, after_glyph; | |
| 277 if (item.a.fRTL) { | |
| 278 // To compute the first glyph when going RTL, we use |to|. | |
| 279 if (to_char >= shaping.char_length()) { | |
| 280 // The end of the text is after (to the left) of us. | |
| 281 from_glyph = 0; | |
| 282 } else { | |
| 283 // Since |to| is exclusive, the first character we draw on the left | |
| 284 // is actually the one right before (to the right) of |to|. | |
| 285 from_glyph = shaping.logs[to_char - 1]; | |
| 286 } | |
| 287 | |
| 288 // The last glyph is actually the first character in the range. | |
| 289 if (from_char <= 0) { | |
| 290 // The first character to draw is before (to the right) of this span, | |
| 291 // so draw all the way to the end. | |
| 292 after_glyph = shaping.glyph_length(); | |
| 293 } else { | |
| 294 // We want to draw everything up until the character to the right of | |
| 295 // |from|. To the right is - 1, so we look that up (remember our | |
| 296 // character could be more than one glyph, so we can't look up our | |
| 297 // glyph and add one). | |
| 298 after_glyph = shaping.logs[from_char - 1]; | |
| 299 } | |
| 300 } else { | |
| 301 // Easy case, everybody agrees about directions. We only need to handle | |
| 302 // boundary conditions to get a range inclusive at the beginning, and | |
| 303 // exclusive at the ending. We have to do some computation to see the | |
| 304 // glyph one past the end. | |
| 305 from_glyph = shaping.logs[from_char < 0 ? 0 : from_char]; | |
| 306 if (to_char >= shaping.char_length()) | |
| 307 after_glyph = shaping.glyph_length(); | |
| 308 else | |
| 309 after_glyph = shaping.logs[to_char]; | |
| 310 } | |
| 311 | |
| 312 // Account for the characters that were skipped in this run. When | |
| 313 // WebKit asks us to draw a subset of the run, it actually tells us | |
| 314 // to draw at the X offset of the beginning of the run, since it | |
| 315 // doesn't know the internal position of any of our characters. | |
| 316 const int* effective_advances = shaping.effective_advances(); | |
| 317 int inner_offset = 0; | |
| 318 for (int i = 0; i < from_glyph; i++) | |
| 319 inner_offset += effective_advances[i]; | |
| 320 | |
| 321 // Actually draw the glyphs we found. | |
| 322 int glyph_count = after_glyph - from_glyph; | |
| 323 if (from_glyph >= 0 && glyph_count > 0) { | |
| 324 // Account for the preceeding space we need to add to this run. We don't | |
| 325 // need to count for the following space because that will be counted | |
| 326 // in AdvanceForItem below when we move to the next run. | |
| 327 inner_offset += shaping.pre_padding; | |
| 328 | |
| 329 // Pass NULL in when there is no justification. | |
| 330 const int* justify = shaping.justify->empty() ? | |
| 331 NULL : &shaping.justify[from_glyph]; | |
| 332 | |
| 333 if (first_run) { | |
| 334 old_font = SelectObject(dc, shaping.hfont_); | |
| 335 first_run = false; | |
| 336 } else { | |
| 337 SelectObject(dc, shaping.hfont_); | |
| 338 } | |
| 339 | |
| 340 // TODO(brettw) bug 698452: if a half a character is selected, | |
| 341 // we should set up a clip rect so we draw the half of the glyph | |
| 342 // correctly. | |
| 343 // Fonts with different ascents can be used to render different runs. | |
| 344 // 'Across-runs' y-coordinate correction needs to be adjusted | |
| 345 // for each font. | |
| 346 HRESULT hr = S_FALSE; | |
| 347 for (int executions = 0; executions < 2; ++executions) { | |
| 348 hr = ScriptTextOut(dc, shaping.script_cache_, cur_x + inner_offset, | |
| 349 y - shaping.ascent_offset_, 0, NULL, &item.a, NULL, | |
| 350 0, &shaping.glyphs[from_glyph], | |
| 351 glyph_count, &shaping.advance[from_glyph], | |
| 352 justify, &shaping.offsets[from_glyph]); | |
| 353 if (S_OK != hr && 0 == executions) { | |
| 354 // If this ScriptTextOut is called from the renderer it might fail | |
| 355 // because the sandbox is preventing it from opening the font files. | |
| 356 // If we are running in the renderer, TryToPreloadFont is overridden | |
| 357 // to ask the browser to preload the font for us so we can access it. | |
| 358 TryToPreloadFont(shaping.hfont_); | |
| 359 continue; | |
| 360 } | |
| 361 break; | |
| 362 } | |
| 363 | |
| 364 DCHECK(S_OK == hr); | |
| 365 | |
| 366 | |
| 367 } | |
| 368 | |
| 369 cur_x += AdvanceForItem(item_idx); | |
| 370 } | |
| 371 | |
| 372 if (old_font) | |
| 373 SelectObject(dc, old_font); | |
| 374 } | |
| 375 | |
| 376 WORD UniscribeState::FirstGlyphForCharacter(int char_offset) const { | |
| 377 // Find the run for the given character. | |
| 378 for (int i = 0; i < static_cast<int>(runs_->size()); i++) { | |
| 379 int first_char = runs_[i].iCharPos; | |
| 380 const Shaping& shaping = shapes_[i]; | |
| 381 int local_offset = char_offset - first_char; | |
| 382 if (local_offset >= 0 && local_offset < shaping.char_length()) { | |
| 383 // The character is in this run, return the first glyph for it (should | |
| 384 // generally be the only glyph). It seems Uniscribe gives glyph 0 for | |
| 385 // empty, which is what we want to return in the "missing" case. | |
| 386 size_t glyph_index = shaping.logs[local_offset]; | |
| 387 if (glyph_index >= shaping.glyphs->size()) { | |
| 388 // The glyph should be in this run, but the run has too few actual | |
| 389 // characters. This can happen when shaping the run fails, in which | |
| 390 // case, we should have no data in the logs at all. | |
| 391 DCHECK(shaping.glyphs->empty()); | |
| 392 return 0; | |
| 393 } | |
| 394 return shaping.glyphs[glyph_index]; | |
| 395 } | |
| 396 } | |
| 397 return 0; | |
| 398 } | |
| 399 | |
| 400 void UniscribeState::FillRuns() { | |
| 401 HRESULT hr; | |
| 402 runs_->resize(UNISCRIBE_STATE_STACK_RUNS); | |
| 403 | |
| 404 SCRIPT_STATE input_state; | |
| 405 input_state.uBidiLevel = is_rtl_; | |
| 406 input_state.fOverrideDirection = directional_override_; | |
| 407 input_state.fInhibitSymSwap = false; | |
| 408 input_state.fCharShape = false; // Not implemented in Uniscribe | |
| 409 input_state.fDigitSubstitute = false; // Do we want this for Arabic? | |
| 410 input_state.fInhibitLigate = inhibit_ligate_; | |
| 411 input_state.fDisplayZWG = false; // Don't draw control characters. | |
| 412 input_state.fArabicNumContext = is_rtl_; // Do we want this for Arabic? | |
| 413 input_state.fGcpClusters = false; | |
| 414 input_state.fReserved = 0; | |
| 415 input_state.fEngineReserved = 0; | |
| 416 // The psControl argument to ScriptItemize should be non-NULL for RTL text, | |
| 417 // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a | |
| 418 // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the | |
| 419 // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx . | |
| 420 static SCRIPT_CONTROL input_control = {0, // uDefaultLanguage :16; | |
| 421 0, // fContextDigits :1; | |
| 422 0, // fInvertPreBoundDir :1; | |
| 423 0, // fInvertPostBoundDir :1; | |
| 424 0, // fLinkStringBefore :1; | |
| 425 0, // fLinkStringAfter :1; | |
| 426 0, // fNeutralOverride :1; | |
| 427 0, // fNumericOverride :1; | |
| 428 0, // fLegacyBidiClass :1; | |
| 429 0, // fMergeNeutralItems :1; | |
| 430 0};// fReserved :7; | |
| 431 // Calling ScriptApplyDigitSubstitution( NULL, &input_control, &input_state) | |
| 432 // here would be appropriate if we wanted to set the language ID, and get | |
| 433 // local digit substitution behavior. For now, don't do it. | |
| 434 | |
| 435 while (true) { | |
| 436 int num_items = 0; | |
| 437 | |
| 438 // Ideally, we would have a way to know the runs before and after this | |
| 439 // one, and put them into the control parameter of ScriptItemize. This | |
| 440 // would allow us to shape characters properly that cross style | |
| 441 // boundaries (WebKit bug 6148). | |
| 442 // | |
| 443 // We tell ScriptItemize that the output list of items is one smaller | |
| 444 // than it actually is. According to Mozilla bug 366643, if there is | |
| 445 // not enough room in the array on pre-SP2 systems, ScriptItemize will | |
| 446 // write one past the end of the buffer. | |
| 447 // | |
| 448 // ScriptItemize is very strange. It will often require a much larger | |
| 449 // ITEM buffer internally than it will give us as output. For example, | |
| 450 // it will say a 16-item buffer is not big enough, and will write | |
| 451 // interesting numbers into all those items. But when we give it a 32 | |
| 452 // item buffer and it succeeds, it only has one item output. | |
| 453 // | |
| 454 // It seems to be doing at least two passes, the first where it puts a | |
| 455 // lot of intermediate data into our items, and the second where it | |
| 456 // collates them. | |
| 457 hr = ScriptItemize(input_, input_length_, | |
| 458 static_cast<int>(runs_->size()) - 1, &input_control, &inp
ut_state, | |
| 459 &runs_[0], &num_items); | |
| 460 if (SUCCEEDED(hr)) { | |
| 461 runs_->resize(num_items); | |
| 462 break; | |
| 463 } | |
| 464 if (hr != E_OUTOFMEMORY) { | |
| 465 // Some kind of unexpected error. | |
| 466 runs_->resize(0); | |
| 467 break; | |
| 468 } | |
| 469 // There was not enough items for it to write into, expand. | |
| 470 runs_->resize(runs_->size() * 2); | |
| 471 } | |
| 472 | |
| 473 // Fix up the directions of the items so they're what WebKit thinks | |
| 474 // they are. WebKit (and we assume any other caller) always knows what | |
| 475 // direction it wants things to be in, and will only give us runs that are in | |
| 476 // the same direction. Sometimes, Uniscibe disagrees, for example, if you | |
| 477 // have embedded ASCII punctuation in an Arabic string, WebKit will | |
| 478 // (correctly) know that is should still be rendered RTL, but Uniscibe might | |
| 479 // think LTR is better. | |
| 480 // | |
| 481 // TODO(brettw) bug 747235: | |
| 482 // This workaround fixes the bug but causes spacing problems in other cases. | |
| 483 // WebKit sometimes gives us a big run that includes ASCII and Arabic, and | |
| 484 // this forcing direction makes those cases incorrect. This seems to happen | |
| 485 // during layout only, so it ends up that spacing is incorrect (because being | |
| 486 // the wrong direction changes ligatures and stuff). | |
| 487 // | |
| 488 //for (size_t i = 0; i < runs_->size(); i++) | |
| 489 // runs_[i].a.fRTL = is_rtl_; | |
| 490 } | |
| 491 | |
| 492 | |
| 493 bool UniscribeState::Shape(const wchar_t* input, | |
| 494 int item_length, | |
| 495 int num_glyphs, | |
| 496 SCRIPT_ITEM& run, | |
| 497 Shaping& shaping) { | |
| 498 HFONT hfont = hfont_; | |
| 499 SCRIPT_CACHE* script_cache = script_cache_; | |
| 500 SCRIPT_FONTPROPERTIES* font_properties = font_properties_; | |
| 501 int ascent = ascent_; | |
| 502 HDC temp_dc = NULL; | |
| 503 HGDIOBJ old_font = 0; | |
| 504 HRESULT hr; | |
| 505 bool lastFallbackTried = false; | |
| 506 bool result; | |
| 507 | |
| 508 int generated_glyphs = 0; | |
| 509 | |
| 510 // In case HFONT passed in ctor cannot render this run, we have to scan | |
| 511 // other fonts from the beginning of the font list. | |
| 512 ResetFontIndex(); | |
| 513 | |
| 514 // Compute shapes. | |
| 515 while (true) { | |
| 516 shaping.logs->resize(item_length); | |
| 517 shaping.glyphs->resize(num_glyphs); | |
| 518 shaping.visattr->resize(num_glyphs); | |
| 519 | |
| 520 // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true | |
| 521 // here. Is that what we want? It will display control characters. | |
| 522 hr = ScriptShape(temp_dc, script_cache, input, item_length, | |
| 523 num_glyphs, &run.a, | |
| 524 &shaping.glyphs[0], &shaping.logs[0], | |
| 525 &shaping.visattr[0], &generated_glyphs); | |
| 526 if (hr == E_PENDING) { | |
| 527 // Allocate the DC. | |
| 528 temp_dc = GetDC(NULL); | |
| 529 old_font = SelectObject(temp_dc, hfont); | |
| 530 continue; | |
| 531 } else if (hr == E_OUTOFMEMORY) { | |
| 532 num_glyphs *= 2; | |
| 533 continue; | |
| 534 } else if (SUCCEEDED(hr) && | |
| 535 (lastFallbackTried || !ContainsMissingGlyphs(&shaping.glyphs[0], | |
| 536 generated_glyphs, font_properties))) { | |
| 537 break; | |
| 538 } | |
| 539 | |
| 540 // The current font can't render this run. clear DC and try | |
| 541 // next font. | |
| 542 if (temp_dc) { | |
| 543 SelectObject(temp_dc, old_font); | |
| 544 ReleaseDC(NULL, temp_dc); | |
| 545 temp_dc = NULL; | |
| 546 } | |
| 547 | |
| 548 if (NextWinFontData(&hfont, &script_cache, &font_properties, &ascent)) { | |
| 549 // The primary font does not support this run. Try next font. | |
| 550 // In case of web page rendering, they come from fonts specified in | |
| 551 // CSS stylesheets. | |
| 552 continue; | |
| 553 } else if (!lastFallbackTried) { | |
| 554 lastFallbackTried = true; | |
| 555 | |
| 556 // Generate a last fallback font based on the script of | |
| 557 // a character to draw while inheriting size and styles | |
| 558 // from the primary font | |
| 559 if (!logfont_.lfFaceName[0]) | |
| 560 SetLogFontAndStyle(hfont_, &logfont_, &style_); | |
| 561 | |
| 562 // TODO(jungshik): generic type should come from webkit for | |
| 563 // UniscribeStateTextRun (a derived class used in webkit). | |
| 564 const wchar_t *family = GetFallbackFamily(input, item_length, | |
| 565 GENERIC_FAMILY_STANDARD, NULL, NULL); | |
| 566 bool font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfo
nt, &script_cache); | |
| 567 | |
| 568 if (!font_ok) { | |
| 569 // If this GetDerivedFontData is called from the renderer it might fail | |
| 570 // because the sandbox is preventing it from opening the font files. | |
| 571 // If we are running in the renderer, TryToPreloadFont is overridden to | |
| 572 // ask the browser to preload the font for us so we can access it. | |
| 573 TryToPreloadFont(hfont); | |
| 574 | |
| 575 // Try again. | |
| 576 font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont,
&script_cache); | |
| 577 DCHECK(font_ok); | |
| 578 } | |
| 579 | |
| 580 // TODO(jungshik) : Currently GetDerivedHFont always returns a | |
| 581 // a valid HFONT, but in the future, I may change it to return 0. | |
| 582 DCHECK(hfont); | |
| 583 | |
| 584 // We don't need a font_properties for the last resort fallback font | |
| 585 // because we don't have anything more to try and are forced to | |
| 586 // accept empty glyph boxes. If we tried a series of fonts as | |
| 587 // 'last-resort fallback', we'd need it, but currently, we don't. | |
| 588 continue; | |
| 589 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { | |
| 590 run.a.eScript = SCRIPT_UNDEFINED; | |
| 591 continue; | |
| 592 } else if (FAILED(hr)) { | |
| 593 // Error shaping. | |
| 594 generated_glyphs = 0; | |
| 595 result = false; | |
| 596 goto cleanup; | |
| 597 } | |
| 598 } | |
| 599 | |
| 600 // Sets Windows font data for this run to those corresponding to | |
| 601 // a font supporting this run. we don't need to store font_properties | |
| 602 // because it's not used elsewhere. | |
| 603 shaping.hfont_ = hfont; | |
| 604 shaping.script_cache_ = script_cache; | |
| 605 | |
| 606 // The ascent of a font for this run can be different from | |
| 607 // that of the primary font so that we need to keep track of | |
| 608 // the difference per run and take that into account when calling | |
| 609 // ScriptTextOut in |Draw|. Otherwise, different runs rendered by | |
| 610 // different fonts would not be aligned vertically. | |
| 611 shaping.ascent_offset_ = ascent_ ? ascent - ascent_ : 0; | |
| 612 result = true; | |
| 613 | |
| 614 cleanup: | |
| 615 shaping.glyphs->resize(generated_glyphs); | |
| 616 shaping.visattr->resize(generated_glyphs); | |
| 617 shaping.advance->resize(generated_glyphs); | |
| 618 shaping.offsets->resize(generated_glyphs); | |
| 619 if (temp_dc) { | |
| 620 SelectObject(temp_dc, old_font); | |
| 621 ReleaseDC(NULL, temp_dc); | |
| 622 } | |
| 623 // On failure, our logs don't mean anything, so zero those out. | |
| 624 if (!result) | |
| 625 shaping.logs->clear(); | |
| 626 | |
| 627 return result; | |
| 628 } | |
| 629 | |
| 630 void UniscribeState::FillShapes() { | |
| 631 shapes_->resize(runs_->size()); | |
| 632 for (size_t i = 0; i < runs_->size(); i++) { | |
| 633 int start_item = runs_[i].iCharPos; | |
| 634 int item_length = input_length_ - start_item; | |
| 635 if (i < runs_->size() - 1) | |
| 636 item_length = runs_[i + 1].iCharPos - start_item; | |
| 637 | |
| 638 int num_glyphs; | |
| 639 if (item_length < UNISCRIBE_STATE_STACK_CHARS) { | |
| 640 // We'll start our buffer sizes with the current stack space available | |
| 641 // in our buffers if the current input fits. As long as it | |
| 642 // doesn't expand past that we'll save a lot of time mallocing. | |
| 643 num_glyphs = UNISCRIBE_STATE_STACK_CHARS; | |
| 644 } else { | |
| 645 // When the input doesn't fit, give up with the stack since it will | |
| 646 // almost surely not be enough room (unless the input actually shrinks, | |
| 647 // which is unlikely) and just start with the length recommended by | |
| 648 // the Uniscribe documentation as a "usually fits" size. | |
| 649 num_glyphs = item_length * 3 / 2 + 16; | |
| 650 } | |
| 651 | |
| 652 // Convert a string to a glyph string trying the primary font, | |
| 653 // fonts in the fallback list and then script-specific last resort font. | |
| 654 Shaping& shaping = shapes_[i]; | |
| 655 if (!Shape(&input_[start_item], item_length, num_glyphs, runs_[i], shaping)) | |
| 656 continue; | |
| 657 | |
| 658 // Compute placements. Note that offsets is documented incorrectly | |
| 659 // and is actually an array. | |
| 660 | |
| 661 // DC that we lazily create if Uniscribe commands us to. | |
| 662 // (this does not happen often because script_cache is already | |
| 663 // updated when calling ScriptShape). | |
| 664 HDC temp_dc = NULL; | |
| 665 HGDIOBJ old_font = NULL; | |
| 666 HRESULT hr; | |
| 667 while (true) { | |
| 668 shaping.pre_padding = 0; | |
| 669 hr = ScriptPlace(temp_dc, shaping.script_cache_, &shaping.glyphs[0], | |
| 670 static_cast<int>(shaping.glyphs->size()), | |
| 671 &shaping.visattr[0], &runs_[i].a, | |
| 672 &shaping.advance[0], &shaping.offsets[0], | |
| 673 &shaping.abc); | |
| 674 if (hr != E_PENDING) | |
| 675 break; | |
| 676 | |
| 677 // Allocate the DC and run the loop again. | |
| 678 temp_dc = GetDC(NULL); | |
| 679 old_font = SelectObject(temp_dc, shaping.hfont_); | |
| 680 } | |
| 681 | |
| 682 if (FAILED(hr)) { | |
| 683 // Some error we don't know how to handle. Nuke all of our data | |
| 684 // since we can't deal with partially valid data later. | |
| 685 runs_->clear(); | |
| 686 shapes_->clear(); | |
| 687 screen_order_->clear(); | |
| 688 } | |
| 689 | |
| 690 if (temp_dc) { | |
| 691 SelectObject(temp_dc, old_font); | |
| 692 ReleaseDC(NULL, temp_dc); | |
| 693 } | |
| 694 } | |
| 695 | |
| 696 AdjustSpaceAdvances(); | |
| 697 | |
| 698 if (letter_spacing_ != 0 || word_spacing_ != 0) | |
| 699 ApplySpacing(); | |
| 700 } | |
| 701 | |
| 702 void UniscribeState::FillScreenOrder() { | |
| 703 screen_order_->resize(runs_->size()); | |
| 704 | |
| 705 // We assume that the input has only one text direction in it. | |
| 706 // TODO(brettw) are we sure we want to keep this restriction? | |
| 707 if (is_rtl_) { | |
| 708 for (int i = 0; i < static_cast<int>(screen_order_->size()); i++) | |
| 709 screen_order_[static_cast<int>(screen_order_->size()) - i - 1] = i; | |
| 710 } else { | |
| 711 for (int i = 0; i < static_cast<int>(screen_order_->size()); i++) | |
| 712 screen_order_[i] = i; | |
| 713 } | |
| 714 } | |
| 715 | |
| 716 void UniscribeState::AdjustSpaceAdvances() { | |
| 717 if (space_width_ == 0) | |
| 718 return; | |
| 719 | |
| 720 int space_width_without_letter_spacing = space_width_ - letter_spacing_; | |
| 721 | |
| 722 // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem. | |
| 723 for (size_t run = 0; run < runs_->size(); run++) { | |
| 724 Shaping& shaping = shapes_[run]; | |
| 725 | |
| 726 for (int i = 0; i < shaping.char_length(); i++) { | |
| 727 if (!TreatAsSpace(input_[runs_[run].iCharPos + i])) | |
| 728 continue; | |
| 729 | |
| 730 int glyph_index = shaping.logs[i]; | |
| 731 int current_advance = shaping.advance[glyph_index]; | |
| 732 // Don't give zero-width spaces a width. | |
| 733 if (!current_advance) | |
| 734 continue; | |
| 735 | |
| 736 // current_advance does not include additional letter-spacing, but | |
| 737 // space_width does. Here we find out how off we are from the correct | |
| 738 // width for the space not including letter-spacing, then just subtract | |
| 739 // that diff. | |
| 740 int diff = current_advance - space_width_without_letter_spacing; | |
| 741 // The shaping can consist of a run of text, so only subtract the | |
| 742 // difference in the width of the glyph. | |
| 743 shaping.advance[glyph_index] -= diff; | |
| 744 shaping.abc.abcB -= diff; | |
| 745 } | |
| 746 } | |
| 747 } | |
| 748 | |
| 749 void UniscribeState::ApplySpacing() { | |
| 750 for (size_t run = 0; run < runs_->size(); run++) { | |
| 751 Shaping& shaping = shapes_[run]; | |
| 752 bool is_rtl = runs_[run].a.fRTL; | |
| 753 | |
| 754 if (letter_spacing_ != 0) { | |
| 755 // RTL text gets padded to the left of each character. We increment the | |
| 756 // run's advance to make this happen. This will be balanced out by NOT | |
| 757 // adding additional advance to the last glyph in the run. | |
| 758 if (is_rtl) | |
| 759 shaping.pre_padding += letter_spacing_; | |
| 760 | |
| 761 // Go through all the glyphs in this run and increase the "advance" to | |
| 762 // account for letter spacing. We adjust letter spacing only on cluster | |
| 763 // boundaries. | |
| 764 // | |
| 765 // This works for most scripts, but may have problems with some indic | |
| 766 // scripts. This behavior is better than Firefox or IE for Hebrew. | |
| 767 for (int i = 0; i < shaping.glyph_length(); i++) { | |
| 768 if (shaping.visattr[i].fClusterStart) { | |
| 769 // Ick, we need to assign the extra space so that the glyph comes | |
| 770 // first, then is followed by the space. This is opposite for RTL. | |
| 771 if (is_rtl) { | |
| 772 if (i != shaping.glyph_length() - 1) { | |
| 773 // All but the last character just get the spacing applied to | |
| 774 // their advance. The last character doesn't get anything, | |
| 775 shaping.advance[i] += letter_spacing_; | |
| 776 shaping.abc.abcB += letter_spacing_; | |
| 777 } | |
| 778 } else { | |
| 779 // LTR case is easier, we just add to the advance. | |
| 780 shaping.advance[i] += letter_spacing_; | |
| 781 shaping.abc.abcB += letter_spacing_; | |
| 782 } | |
| 783 } | |
| 784 } | |
| 785 } | |
| 786 | |
| 787 // Go through all the characters to find whitespace and insert the extra | |
| 788 // wordspacing amount for the glyphs they correspond to. | |
| 789 if (word_spacing_ != 0) { | |
| 790 for (int i = 0; i < shaping.char_length(); i++) { | |
| 791 if (!TreatAsSpace(input_[runs_[run].iCharPos + i])) | |
| 792 continue; | |
| 793 | |
| 794 // The char in question is a word separator... | |
| 795 int glyph_index = shaping.logs[i]; | |
| 796 | |
| 797 // Spaces will not have a glyph in Uniscribe, it will just add | |
| 798 // additional advance to the character to the left of the space. The | |
| 799 // space's corresponding glyph will be the character following it in | |
| 800 // reading order. | |
| 801 if (is_rtl) { | |
| 802 // In RTL, the glyph to the left of the space is the same as the | |
| 803 // first glyph of the following character, so we can just increment | |
| 804 // it. | |
| 805 shaping.advance[glyph_index] += word_spacing_; | |
| 806 shaping.abc.abcB += word_spacing_; | |
| 807 } else { | |
| 808 // LTR is actually more complex here, we apply it to the previous | |
| 809 // character if there is one, otherwise we have to apply it to the | |
| 810 // leading space of the run. | |
| 811 if (glyph_index == 0) { | |
| 812 shaping.pre_padding += word_spacing_; | |
| 813 } else { | |
| 814 shaping.advance[glyph_index - 1] += word_spacing_; | |
| 815 shaping.abc.abcB += word_spacing_; | |
| 816 } | |
| 817 } | |
| 818 } | |
| 819 } // word_spacing_ != 0 | |
| 820 | |
| 821 // Loop for next run... | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 // The advance is the ABC width of the run | |
| 826 int UniscribeState::AdvanceForItem(int item_index) const { | |
| 827 int accum = 0; | |
| 828 const Shaping& shaping = shapes_[item_index]; | |
| 829 | |
| 830 if (shaping.justify->empty()) { | |
| 831 // Easy case with no justification, the width is just the ABC width of
t | |
| 832 // the run. (The ABC width is the sum of the advances). | |
| 833 return shaping.abc.abcA + shaping.abc.abcB + shaping.abc.abcC + | |
| 834 shaping.pre_padding; | |
| 835 } | |
| 836 | |
| 837 // With justification, we use the justified amounts instead. The | |
| 838 // justification array contains both the advance and the extra space | |
| 839 // added for justification, so is the width we want. | |
| 840 int justification = 0; | |
| 841 for (size_t i = 0; i < shaping.justify->size(); i++) | |
| 842 justification += shaping.justify[i]; | |
| 843 | |
| 844 return shaping.pre_padding + justification; | |
| 845 } | |
| 846 | |
| 847 } // namespace gfx | |
| 848 | |
| OLD | NEW |