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 |