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