Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: base/gfx/uniscribe.cc

Issue 10785: Debase our Uniscribe code. This moves FontUtils and all our Uniscribe code fr... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698