• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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