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
42namespace 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.
50static 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
71UniscribeHelper::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
97UniscribeHelper::~UniscribeHelper()
98{
99}
100
101void 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
121int 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
129void 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
183int 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
223int 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
266void 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
408WORD 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
436void 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
512bool 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
679void 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
761void 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
776void 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
824void 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
905int 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
935bool 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