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