2009-01-15 Dimitri Glazkov <dglazkov@chromium.org>
authordglazkov@chromium.org <dglazkov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 15 Jan 2009 20:52:05 +0000 (20:52 +0000)
committerdglazkov@chromium.org <dglazkov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 15 Jan 2009 20:52:05 +0000 (20:52 +0000)
        Reviewed by Eric Seidel.

        https://bugs.webkit.org/show_bug.cgi?id=23340
        Add remaining bits of graphics/chromium.

        * platform/graphics/chromium/SimpleFontDataChromiumWin.cpp: Added.
        (WebCore::scaleEmToUnits):
        (WebCore::SimpleFontData::platformInit):
        (WebCore::SimpleFontData::platformDestroy):
        (WebCore::SimpleFontData::smallCapsFontData):
        (WebCore::SimpleFontData::containsCharacters):
        (WebCore::SimpleFontData::determinePitch):
        (WebCore::SimpleFontData::platformWidthForGlyph):
        * platform/graphics/chromium/SimpleFontDataLinux.cpp: Added.
        (WebCore::SimpleFontData::platformInit):
        (WebCore::SimpleFontData::platformDestroy):
        (WebCore::SimpleFontData::smallCapsFontData):
        (WebCore::SimpleFontData::containsCharacters):
        (WebCore::SimpleFontData::determinePitch):
        (WebCore::SimpleFontData::platformWidthForGlyph):
        * platform/graphics/chromium/ThemeHelperChromiumWin.cpp: Added.
        (WebCore::ThemeHelperWin::ThemeHelperWin):
        (WebCore::ThemeHelperWin::~ThemeHelperWin):
        * platform/graphics/chromium/ThemeHelperChromiumWin.h: Added.
        (WebCore::ThemeHelperWin::):
        (WebCore::ThemeHelperWin::context):
        (WebCore::ThemeHelperWin::rect):
        * platform/graphics/chromium/UniscribeHelper.cpp: Added.
        (WebCore::treatAsSpace):
        (WebCore::containsMissingGlyphs):
        (WebCore::setLogFontAndStyle):
        (WebCore::UniscribeHelper::UniscribeHelper):
        (WebCore::UniscribeHelper::~UniscribeHelper):
        (WebCore::UniscribeHelper::initWithOptionalLengthProtection):
        (WebCore::UniscribeHelper::width):
        (WebCore::UniscribeHelper::justify):
        (WebCore::UniscribeHelper::characterToX):
        (WebCore::UniscribeHelper::xToCharacter):
        (WebCore::UniscribeHelper::draw):
        (WebCore::UniscribeHelper::firstGlyphForCharacter):
        (WebCore::UniscribeHelper::fillRuns):
        (WebCore::UniscribeHelper::shape):
        (WebCore::UniscribeHelper::fillShapes):
        (WebCore::UniscribeHelper::fillScreenOrder):
        (WebCore::UniscribeHelper::adjustSpaceAdvances):
        (WebCore::UniscribeHelper::applySpacing):
        (WebCore::UniscribeHelper::advanceForItem):
        * platform/graphics/chromium/UniscribeHelper.h: Added.
        (WebCore::UniscribeHelper::directionalOverride):
        (WebCore::UniscribeHelper::setDirectionalOverride):
        (WebCore::UniscribeHelper::inhibitLigate):
        (WebCore::UniscribeHelper::setInhibitLigate):
        (WebCore::UniscribeHelper::letterSpacing):
        (WebCore::UniscribeHelper::setLetterSpacing):
        (WebCore::UniscribeHelper::spaceWidth):
        (WebCore::UniscribeHelper::setSpaceWidth):
        (WebCore::UniscribeHelper::wordSpacing):
        (WebCore::UniscribeHelper::setWordSpacing):
        (WebCore::UniscribeHelper::setAscent):
        (WebCore::UniscribeHelper::init):
        (WebCore::UniscribeHelper::tryToPreloadFont):
        (WebCore::UniscribeHelper::Shaping::Shaping):
        (WebCore::UniscribeHelper::Shaping::glyphLength):
        (WebCore::UniscribeHelper::Shaping::charLength):
        (WebCore::UniscribeHelper::Shaping::effectiveAdvances):
        (WebCore::UniscribeHelper::nextWinFontData):
        (WebCore::UniscribeHelper::resetFontIndex):
        * platform/graphics/chromium/UniscribeHelperTextRun.cpp: Added.
        (WebCore::UniscribeHelperTextRun::UniscribeHelperTextRun):
        (WebCore::UniscribeHelperTextRun::tryToPreloadFont):
        (WebCore::UniscribeHelperTextRun::nextWinFontData):
        (WebCore::UniscribeHelperTextRun::resetFontIndex):
        * platform/graphics/chromium/UniscribeHelperTextRun.h: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@39941 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp [new file with mode: 0644]
WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp [new file with mode: 0644]
WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp [new file with mode: 0644]
WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h [new file with mode: 0644]
WebCore/platform/graphics/chromium/UniscribeHelper.cpp [new file with mode: 0644]
WebCore/platform/graphics/chromium/UniscribeHelper.h [new file with mode: 0644]
WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp [new file with mode: 0644]
WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h [new file with mode: 0644]

index 4cbe7a81abd768e66fbb31c6f12f5cd9a0de3514..1ba846c2f530e3c45f9b001e34d5471692582643 100644 (file)
@@ -1,3 +1,79 @@
+2009-01-15  Dimitri Glazkov  <dglazkov@chromium.org>
+
+        Reviewed by Eric Seidel.
+
+        https://bugs.webkit.org/show_bug.cgi?id=23340
+        Add remaining bits of graphics/chromium.
+
+        * platform/graphics/chromium/SimpleFontDataChromiumWin.cpp: Added.
+        (WebCore::scaleEmToUnits):
+        (WebCore::SimpleFontData::platformInit):
+        (WebCore::SimpleFontData::platformDestroy):
+        (WebCore::SimpleFontData::smallCapsFontData):
+        (WebCore::SimpleFontData::containsCharacters):
+        (WebCore::SimpleFontData::determinePitch):
+        (WebCore::SimpleFontData::platformWidthForGlyph):
+        * platform/graphics/chromium/SimpleFontDataLinux.cpp: Added.
+        (WebCore::SimpleFontData::platformInit):
+        (WebCore::SimpleFontData::platformDestroy):
+        (WebCore::SimpleFontData::smallCapsFontData):
+        (WebCore::SimpleFontData::containsCharacters):
+        (WebCore::SimpleFontData::determinePitch):
+        (WebCore::SimpleFontData::platformWidthForGlyph):
+        * platform/graphics/chromium/ThemeHelperChromiumWin.cpp: Added.
+        (WebCore::ThemeHelperWin::ThemeHelperWin):
+        (WebCore::ThemeHelperWin::~ThemeHelperWin):
+        * platform/graphics/chromium/ThemeHelperChromiumWin.h: Added.
+        (WebCore::ThemeHelperWin::):
+        (WebCore::ThemeHelperWin::context):
+        (WebCore::ThemeHelperWin::rect):
+        * platform/graphics/chromium/UniscribeHelper.cpp: Added.
+        (WebCore::treatAsSpace):
+        (WebCore::containsMissingGlyphs):
+        (WebCore::setLogFontAndStyle):
+        (WebCore::UniscribeHelper::UniscribeHelper):
+        (WebCore::UniscribeHelper::~UniscribeHelper):
+        (WebCore::UniscribeHelper::initWithOptionalLengthProtection):
+        (WebCore::UniscribeHelper::width):
+        (WebCore::UniscribeHelper::justify):
+        (WebCore::UniscribeHelper::characterToX):
+        (WebCore::UniscribeHelper::xToCharacter):
+        (WebCore::UniscribeHelper::draw):
+        (WebCore::UniscribeHelper::firstGlyphForCharacter):
+        (WebCore::UniscribeHelper::fillRuns):
+        (WebCore::UniscribeHelper::shape):
+        (WebCore::UniscribeHelper::fillShapes):
+        (WebCore::UniscribeHelper::fillScreenOrder):
+        (WebCore::UniscribeHelper::adjustSpaceAdvances):
+        (WebCore::UniscribeHelper::applySpacing):
+        (WebCore::UniscribeHelper::advanceForItem):
+        * platform/graphics/chromium/UniscribeHelper.h: Added.
+        (WebCore::UniscribeHelper::directionalOverride):
+        (WebCore::UniscribeHelper::setDirectionalOverride):
+        (WebCore::UniscribeHelper::inhibitLigate):
+        (WebCore::UniscribeHelper::setInhibitLigate):
+        (WebCore::UniscribeHelper::letterSpacing):
+        (WebCore::UniscribeHelper::setLetterSpacing):
+        (WebCore::UniscribeHelper::spaceWidth):
+        (WebCore::UniscribeHelper::setSpaceWidth):
+        (WebCore::UniscribeHelper::wordSpacing):
+        (WebCore::UniscribeHelper::setWordSpacing):
+        (WebCore::UniscribeHelper::setAscent):
+        (WebCore::UniscribeHelper::init):
+        (WebCore::UniscribeHelper::tryToPreloadFont):
+        (WebCore::UniscribeHelper::Shaping::Shaping):
+        (WebCore::UniscribeHelper::Shaping::glyphLength):
+        (WebCore::UniscribeHelper::Shaping::charLength):
+        (WebCore::UniscribeHelper::Shaping::effectiveAdvances):
+        (WebCore::UniscribeHelper::nextWinFontData):
+        (WebCore::UniscribeHelper::resetFontIndex):
+        * platform/graphics/chromium/UniscribeHelperTextRun.cpp: Added.
+        (WebCore::UniscribeHelperTextRun::UniscribeHelperTextRun):
+        (WebCore::UniscribeHelperTextRun::tryToPreloadFont):
+        (WebCore::UniscribeHelperTextRun::nextWinFontData):
+        (WebCore::UniscribeHelperTextRun::resetFontIndex):
+        * platform/graphics/chromium/UniscribeHelperTextRun.h: Added.
+
 2009-01-15  Chris Marrin  <cmarrin@apple.com>
 
         Reviewed by Dan Bernstein.
diff --git a/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp
new file mode 100644 (file)
index 0000000..06e997f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SimpleFontData.h"
+
+#include "ChromiumBridge.h"
+#include "Font.h"
+#include "FontCache.h"
+#include "FloatRect.h"
+#include "FontDescription.h"
+#include <wtf/MathExtras.h>
+
+#include <unicode/uchar.h>
+#include <unicode/unorm.h>
+#include <objidl.h>
+#include <mlang.h>
+
+namespace WebCore {
+
+static inline float scaleEmToUnits(float x, int unitsPerEm)
+{
+    return unitsPerEm ? x / static_cast<float>(unitsPerEm) : x;
+}
+
+void SimpleFontData::platformInit()
+{
+    HDC dc = GetDC(0);
+    HGDIOBJ oldFont = SelectObject(dc, m_font.hfont());
+
+    TEXTMETRIC textMetric = {0};
+    if (!GetTextMetrics(dc, &textMetric)) {
+        if (ChromiumBridge::ensureFontLoaded(m_font.hfont())) {
+            // Retry GetTextMetrics.
+            // FIXME: Handle gracefully the error if this call also fails.
+            // See http://crbug.com/6401.
+            if (!GetTextMetrics(dc, &textMetric))
+                ASSERT_NOT_REACHED();
+        }
+    }
+
+    m_avgCharWidth = textMetric.tmAveCharWidth;
+    m_maxCharWidth = textMetric.tmMaxCharWidth;
+
+    m_ascent = textMetric.tmAscent;
+    m_descent = textMetric.tmDescent;
+    m_lineGap = textMetric.tmExternalLeading;
+    m_xHeight = m_ascent * 0.56f;  // Best guess for xHeight for non-Truetype fonts.
+
+    OUTLINETEXTMETRIC outlineTextMetric;
+    if (GetOutlineTextMetrics(dc, sizeof(outlineTextMetric), &outlineTextMetric) > 0) {
+        // This is a TrueType font.  We might be able to get an accurate xHeight.
+        GLYPHMETRICS glyphMetrics = {0};
+        MAT2 identityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
+        DWORD len = GetGlyphOutlineW(dc, 'x', GGO_METRICS, &glyphMetrics, 0, 0, &identityMatrix);
+        if (len != GDI_ERROR && glyphMetrics.gmBlackBoxY > 0)
+            m_xHeight = static_cast<float>(glyphMetrics.gmBlackBoxY);
+    }
+
+    m_lineSpacing = m_ascent + m_descent + m_lineGap;
+
+    SelectObject(dc, oldFont);
+    ReleaseDC(0, dc);
+}
+
+void SimpleFontData::platformDestroy()
+{
+    // We don't hash this on Win32, so it's effectively owned by us.
+    delete m_smallCapsFontData;
+    m_smallCapsFontData = 0;
+}
+
+SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
+{
+    if (!m_smallCapsFontData) {
+        LOGFONT winFont;
+        GetObject(m_font.hfont(), sizeof(LOGFONT), &winFont);
+        float smallCapsSize = 0.70f * fontDescription.computedSize();
+        // Unlike WebKit trunk, we don't multiply the size by 32.  That seems
+        // to be some kind of artifact of their CG backend, or something.
+        winFont.lfHeight = -lroundf(smallCapsSize);
+        HFONT hfont = CreateFontIndirect(&winFont);
+        m_smallCapsFontData =
+            new SimpleFontData(FontPlatformData(hfont, smallCapsSize));
+    }
+    return m_smallCapsFontData;
+}
+
+bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
+{
+  // This used to be implemented with IMLangFontLink2, but since that code has
+  // been disabled, this would always return false anyway.
+  return false;
+}
+
+void SimpleFontData::determinePitch()
+{
+    // TEXTMETRICS have this.  Set m_treatAsFixedPitch based off that.
+    HDC dc = GetDC(0);
+    HGDIOBJ oldFont = SelectObject(dc, m_font.hfont());
+
+    // Yes, this looks backwards, but the fixed pitch bit is actually set if the font
+    // is *not* fixed pitch.  Unbelievable but true.
+    TEXTMETRIC textMetric = {0};
+    if (!GetTextMetrics(dc, &textMetric)) {
+        if (ChromiumBridge::ensureFontLoaded(m_font.hfont())) {
+            // Retry GetTextMetrics.
+            // FIXME: Handle gracefully the error if this call also fails.
+            // See http://crbug.com/6401.
+            if (!GetTextMetrics(dc, &textMetric))
+                ASSERT_NOT_REACHED();
+        }
+    }
+
+    m_treatAsFixedPitch = ((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
+
+    SelectObject(dc, oldFont);
+    ReleaseDC(0, dc);
+}
+
+float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
+{
+    HDC dc = GetDC(0);
+    HGDIOBJ oldFont = SelectObject(dc, m_font.hfont());
+
+    int width = 0;
+    if (!GetCharWidthI(dc, glyph, 1, 0, &width)) {
+        // Ask the browser to preload the font and retry.
+        if (ChromiumBridge::ensureFontLoaded(m_font.hfont())) {
+            // FIXME: Handle gracefully the error if this call also fails.
+            // See http://crbug.com/6401.
+            if (!GetCharWidthI(dc, glyph, 1, 0, &width))
+                ASSERT_NOT_REACHED();
+        }
+    }
+
+    SelectObject(dc, oldFont);
+    ReleaseDC(0, dc);
+
+    return static_cast<float>(width);
+}
+
+}  // namespace WebCore
diff --git a/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp b/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp
new file mode 100644 (file)
index 0000000..e28d90d
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SimpleFontData.h"
+
+#include "Font.h"
+#include "FontCache.h"
+#include "FloatRect.h"
+#include "FontDescription.h"
+#include "Logging.h"
+#include "NotImplemented.h"
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include "SkTime.h"
+
+namespace WebCore {
+
+// Smallcaps versions of fonts are 70% the size of the normal font.
+static const float smallCapsFraction = 0.7f;
+
+void SimpleFontData::platformInit()
+{
+    SkPaint paint;
+    SkPaint::FontMetrics metrics;
+
+    m_font.setupPaint(&paint);
+    paint.getFontMetrics(&metrics);
+
+    // Beware those who step here: This code is designed to match Win32 font
+    // metrics *exactly*.
+    if (metrics.fVDMXMetricsValid) {
+        m_ascent = metrics.fVDMXAscent;
+        m_descent = metrics.fVDMXDescent;
+    } else {
+        m_ascent = SkScalarRound(-metrics.fAscent);
+        m_descent = SkScalarRound(metrics.fHeight) - m_ascent;
+    }
+
+    if (metrics.fXHeight)
+        m_xHeight = metrics.fXHeight;
+    else {
+        // hack taken from the Windows port
+        m_xHeight = static_cast<float>(m_ascent) * 0.56;
+    }
+
+    m_lineGap = SkScalarRound(metrics.fLeading);
+    m_lineSpacing = m_ascent + m_descent + m_lineGap;
+
+    // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is
+    // calculated for us, but we need to calculate m_maxCharWidth and
+    // m_avgCharWidth in order for text entry widgets to be sized correctly.
+
+    m_maxCharWidth = SkScalarRound(metrics.fXRange * SkScalarRound(m_font.size()));
+
+    if (metrics.fAvgCharWidth)
+        m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth);
+    else {
+        m_avgCharWidth = m_xHeight;
+
+        GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
+
+        if (glyphPageZero) {
+            static const UChar32 x_char = 'x';
+            const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(x_char).glyph;
+
+            if (xGlyph)
+                m_avgCharWidth = widthForGlyph(xGlyph);
+        }
+    }
+}
+
+void SimpleFontData::platformDestroy()
+{
+    delete m_smallCapsFontData;
+}
+
+SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
+{
+    if (!m_smallCapsFontData) {
+        const float smallCapsSize = lroundf(fontDescription.computedSize() * smallCapsFraction);
+        m_smallCapsFontData = new SimpleFontData(FontPlatformData(m_font, smallCapsSize));
+    }
+
+    return m_smallCapsFontData;
+}
+
+bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
+{
+    SkPaint paint;
+    static const unsigned maxBufferCount = 64;
+    uint16_t glyphs[maxBufferCount];
+
+    m_font.setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+    while (length > 0) {
+        int n = SkMin32(length, SK_ARRAY_COUNT(glyphs));
+
+        // textToGlyphs takes a byte count so we double the character count.
+        int count = paint.textToGlyphs(characters, n * 2, glyphs);
+        for (int i = 0; i < count; i++) {
+            if (0 == glyphs[i])
+                return false;       // missing glyph
+        }
+
+        characters += n;
+        length -= n;
+    }
+
+    return true;
+}
+
+void SimpleFontData::determinePitch()
+{
+    m_treatAsFixedPitch = platformData().isFixedPitch();
+}
+
+float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
+{
+    SkASSERT(sizeof(glyph) == 2);   // compile-time assert
+
+    SkPaint paint;
+
+    m_font.setupPaint(&paint);
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    SkScalar width = paint.measureText(&glyph, 2);
+    
+    return SkScalarToFloat(width);
+}
+
+}  // namespace WebCore
diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp
new file mode 100644 (file)
index 0000000..798ee32
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ThemeHelperChromiumWin.h"
+
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+
+namespace WebCore {
+
+ThemeHelperWin::ThemeHelperWin(GraphicsContext* context, const IntRect& rect)
+    : m_orgContext(context)
+    , m_orgMatrix(context->getCTM())
+    , m_orgRect(rect)
+{
+    if (m_orgMatrix.b() != 0 || m_orgMatrix.c() != 0) {  // Check for skew.
+        // Complicated effects, make a copy and draw the bitmap there.
+        m_type = COPY;
+        m_rect.setSize(rect.size());
+
+        m_newBuffer.set(ImageBuffer::create(rect.size(), false).release());
+
+        // Theme drawing messes with the transparency.
+        // FIXME: Ideally, we would leave this transparent, but I was
+        // having problems with button drawing, so we fill with white. Buttons
+        // looked good with transparent here and no fixing up of the alpha
+        // later, but text areas didn't. This makes text areas look good but
+        // gives buttons a white halo. Is there a way to fix this? I think
+        // buttons actually have antialised edges which is just not possible
+        // to handle on a transparent background given that it messes with the
+        // alpha channel.
+        FloatRect newContextRect(0, 0, rect.width(), rect.height());
+        GraphicsContext* newContext = m_newBuffer->context();
+        newContext->setFillColor(Color::white);
+        newContext->fillRect(newContextRect);
+
+        return;
+    }
+
+    if (m_orgMatrix.a() != 1.0 || m_orgMatrix.d() != 1.0) {  // Check for scale.
+        // Only a scaling is applied.
+        m_type = SCALE;
+
+        // Save the transformed coordinates to draw.
+        m_rect = m_orgMatrix.mapRect(rect);
+        
+        m_orgContext->save();
+        m_orgContext->concatCTM(m_orgContext->getCTM().inverse());
+        return;
+    }
+
+    // Nothing interesting.
+    m_rect = rect;
+    m_type = ORIGINAL;
+}
+
+ThemeHelperWin::~ThemeHelperWin()
+{
+    switch (m_type) {
+    case SCALE:
+        m_orgContext->restore();
+        break;
+    case COPY: {
+        // Copy the duplicate bitmap with our control to the original canvas.
+        FloatRect destRect(m_orgRect);
+        m_newBuffer->context()->platformContext()->canvas()->
+            getTopPlatformDevice().fixupAlphaBeforeCompositing();
+        m_orgContext->drawImage(m_newBuffer->image(), destRect);
+        break;
+    }
+    case ORIGINAL:
+        break;
+    }
+}
+
+}  // namespace WebCore
diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h
new file mode 100644 (file)
index 0000000..1771fb4
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ThemeHelperWin_h
+#define ThemeHelperWin_h
+
+#include "TransformationMatrix.h"
+#include "ImageBuffer.h"
+#include "IntRect.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class GraphicsContext;
+class IntRect;
+
+// Helps drawing theme elements like buttons and scroll bars. This will handle
+// translations and scalings that Windows might not, by either making Windows
+// draw the appropriate sized control, or by rendering it into an off-screen
+// context and transforming it ourselves.
+class ThemeHelperWin {
+    enum Type {
+        // Use the original canvas with no changes. This is the normal mode.
+        ORIGINAL,
+
+        // Use the original canvas but scale the rectangle of the control so
+        // that it will be the correct size, undoing any scale already on the
+        // canvas. This will have the effect of just drawing the control bigger
+        // or smaller and not actually expanding or contracting the pixels in
+        // it. This usually looks better.
+        SCALE,
+
+        // Make a copy of the control and then transform it ourselves after
+        // Windows draws it. This allows us to get complex effects.
+        COPY,
+    };
+
+public:
+    // Prepares drawing a control with the given rect to the given context.
+    ThemeHelperWin(GraphicsContext* context, const IntRect& rect);
+    ~ThemeHelperWin();
+
+    // Returns the context to draw the control into, which may be the original
+    // or the copy, depending on the mode.
+    GraphicsContext* context()
+    {
+        return m_newBuffer.get() ? m_newBuffer->context() : m_orgContext;
+    }
+
+    // Returns the rectangle in which to draw into the canvas() by Windows.
+    const IntRect& rect() { return m_rect; }
+
+private:
+    Type m_type;
+
+    // The original canvas to wrote to. Not owned by this class.
+    GraphicsContext* m_orgContext;
+    TransformationMatrix m_orgMatrix;
+    IntRect m_orgRect;
+
+    // When m_type == COPY, this will be a new surface owned by this class that
+    // represents the copy.
+    OwnPtr<ImageBuffer> m_newBuffer;
+
+    // The control rectangle in the coordinate space of canvas().
+    IntRect m_rect;
+};
+
+}  // namespace WebCore
+
+#endif  // ThemeHelperWin_h
diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.cpp b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp
new file mode 100644 (file)
index 0000000..caeb959
--- /dev/null
@@ -0,0 +1,902 @@
+/*
+ * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "UniscribeHelper.h"
+
+#include <windows.h>
+
+#include "FontUtilsChromiumWin.h"
+#include <wtf/Assertions.h>
+
+namespace WebCore {
+
+// This function is used to see where word spacing should be applied inside
+// runs. Note that this must match Font::treatAsSpace so we all agree where
+// and how much space this is, so we don't want to do more general Unicode
+// "is this a word break" thing.
+static bool treatAsSpace(UChar c)
+{
+    return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
+}
+
+// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
+// and blank glyphs. Just because ScriptShape succeeds does not mean
+// that a text run is rendered correctly. Some characters may be rendered
+// with default/invalid/blank glyphs. Therefore, we need to check if the glyph
+// array returned by ScriptShape contains any of those glyphs to make
+// sure that the text run is rendered successfully.
+static bool containsMissingGlyphs(WORD *glyphs,
+                                  int length,
+                                  SCRIPT_FONTPROPERTIES* properties)
+{
+    for (int i = 0; i < length; ++i) {
+        if (glyphs[i] == properties->wgDefault 
+            || (glyphs[i] == properties->wgInvalid
+            && glyphs[i] != properties->wgBlank))
+            return true;
+    }
+
+    return false;
+}
+
+// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
+// handle and we can't directly query it to make a new HFONT sharing
+// its characteristics (height, style, etc) except for family name.
+// This function uses GetObject to convert HFONT back to LOGFONT,
+// resets the fields of LOGFONT and calculates style to use later
+// for the creation of a font identical to HFONT other than family name.
+static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
+{
+    ASSERT(hfont && logfont);
+    if (!hfont || !logfont)
+        return;
+
+    GetObject(hfont, sizeof(LOGFONT), logfont);
+    // We reset these fields to values appropriate for CreateFontIndirect.
+    // while keeping lfHeight, which is the most important value in creating
+    // a new font similar to hfont.
+    logfont->lfWidth = 0;
+    logfont->lfEscapement = 0;
+    logfont->lfOrientation = 0;
+    logfont->lfCharSet = DEFAULT_CHARSET;
+    logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
+    logfont->lfQuality = DEFAULT_QUALITY;  // Honor user's desktop settings.
+    logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+    if (style)
+        *style = getStyleFromLogfont(logfont);
+}
+
+UniscribeHelper::UniscribeHelper(const UChar* input,
+                                int inputLength,
+                                bool isRtl,
+                                HFONT hfont,
+                                SCRIPT_CACHE* scriptCache,
+                                SCRIPT_FONTPROPERTIES* fontProperties)
+    : m_input(input)
+    , m_inputLength(inputLength)
+    , m_isRtl(isRtl)
+    , m_hfont(hfont)
+    , m_scriptCache(scriptCache)
+    , m_fontProperties(fontProperties)
+    , m_directionalOverride(false)
+    , m_inhibitLigate(false)
+    , m_letterSpacing(0)
+    , m_spaceWidth(0)
+    , m_wordSpacing(0)
+    , m_ascent(0)
+{
+    m_logfont.lfFaceName[0] = 0;
+}
+
+UniscribeHelper::~UniscribeHelper()
+{
+}
+
+void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection)
+{
+    // We cap the input length and just don't do anything. We'll allocate a lot
+    // of things of the size of the number of characters, so the allocated
+    // memory will be several times the input length. Plus shaping such a large
+    // buffer may be a form of denial of service. No legitimate text should be
+    // this long.  It also appears that Uniscribe flatly rejects very long
+    // strings, so we don't lose anything by doing this.
+    //
+    // The input length protection may be disabled by the unit tests to cause
+    // an error condition.
+    static const int kMaxInputLength = 65535;
+    if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength))
+        return;
+
+    fillRuns();
+    fillShapes();
+    fillScreenOrder();
+}
+
+int UniscribeHelper::width() const
+{
+    int width = 0;
+    for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++)
+        width += advanceForItem(itemIndex);
+    return width;
+}
+
+void UniscribeHelper::justify(int additionalSpace)
+{
+    // Count the total number of glyphs we have so we know how big to make the
+    // buffers below.
+    int totalGlyphs = 0;
+    for (size_t run = 0; run < m_runs.size(); run++) {
+        int runIndex = m_screenOrder[run];
+        totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength());
+    }
+    if (totalGlyphs == 0)
+        return;  // Nothing to do.
+
+    // We make one big buffer in screen order of all the glyphs we are drawing
+    // across runs so that the justification function will adjust evenly across
+    // all glyphs.
+    Vector<SCRIPT_VISATTR, 64> visualAttributes;
+    visualAttributes.resize(totalGlyphs);
+    Vector<int, 64> advances;
+    advances.resize(totalGlyphs);
+    Vector<int, 64> justify;
+    justify.resize(totalGlyphs);
+
+    // Build the packed input.
+    int destIndex = 0;
+    for (size_t run = 0; run < m_runs.size(); run++) {
+        int runIndex = m_screenOrder[run];
+        const Shaping& shaping = m_shapes[runIndex];
+
+        for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) {
+            memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i],
+                   sizeof(SCRIPT_VISATTR));
+            advances[destIndex] = shaping.m_advance[i];
+        }
+    }
+
+    // The documentation for Scriptjustify is wrong, the parameter is the space
+    // to add and not the width of the column you want.
+    const int minKashida = 1;  // How do we decide what this should be?
+    ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs,
+                  additionalSpace, minKashida, &justify[0]);
+
+    // Now we have to unpack the justification amounts back into the runs so
+    // the glyph indices match.
+    int globalGlyphIndex = 0;
+    for (size_t run = 0; run < m_runs.size(); run++) {
+        int runIndex = m_screenOrder[run];
+        Shaping& shaping = m_shapes[runIndex];
+
+        shaping.m_justify.resize(shaping.glyphLength());
+        for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
+            shaping.m_justify[i] = justify[globalGlyphIndex];
+    }
+}
+
+int UniscribeHelper::characterToX(int offset) const
+{
+    HRESULT hr;
+    ASSERT(offset <= m_inputLength);
+
+    // Our algorithm is to traverse the items in screen order from left to
+    // right, adding in each item's screen width until we find the item with
+    // the requested character in it.
+    int width = 0;
+    for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
+        // Compute the length of this run.
+        int itemIndex = m_screenOrder[screenIndex];
+        const SCRIPT_ITEM& item = m_runs[itemIndex];
+        const Shaping& shaping = m_shapes[itemIndex];
+        int itemLength = shaping.charLength();
+
+        if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
+            // Character offset is in this run.
+            int charLength = offset - item.iCharPos;
+
+            int curX = 0;
+            hr = ScriptCPtoX(charLength, FALSE, itemLength,
+                             shaping.glyphLength(),
+                             &shaping.m_logs[0], &shaping.m_visualAttributes[0],
+                             shaping.effectiveAdvances(), &item.a, &curX);
+            if (FAILED(hr))
+                return 0;
+
+            width += curX + shaping.m_prePadding;
+            ASSERT(width >= 0);
+            return width;
+        }
+
+        // Move to the next item.
+        width += advanceForItem(itemIndex);
+    }
+    ASSERT(width >= 0);
+    return width;
+}
+
+int UniscribeHelper::xToCharacter(int x) const
+{
+    // We iterate in screen order until we find the item with the given pixel
+    // position in it. When we find that guy, we ask Uniscribe for the
+    // character index.
+    HRESULT hr;
+    for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
+        int itemIndex = m_screenOrder[screenIndex];
+        int itemAdvance = advanceForItem(itemIndex);
+
+        // Note that the run may be empty if shaping failed, so we want to skip
+        // over it.
+        const Shaping& shaping = m_shapes[itemIndex];
+        int itemLength = shaping.charLength();
+        if (x <= itemAdvance && itemLength > 0) {
+            // The requested offset is within this item.
+            const SCRIPT_ITEM& item = m_runs[itemIndex];
+
+            // Account for the leading space we've added to this run that
+            // Uniscribe doesn't know about.
+            x -= shaping.m_prePadding;
+
+            int charX = 0;
+            int trailing;
+            hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
+                             &shaping.m_logs[0], &shaping.m_visualAttributes[0],
+                             shaping.effectiveAdvances(), &item.a, &charX,
+                             &trailing);
+
+            // The character offset is within the item. We need to add the
+            // item's offset to transform it into the space of the TextRun
+            return charX + item.iCharPos;
+        }
+
+        // The offset is beyond this item, account for its length and move on.
+        x -= itemAdvance;
+    }
+
+    // Error condition, we don't know what to do if we don't have that X
+    // position in any of our items.
+    return 0;
+}
+
+void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to)
+{
+    HGDIOBJ oldFont = 0;
+    int curX = x;
+    bool firstRun = true;
+
+    for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
+        int itemIndex = m_screenOrder[screenIndex];
+        const SCRIPT_ITEM& item = m_runs[itemIndex];
+        const Shaping& shaping = m_shapes[itemIndex];
+
+        // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
+        // be negative, etc. The code below handles this.
+        int fromChar = from - item.iCharPos;
+        int toChar = to - item.iCharPos;
+
+        // See if we need to draw any characters in this item.
+        if (shaping.charLength() == 0 ||
+            fromChar >= shaping.charLength() || toChar <= 0) {
+            // No chars in this item to display.
+            curX += advanceForItem(itemIndex);
+            continue;
+        }
+
+        // Compute the starting glyph within this span. |from| and |to| are
+        // global offsets that may intersect arbitrarily with our local run.
+        int fromGlyph, afterGlyph;
+        if (item.a.fRTL) {
+            // To compute the first glyph when going RTL, we use |to|.
+            if (toChar >= shaping.charLength())
+                // The end of the text is after (to the left) of us.
+                fromGlyph = 0;
+            else {
+                // Since |to| is exclusive, the first character we draw on the
+                // left is actually the one right before (to the right) of
+                // |to|.
+                fromGlyph = shaping.m_logs[toChar - 1];
+            }
+
+            // The last glyph is actually the first character in the range.
+            if (fromChar <= 0) {
+                // The first character to draw is before (to the right) of this
+                // span, so draw all the way to the end.
+                afterGlyph = shaping.glyphLength();
+            } else {
+                // We want to draw everything up until the character to the
+                // right of |from|. To the right is - 1, so we look that up
+                // (remember our character could be more than one glyph, so we
+                // can't look up our glyph and add one).
+                afterGlyph = shaping.m_logs[fromChar - 1];
+            }
+        } else {
+            // Easy case, everybody agrees about directions. We only need to
+            // handle boundary conditions to get a range inclusive at the
+            // beginning, and exclusive at the ending. We have to do some
+            // computation to see the glyph one past the end.
+            fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
+            if (toChar >= shaping.charLength())
+                afterGlyph = shaping.glyphLength();
+            else
+                afterGlyph = shaping.m_logs[toChar];
+        }
+
+        // Account for the characters that were skipped in this run. When
+        // WebKit asks us to draw a subset of the run, it actually tells us
+        // to draw at the X offset of the beginning of the run, since it
+        // doesn't know the internal position of any of our characters.
+        const int* effectiveAdvances = shaping.effectiveAdvances();
+        int innerOffset = 0;
+        for (int i = 0; i < fromGlyph; i++)
+            innerOffset += effectiveAdvances[i];
+
+        // Actually draw the glyphs we found.
+        int glyphCount = afterGlyph - fromGlyph;
+        if (fromGlyph >= 0 && glyphCount > 0) {
+            // Account for the preceeding space we need to add to this run. We
+            // don't need to count for the following space because that will be
+            // counted in advanceForItem below when we move to the next run.
+            innerOffset += shaping.m_prePadding;
+
+            // Pass 0 in when there is no justification.
+            const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph];
+
+            if (firstRun) {
+                oldFont = SelectObject(dc, shaping.m_hfont);
+                firstRun = false;
+            } else
+                SelectObject(dc, shaping.m_hfont);
+
+            // Fonts with different ascents can be used to render different
+            // runs.  'Across-runs' y-coordinate correction needs to be
+            // adjusted for each font.
+            HRESULT hr = S_FALSE;
+            for (int executions = 0; executions < 2; ++executions) {
+                hr = ScriptTextOut(dc, shaping.m_scriptCache,
+                                   curX + innerOffset,
+                                   y - shaping.m_ascentOffset,
+                                   0, 0, &item.a, 0, 0,
+                                   &shaping.m_glyphs[fromGlyph],
+                                   glyphCount,
+                                   &shaping.m_advance[fromGlyph],
+                                   justify,
+                                   &shaping.m_offsets[fromGlyph]);
+                if (S_OK != hr && 0 == executions) {
+                    // If this ScriptTextOut is called from the renderer it
+                    // might fail because the sandbox is preventing it from
+                    // opening the font files.  If we are running in the
+                    // renderer, TryToPreloadFont is overridden to ask the
+                    // browser to preload the font for us so we can access it.
+                    tryToPreloadFont(shaping.m_hfont);
+                    continue;
+                }
+                break;
+            }
+
+            ASSERT(S_OK == hr);
+        }
+
+        curX += advanceForItem(itemIndex);
+    }
+
+    if (oldFont)
+        SelectObject(dc, oldFont);
+}
+
+WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const
+{
+    // Find the run for the given character.
+    for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
+        int firstChar = m_runs[i].iCharPos;
+        const Shaping& shaping = m_shapes[i];
+        int localOffset = charOffset - firstChar;
+        if (localOffset >= 0 && localOffset < shaping.charLength()) {
+            // The character is in this run, return the first glyph for it
+            // (should generally be the only glyph). It seems Uniscribe gives
+            // glyph 0 for empty, which is what we want to return in the
+            // "missing" case.
+            size_t glyphIndex = shaping.m_logs[localOffset];
+            if (glyphIndex >= shaping.m_glyphs.size()) {
+                // The glyph should be in this run, but the run has too few
+                // actual characters. This can happen when shaping the run
+                // fails, in which case, we should have no data in the logs at
+                // all.
+                ASSERT(shaping.m_glyphs.size() == 0);
+                return 0;
+            }
+            return shaping.m_glyphs[glyphIndex];
+        }
+    }
+
+    return 0;
+}
+
+void UniscribeHelper::fillRuns()
+{
+    HRESULT hr;
+    m_runs.resize(UNISCRIBE_HELPER_STACK_RUNS);
+
+    SCRIPT_STATE inputState;
+    inputState.uBidiLevel = m_isRtl;
+    inputState.fOverrideDirection = m_directionalOverride;
+    inputState.fInhibitSymSwap = false;
+    inputState.fCharShape = false;  // Not implemented in Uniscribe
+    inputState.fDigitSubstitute = false;  // Do we want this for Arabic?
+    inputState.fInhibitLigate = m_inhibitLigate;
+    inputState.fDisplayZWG = false;  // Don't draw control characters.
+    inputState.fArabicNumContext = m_isRtl;  // Do we want this for Arabic?
+    inputState.fGcpClusters = false;
+    inputState.fReserved = 0;
+    inputState.fEngineReserved = 0;
+    // The psControl argument to ScriptItemize should be non-0 for RTL text,
+    // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
+    // SCRIPT_CONTROL that is set to all zeros.  Zero as a locale ID means the
+    // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx
+    static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage    :16;
+                                           0, // fContextDigits      :1;
+                                           0, // fInvertPreBoundDir  :1;
+                                           0, // fInvertPostBoundDir :1;
+                                           0, // fLinkStringBefore   :1;
+                                           0, // fLinkStringAfter    :1;
+                                           0, // fNeutralOverride    :1;
+                                           0, // fNumericOverride    :1;
+                                           0, // fLegacyBidiClass    :1;
+                                           0, // fMergeNeutralItems  :1;
+                                           0};// fReserved           :7;
+    // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState)
+    // here would be appropriate if we wanted to set the language ID, and get
+    // local digit substitution behavior.  For now, don't do it.
+
+    while (true) {
+        int numberOfItems = 0;
+
+        // Ideally, we would have a way to know the runs before and after this
+        // one, and put them into the control parameter of ScriptItemize. This
+        // would allow us to shape characters properly that cross style
+        // boundaries (WebKit bug 6148).
+        //
+        // We tell ScriptItemize that the output list of items is one smaller
+        // than it actually is. According to Mozilla bug 366643, if there is
+        // not enough room in the array on pre-SP2 systems, ScriptItemize will
+        // write one past the end of the buffer.
+        //
+        // ScriptItemize is very strange. It will often require a much larger
+        // ITEM buffer internally than it will give us as output. For example,
+        // it will say a 16-item buffer is not big enough, and will write
+        // interesting numbers into all those items. But when we give it a 32
+        // item buffer and it succeeds, it only has one item output.
+        //
+        // It seems to be doing at least two passes, the first where it puts a
+        // lot of intermediate data into our items, and the second where it
+        // collates them.
+        hr = ScriptItemize(m_input, m_inputLength,
+                           static_cast<int>(m_runs.size()) - 1, &inputControl,
+                           &inputState,
+                           &m_runs[0], &numberOfItems);
+        if (SUCCEEDED(hr)) {
+            m_runs.resize(numberOfItems);
+            break;
+        }
+        if (hr != E_OUTOFMEMORY) {
+            // Some kind of unexpected error.
+            m_runs.resize(0);
+            break;
+        }
+        // There was not enough items for it to write into, expand.
+        m_runs.resize(m_runs.size() * 2);
+    }
+}
+
+bool UniscribeHelper::shape(const UChar* input,
+                            int itemLength,
+                            int numGlyphs,
+                            SCRIPT_ITEM& run,
+                            Shaping& shaping)
+{
+    HFONT hfont = m_hfont;
+    SCRIPT_CACHE* scriptCache = m_scriptCache;
+    SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
+    int ascent = m_ascent;
+    HDC tempDC = 0;
+    HGDIOBJ oldFont = 0;
+    HRESULT hr;
+    bool lastFallbackTried = false;
+    bool result;
+
+    int generatedGlyphs = 0;
+
+    // In case HFONT passed in ctor cannot render this run, we have to scan
+    // other fonts from the beginning of the font list.
+    resetFontIndex();
+
+    // Compute shapes.
+    while (true) {
+        shaping.m_logs.resize(itemLength);
+        shaping.m_glyphs.resize(numGlyphs);
+        shaping.m_visualAttributes.resize(numGlyphs);
+
+#ifdef PURIFY
+        // http://code.google.com/p/chromium/issues/detail?id=5309
+        // Purify isn't able to track the assignments that ScriptShape makes to
+        // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it
+        // writes, will be considered un-initialized data.
+        //
+        // This hack avoid the false-positive UMRs by marking the buffer as
+        // initialized.
+        //
+        // FIXME: A better solution would be to use Purify's API and mark only
+        // the populated range as initialized:
+        //
+        //     PurifyMarkAsInitialized(
+        //         &shaping.m_glyphs[0],
+        //         sizeof(shaping.m_glyphs[0] * generatedGlyphs);
+        
+        ZeroMemory(&shaping.m_glyphs[0],
+                   sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
+#endif
+
+        // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
+        // here. Is that what we want? It will display control characters.
+        hr = ScriptShape(tempDC, scriptCache, input, itemLength,
+                         numGlyphs, &run.a,
+                         &shaping.m_glyphs[0], &shaping.m_logs[0],
+                         &shaping.m_visualAttributes[0], &generatedGlyphs);
+        if (hr == E_PENDING) {
+            // Allocate the DC.
+            tempDC = GetDC(0);
+            oldFont = SelectObject(tempDC, hfont);
+            continue;
+        } else if (hr == E_OUTOFMEMORY) {
+            numGlyphs *= 2;
+            continue;
+        } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(&shaping.m_glyphs[0], generatedGlyphs, fontProperties)))
+            break;
+
+        // The current font can't render this run. clear DC and try
+        // next font.
+        if (tempDC) {
+            SelectObject(tempDC, oldFont);
+            ReleaseDC(0, tempDC);
+            tempDC = 0;
+        }
+
+        if (nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) {
+            // The primary font does not support this run. Try next font.
+            // In case of web page rendering, they come from fonts specified in
+            // CSS stylesheets.
+            continue;
+        } else if (!lastFallbackTried) {
+            lastFallbackTried = true;
+
+            // Generate a last fallback font based on the script of
+            // a character to draw while inheriting size and styles
+            // from the primary font
+            if (!m_logfont.lfFaceName[0])
+                setLogFontAndStyle(m_hfont, &m_logfont, &m_style);
+
+            // TODO(jungshik): generic type should come from webkit for
+            // UniscribeHelperTextRun (a derived class used in webkit).
+            const UChar *family = getFallbackFamily(input, itemLength,
+                FontDescription::StandardFamily, 0, 0);
+            bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
+                                              &ascent, &hfont, &scriptCache);
+
+            if (!fontOk) {
+                // If this GetDerivedFontData is called from the renderer it
+                // might fail because the sandbox is preventing it from opening
+                // the font files.  If we are running in the renderer,
+                // TryToPreloadFont is overridden to ask the browser to preload
+                // the font for us so we can access it.
+                tryToPreloadFont(hfont);
+
+                // Try again.
+                fontOk = getDerivedFontData(family, m_style, &m_logfont,
+                                             &ascent, &hfont, &scriptCache);
+                ASSERT(fontOk);
+            }
+
+            // TODO(jungshik) : Currently GetDerivedHFont always returns a
+            // a valid HFONT, but in the future, I may change it to return 0.
+            ASSERT(hfont);
+
+            // We don't need a font_properties for the last resort fallback font
+            // because we don't have anything more to try and are forced to
+            // accept empty glyph boxes. If we tried a series of fonts as
+            // 'last-resort fallback', we'd need it, but currently, we don't.
+            continue;
+        } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
+            run.a.eScript = SCRIPT_UNDEFINED;
+            continue;
+        } else if (FAILED(hr)) {
+            // Error shaping.
+            generatedGlyphs = 0;
+            result = false;
+            goto cleanup;
+        }
+    }
+
+    // Sets Windows font data for this run to those corresponding to
+    // a font supporting this run. we don't need to store font_properties
+    // because it's not used elsewhere.
+    shaping.m_hfont = hfont;
+    shaping.m_scriptCache = scriptCache;
+
+    // The ascent of a font for this run can be different from
+    // that of the primary font so that we need to keep track of
+    // the difference per run and take that into account when calling
+    // ScriptTextOut in |draw|. Otherwise, different runs rendered by
+    // different fonts would not be aligned vertically.
+    shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
+    result = true;
+
+  cleanup:
+    shaping.m_glyphs.resize(generatedGlyphs);
+    shaping.m_visualAttributes.resize(generatedGlyphs);
+    shaping.m_advance.resize(generatedGlyphs);
+    shaping.m_offsets.resize(generatedGlyphs);
+    if (tempDC) {
+        SelectObject(tempDC, oldFont);
+        ReleaseDC(0, tempDC);
+    }
+    // On failure, our logs don't mean anything, so zero those out.
+    if (!result)
+        shaping.m_logs.clear();
+
+    return result;
+}
+
+void UniscribeHelper::fillShapes()
+{
+    m_shapes.resize(m_runs.size());
+    for (size_t i = 0; i < m_runs.size(); i++) {
+        int startItem = m_runs[i].iCharPos;
+        int itemLength = m_inputLength - startItem;
+        if (i < m_runs.size() - 1)
+            itemLength = m_runs[i + 1].iCharPos - startItem;
+
+        int numGlyphs;
+        if (itemLength < UNISCRIBE_HELPER_STACK_CHARS) {
+            // We'll start our buffer sizes with the current stack space
+            // available in our buffers if the current input fits. As long as
+            // it doesn't expand past that we'll save a lot of time mallocing.
+            numGlyphs = UNISCRIBE_HELPER_STACK_CHARS;
+        } else {
+            // When the input doesn't fit, give up with the stack since it will
+            // almost surely not be enough room (unless the input actually
+            // shrinks, which is unlikely) and just start with the length
+            // recommended by the Uniscribe documentation as a "usually fits"
+            // size.
+            numGlyphs = itemLength * 3 / 2 + 16;
+        }
+
+        // Convert a string to a glyph string trying the primary font, fonts in
+        // the fallback list and then script-specific last resort font.
+        Shaping& shaping = m_shapes[i];
+        if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping))
+            continue;
+
+        // Compute placements. Note that offsets is documented incorrectly
+        // and is actually an array.
+
+        // DC that we lazily create if Uniscribe commands us to.
+        // (this does not happen often because scriptCache is already
+        //  updated when calling ScriptShape).
+        HDC tempDC = 0;
+        HGDIOBJ oldFont = 0;
+        HRESULT hr;
+        while (true) {
+            shaping.m_prePadding = 0;
+            hr = ScriptPlace(tempDC, shaping.m_scriptCache,
+                             &shaping.m_glyphs[0],
+                             static_cast<int>(shaping.m_glyphs.size()),
+                             &shaping.m_visualAttributes[0], &m_runs[i].a,
+                             &shaping.m_advance[0], &shaping.m_offsets[0],
+                             &shaping.m_abc);
+            if (hr != E_PENDING)
+                break;
+
+            // Allocate the DC and run the loop again.
+            tempDC = GetDC(0);
+            oldFont = SelectObject(tempDC, shaping.m_hfont);
+        }
+
+        if (FAILED(hr)) {
+            // Some error we don't know how to handle. Nuke all of our data
+            // since we can't deal with partially valid data later.
+            m_runs.clear();
+            m_shapes.clear();
+            m_screenOrder.clear();
+        }
+
+        if (tempDC) {
+            SelectObject(tempDC, oldFont);
+            ReleaseDC(0, tempDC);
+        }
+    }
+
+    adjustSpaceAdvances();
+
+    if (m_letterSpacing != 0 || m_wordSpacing != 0)
+        applySpacing();
+}
+
+void UniscribeHelper::fillScreenOrder()
+{
+    m_screenOrder.resize(m_runs.size());
+
+    // We assume that the input has only one text direction in it.
+    // TODO(brettw) are we sure we want to keep this restriction?
+    if (m_isRtl) {
+        for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
+            m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
+    } else {
+        for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
+            m_screenOrder[i] = i;
+    }
+}
+
+void UniscribeHelper::adjustSpaceAdvances()
+{
+    if (m_spaceWidth == 0)
+        return;
+
+    int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
+
+    // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
+    for (size_t run = 0; run < m_runs.size(); run++) {
+        Shaping& shaping = m_shapes[run];
+
+        for (int i = 0; i < shaping.charLength(); i++) {
+            if (!treatAsSpace(m_input[m_runs[run].iCharPos + i]))
+                continue;
+
+            int glyphIndex = shaping.m_logs[i];
+            int currentAdvance = shaping.m_advance[glyphIndex];
+            // Don't give zero-width spaces a width.
+            if (!currentAdvance)
+                continue;
+
+            // currentAdvance does not include additional letter-spacing, but
+            // space_width does. Here we find out how off we are from the
+            // correct width for the space not including letter-spacing, then
+            // just subtract that diff.
+            int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
+            // The shaping can consist of a run of text, so only subtract the
+            // difference in the width of the glyph.
+            shaping.m_advance[glyphIndex] -= diff;
+            shaping.m_abc.abcB -= diff;
+        }
+    }
+}
+
+void UniscribeHelper::applySpacing()
+{
+    for (size_t run = 0; run < m_runs.size(); run++) {
+        Shaping& shaping = m_shapes[run];
+        bool isRtl = m_runs[run].a.fRTL;
+
+        if (m_letterSpacing != 0) {
+            // RTL text gets padded to the left of each character. We increment
+            // the run's advance to make this happen. This will be balanced out
+            // by NOT adding additional advance to the last glyph in the run.
+            if (isRtl)
+                shaping.m_prePadding += m_letterSpacing;
+
+            // Go through all the glyphs in this run and increase the "advance"
+            // to account for letter spacing. We adjust letter spacing only on
+            // cluster boundaries.
+            //
+            // This works for most scripts, but may have problems with some
+            // indic scripts. This behavior is better than Firefox or IE for
+            // Hebrew.
+            for (int i = 0; i < shaping.glyphLength(); i++) {
+                if (shaping.m_visualAttributes[i].fClusterStart) {
+                    // Ick, we need to assign the extra space so that the glyph
+                    // comes first, then is followed by the space. This is
+                    // opposite for RTL.
+                    if (isRtl) {
+                        if (i != shaping.glyphLength() - 1) {
+                            // All but the last character just get the spacing
+                            // applied to their advance. The last character
+                            // doesn't get anything,
+                            shaping.m_advance[i] += m_letterSpacing;
+                            shaping.m_abc.abcB += m_letterSpacing;
+                        }
+                    } else {
+                        // LTR case is easier, we just add to the advance.
+                        shaping.m_advance[i] += m_letterSpacing;
+                        shaping.m_abc.abcB += m_letterSpacing;
+                    }
+                }
+            }
+        }
+
+        // Go through all the characters to find whitespace and insert the
+        // extra wordspacing amount for the glyphs they correspond to.
+        if (m_wordSpacing != 0) {
+            for (int i = 0; i < shaping.charLength(); i++) {
+                if (!treatAsSpace(m_input[m_runs[run].iCharPos + i]))
+                    continue;
+
+                // The char in question is a word separator...
+                int glyphIndex = shaping.m_logs[i];
+
+                // Spaces will not have a glyph in Uniscribe, it will just add
+                // additional advance to the character to the left of the
+                // space. The space's corresponding glyph will be the character
+                // following it in reading order.
+                if (isRtl) {
+                    // In RTL, the glyph to the left of the space is the same
+                    // as the first glyph of the following character, so we can
+                    // just increment it.
+                    shaping.m_advance[glyphIndex] += m_wordSpacing;
+                    shaping.m_abc.abcB += m_wordSpacing;
+                } else {
+                    // LTR is actually more complex here, we apply it to the
+                    // previous character if there is one, otherwise we have to
+                    // apply it to the leading space of the run.
+                    if (glyphIndex == 0)
+                        shaping.m_prePadding += m_wordSpacing;
+                    else {
+                        shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
+                        shaping.m_abc.abcB += m_wordSpacing;
+                    }
+                }
+            }
+        }  // m_wordSpacing != 0
+
+        // Loop for next run...
+    }
+}
+
+// The advance is the ABC width of the run
+int UniscribeHelper::advanceForItem(int itemIndex) const
+{
+    int accum = 0;
+    const Shaping& shaping = m_shapes[itemIndex];
+
+    if (shaping.m_justify.size() == 0) {
+        // Easy case with no justification, the width is just the ABC width of
+        // the run. (The ABC width is the sum of the advances).
+        return shaping.m_abc.abcA + shaping.m_abc.abcB +
+               shaping.m_abc.abcC + shaping.m_prePadding;
+    }
+
+    // With justification, we use the justified amounts instead. The
+    // justification array contains both the advance and the extra space
+    // added for justification, so is the width we want.
+    int justification = 0;
+    for (size_t i = 0; i < shaping.m_justify.size(); i++)
+        justification += shaping.m_justify[i];
+
+    return shaping.m_prePadding + justification;
+}
+
+}  // namespace WebCore
diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.h b/WebCore/platform/graphics/chromium/UniscribeHelper.h
new file mode 100644 (file)
index 0000000..d291105
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// A wrapper around Uniscribe that provides a reasonable API.
+
+#ifndef UniscribeHelper_h
+#define UniscribeHelper_h
+
+#include <windows.h>
+#include <usp10.h>
+#include <map>
+
+#include <unicode/uchar.h>
+#include <wtf/Vector.h>
+
+class UniscribeTest_TooBig_Test;  // A gunit test for UniscribeHelper.
+
+namespace WebCore {
+
+#define UNISCRIBE_HELPER_STACK_RUNS 8
+#define UNISCRIBE_HELPER_STACK_CHARS 32
+
+// This object should be safe to create & destroy frequently, as long as the
+// caller preserves the script_cache when possible (this data may be slow to
+// compute).
+//
+// This object is "kind of large" (~1K) because it reserves a lot of space for
+// working with to avoid expensive heap operations. Therefore, not only should
+// you not worry about creating and destroying it, you should try to not keep
+// them around.
+class UniscribeHelper {
+public:
+    // Initializes this Uniscribe run with the text pointed to by |run| with
+    // |length|. The input is NOT null terminated.
+    //
+    // The is_rtl flag should be set if the input script is RTL. It is assumed
+    // that the caller has already divided up the input text (using ICU, for
+    // example) into runs of the same direction of script. This avoids
+    // disagreements between the caller and Uniscribe later (see FillItems).
+    //
+    // A script cache should be provided by the caller that is initialized to
+    // NULL. When the caller is done with the cache (it may be stored between
+    // runs as long as it is used consistently with the same HFONT), it should
+    // call ScriptFreeCache().
+    UniscribeHelper(const UChar* input,
+                    int inputLength,
+                    bool isRtl,
+                    HFONT,
+                    SCRIPT_CACHE*,
+                    SCRIPT_FONTPROPERTIES*);
+
+    virtual ~UniscribeHelper();
+
+    // Sets Uniscribe's directional override flag. False by default.
+    bool directionalOverride() const
+    {
+        return m_directionalOverride;
+    }
+    void setDirectionalOverride(bool override)
+    {
+        m_directionalOverride = override;
+    }
+
+    // Set's Uniscribe's no-ligate override flag. False by default.
+    bool inhibitLigate() const
+    {
+        return m_inhibitLigate;
+    }
+    void setInhibitLigate(bool inhibit)
+    {
+        m_inhibitLigate = inhibit;
+    }
+
+    // Set letter spacing. We will try to insert this much space between
+    // graphemes (one or more glyphs perceived as a single unit by ordinary
+    // users of a script). Positive values increase letter spacing, negative
+    // values decrease it. 0 by default.
+    int letterSpacing() const
+    {
+        return m_letterSpacing;
+    }
+    void setLetterSpacing(int letterSpacing)
+    {
+        m_letterSpacing = letterSpacing;
+    }
+
+    // Set the width of a standard space character. We use this to normalize
+    // space widths. Windows will make spaces after Hindi characters larger than
+    // other spaces. A space_width of 0 means to use the default space width.
+    //
+    // Must be set before Init() is called.
+    int spaceWidth() const
+    {
+        return m_spaceWidth;
+    }
+    void setSpaceWidth(int spaceWidth)
+    {
+        m_spaceWidth = spaceWidth;
+    }
+
+    // Set word spacing. We will try to insert this much extra space between
+    // each word in the input (beyond whatever whitespace character separates
+    // words). Positive values lead to increased letter spacing, negative values
+    // decrease it. 0 by default.
+    //
+    // Must be set before Init() is called.
+    int wordSpacing() const
+    {
+        return m_wordSpacing;
+    }
+    void setWordSpacing(int wordSpacing)
+    {
+        m_wordSpacing = wordSpacing;
+    }
+
+    void setAscent(int ascent)
+    {
+        m_ascent = ascent;
+    }
+
+    // You must call this after setting any options but before doing any
+    // other calls like asking for widths or drawing.
+    void init()
+    {
+        initWithOptionalLengthProtection(true);
+    }
+
+    // Returns the total width in pixels of the text run.
+    int width() const;
+
+    // Call to justify the text, with the amount of space that should be ADDED
+    // to get the desired width that the column should be justified to.
+    // Normally, spaces are inserted, but for Arabic there will be kashidas
+    // (extra strokes) inserted instead.
+    //
+    // This function MUST be called AFTER Init().
+    void justify(int additionalSpace);
+
+    // Computes the given character offset into a pixel offset of the beginning
+    // of that character.
+    int characterToX(int offset) const;
+
+    // Converts the given pixel X position into a logical character offset into
+    // the run. For positions appearing before the first character, this will
+    // return -1.
+    int xToCharacter(int x) const;
+
+    // Draws the given characters to (x, y) in the given DC. The font will be
+    // handled by this function, but the font color and other attributes should
+    // be pre-set.
+    //
+    // The y position is the upper left corner, NOT the baseline.
+    void draw(HDC, int x, int y, int from, int to);
+
+    // Returns the first glyph assigned to the character at the given offset.
+    // This function is used to retrieve glyph information when Uniscribe is
+    // being used to generate glyphs for non-complex, non-BMP (above U+FFFF)
+    // characters. These characters are not otherwise special and have no
+    // complex shaping rules, so we don't otherwise need Uniscribe, except
+    // Uniscribe is the only way to get glyphs for non-BMP characters.
+    //
+    // Returns 0 if there is no glyph for the given character.
+    WORD firstGlyphForCharacter(int charOffset) const;
+
+protected:
+    // Backend for init. The flag allows the unit test to specify whether we
+    // should fail early for very long strings like normal, or try to pass the
+    // long string to Uniscribe. The latter provides a way to force failure of
+    // shaping.
+    void initWithOptionalLengthProtection(bool lengthProtection);
+
+    // Tries to preload the font when the it is not accessible.
+    // This is the default implementation and it does not do anything.
+    virtual void tryToPreloadFont(HFONT) {}
+
+private:
+    friend class UniscribeTest_TooBig_Test;
+
+    // An array corresponding to each item in runs_ containing information
+    // on each of the glyphs that were generated. Like runs_, this is in
+    // reading order. However, for rtl text, the characters within each
+    // item will be reversed.
+    struct Shaping {
+        Shaping()
+            : m_prePadding(0)
+            , m_hfont(NULL)
+            , m_scriptCache(NULL)
+            , m_ascentOffset(0) {
+            m_abc.abcA = 0;
+            m_abc.abcB = 0;
+            m_abc.abcC = 0;
+        }
+
+        // Returns the number of glyphs (which will be drawn to the screen)
+        // in this run.
+        int glyphLength() const
+        {
+            return static_cast<int>(m_glyphs.size());
+        }
+
+        // Returns the number of characters (that we started with) in this run.
+        int charLength() const
+        {
+            return static_cast<int>(m_logs.size());
+        }
+
+        // Returns the advance array that should be used when measuring glyphs.
+        // The returned pointer will indicate an array with glyph_length()
+        // elements and the advance that should be used for each one. This is
+        // either the real advance, or the justified advances if there is one,
+        // and is the array we want to use for measurement.
+        const int* effectiveAdvances() const
+        {
+            if (m_advance.size() == 0)
+                return 0;
+            if (m_justify.size() == 0)
+                return &m_advance[0];
+            return &m_justify[0];
+        }
+
+        // This is the advance amount of space that we have added to the
+        // beginning of the run. It is like the ABC's |A| advance but one that
+        // we create and must handle internally whenever computing with pixel
+        // offsets.
+        int m_prePadding;
+
+        // Glyph indices in the font used to display this item. These indices
+        // are in screen order.
+        Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_glyphs;
+
+        // For each input character, this tells us the first glyph index it
+        // generated. This is the only array with size of the input chars.
+        //
+        // All offsets are from the beginning of this run. Multiple characters
+        // can generate one glyph, in which case there will be adjacent
+        // duplicates in this list. One character can also generate multiple
+        // glyphs, in which case there will be skipped indices in this list.
+        Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_logs;
+
+        // Flags and such for each glyph.
+        Vector<SCRIPT_VISATTR, UNISCRIBE_HELPER_STACK_CHARS> m_visualAttributes;
+
+        // Horizontal advances for each glyph listed above, this is basically
+        // how wide each glyph is.
+        Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_advance;
+
+        // This contains glyph offsets, from the nominal position of a glyph.
+        // It is used to adjust the positions of multiple combining characters
+        // around/above/below base characters in a context-sensitive manner so
+        // that they don't bump against each other and the base character.
+        Vector<GOFFSET, UNISCRIBE_HELPER_STACK_CHARS> m_offsets;
+
+        // Filled by a call to Justify, this is empty for nonjustified text.
+        // If nonempty, this contains the array of justify characters for each
+        // character as returned by ScriptJustify.
+        //
+        // This is the same as the advance array, but with extra space added
+        // for some characters. The difference between a glyph's |justify|
+        // width and it's |advance| width is the extra space added.
+        Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_justify;
+
+        // Sizing information for this run. This treats the entire run as a
+        // character with a preceeding advance, width, and ending advance.  The
+        // B width is the sum of the |advance| array, and the A and C widths
+        // are any extra spacing applied to each end.
+        //
+        // It is unclear from the documentation what this actually means. From
+        // experimentation, it seems that the sum of the character advances is
+        // always the sum of the ABC values, and I'm not sure what you're
+        // supposed to do with the ABC values.
+        ABC m_abc;
+
+        // Pointers to windows font data used to render this run.
+        HFONT m_hfont;
+        SCRIPT_CACHE* m_scriptCache;
+
+        // Ascent offset between the ascent of the primary font
+        // and that of the fallback font. The offset needs to be applied,
+        // when drawing a string, to align multiple runs rendered with
+        // different fonts.
+        int m_ascentOffset;
+    };
+
+    // Computes the runs_ array from the text run.
+    void fillRuns();
+
+    // Computes the shapes_ array given an runs_ array already filled in.
+    void fillShapes();
+
+    // Fills in the screen_order_ array (see below).
+    void fillScreenOrder();
+
+    // Called to update the glyph positions based on the current spacing
+    // options that are set.
+    void applySpacing();
+
+    // Normalizes all advances for spaces to the same width. This keeps windows
+    // from making spaces after Hindi characters larger, which is then
+    // inconsistent with our meaure of the width since WebKit doesn't include
+    // spaces in text-runs sent to uniscribe unless white-space:pre.
+    void adjustSpaceAdvances();
+
+    // Returns the total width of a single item.
+    int advanceForItem(int) const;
+
+    // Shapes a run (pointed to by |input|) using |hfont| first.
+    // Tries a series of fonts specified retrieved with NextWinFontData
+    // and finally a font covering characters in |*input|. A string pointed
+    // by |input| comes from ScriptItemize and is supposed to contain
+    // characters belonging to a single script aside from characters common to
+    // all scripts (e.g. space).
+    bool shape(const UChar* input, int itemLength, int numGlyphs, SCRIPT_ITEM& run, Shaping&);
+
+    // Gets Windows font data for the next best font to try in the list
+    // of fonts. When there's no more font available, returns false
+    // without touching any of out params. Need to call ResetFontIndex
+    // to start scanning of the font list from the beginning.
+    virtual bool nextWinFontData(HFONT*, SCRIPT_CACHE**, SCRIPT_FONTPROPERTIES**, int* ascent)
+    {
+        return false;
+    }
+
+    // Resets the font index to the first in the list of fonts to try after the
+    // primaryFont turns out not to work. With fontIndex reset,
+    // NextWinFontData scans fallback fonts from the beginning.
+    virtual void resetFontIndex() {}
+
+    // The input data for this run of Uniscribe. See the constructor.
+    const UChar* m_input;
+    const int m_inputLength;
+    const bool m_isRtl;
+
+    // Windows font data for the primary font. In a sense, m_logfont and m_style
+    // are redundant because m_hfont contains all the information. However,
+    // invoking GetObject, everytime we need the height and the style, is rather
+    // expensive so that we cache them. Would it be better to add getter and
+    // (virtual) setter for the height and the style of the primary font,
+    // instead of m_logfont? Then, a derived class ctor can set m_ascent,
+    // m_height and m_style if they're known. Getters for them would have to
+    // 'infer' their values from m_hfont ONLY when they're not set.
+    HFONT m_hfont;
+    SCRIPT_CACHE* m_scriptCache;
+    SCRIPT_FONTPROPERTIES* m_fontProperties;
+    int m_ascent;
+    LOGFONT m_logfont;
+    int m_style;
+
+    // Options, see the getters/setters above.
+    bool m_directionalOverride;
+    bool m_inhibitLigate;
+    int m_letterSpacing;
+    int m_spaceWidth;
+    int m_wordSpacing;
+
+    // Uniscribe breaks the text into Runs. These are one length of text that is
+    // in one script and one direction. This array is in reading order.
+    Vector<SCRIPT_ITEM, UNISCRIBE_HELPER_STACK_RUNS> m_runs;
+
+    Vector<Shaping, UNISCRIBE_HELPER_STACK_RUNS> m_shapes;
+
+    // This is a mapping between reading order and screen order for the items.
+    // Uniscribe's items array are in reading order. For right-to-left text,
+    // or mixed (although WebKit's |TextRun| should really be only one
+    // direction), this makes it very difficult to compute character offsets
+    // and positions. This list is in screen order from left to right, and
+    // gives the index into the |m_runs| and |m_shapes| arrays of each
+    // subsequent item.
+    Vector<int, UNISCRIBE_HELPER_STACK_RUNS> m_screenOrder;
+};
+
+}  // namespace WebCore
+
+#endif  // UniscribeHelper_h
diff --git a/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp b/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp
new file mode 100644 (file)
index 0000000..f801c13
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "UniscribeHelperTextRun.h"
+
+#include "ChromiumBridge.h"
+#include "Font.h"
+#include "SimpleFontData.h"
+
+namespace WebCore {
+
+UniscribeHelperTextRun::UniscribeHelperTextRun(const TextRun& run,
+                                               const Font& font)
+    : UniscribeHelper(run.characters(), run.length(), run.rtl(),
+                      font.primaryFont()->platformData().hfont(),
+                      font.primaryFont()->platformData().scriptCache(),
+                      font.primaryFont()->platformData().scriptFontProperties())
+    , m_font(&font)
+    , m_fontIndex(0)
+{
+    setDirectionalOverride(run.directionalOverride());
+    setLetterSpacing(font.letterSpacing());
+    setSpaceWidth(font.spaceWidth());
+    setWordSpacing(font.wordSpacing());
+    setAscent(font.primaryFont()->ascent());
+
+    init();
+
+    // Padding is the amount to add to make justification happen. This
+    // should be done after Init() so all the runs are already measured.
+    if (run.padding() > 0)
+        justify(run.padding());
+}
+
+UniscribeHelperTextRun::UniscribeHelperTextRun(
+    const wchar_t* input,
+    int inputLength,
+    bool isRtl,
+    HFONT hfont,
+    SCRIPT_CACHE* scriptCache,
+    SCRIPT_FONTPROPERTIES* fontProperties)
+    : UniscribeHelper(input, inputLength, isRtl, hfont,
+                      scriptCache, fontProperties)
+    , m_font(0)
+    , m_fontIndex(-1)
+{
+}
+
+void UniscribeHelperTextRun::tryToPreloadFont(HFONT font)
+{
+    // Ask the browser to get the font metrics for this font.
+    // That will preload the font and it should now be accessible
+    // from the renderer.
+    ChromiumBridge::ensureFontLoaded(font);
+}
+
+bool UniscribeHelperTextRun::nextWinFontData(
+    HFONT* hfont,
+    SCRIPT_CACHE** scriptCache,
+    SCRIPT_FONTPROPERTIES** fontProperties,
+    int* ascent)
+{
+    // This check is necessary because NextWinFontData can be called again
+    // after we already ran out of fonts. fontDataAt behaves in a strange
+    // manner when the difference between param passed and # of fonts stored in
+    // WebKit::Font is larger than one. We can avoid this check by setting
+    // font_index_ to # of elements in hfonts_ when we run out of font. In that
+    // case, we'd have to go through a couple of more checks before returning
+    // false.
+    if (m_fontIndex == -1 || !m_font)
+        return false;
+
+    // If the font data for a fallback font requested is not yet retrieved, add
+    // them to our vectors. Note that '>' rather than '>=' is used to test that
+    // condition. primaryFont is not stored in hfonts_, and friends so that
+    // indices for fontDataAt and our vectors for font data are 1 off from each
+    // other.  That is, when fully populated, hfonts_ and friends have one font
+    // fewer than what's contained in font_.
+    if (static_cast<size_t>(++m_fontIndex) > m_hfonts.size()) {
+        const FontData *fontData = m_font->fontDataAt(m_fontIndex);
+        if (!fontData) {
+            // Ran out of fonts.
+            m_fontIndex = -1;
+            return false;
+        }
+
+        // FIXME: this won't work for SegmentedFontData
+        // http://crbug.com/6425
+        const SimpleFontData* simpleFontData =
+            fontData->fontDataForCharacter(' ');
+
+        m_hfonts.append(simpleFontData->platformData().hfont());
+        m_scriptCaches.append(simpleFontData->platformData().scriptCache());
+        m_fontProperties.append(simpleFontData->platformData().scriptFontProperties());
+        m_ascents.append(simpleFontData->ascent());
+    }
+
+    *hfont = m_hfonts[m_fontIndex - 1];
+    *scriptCache = m_scriptCaches[m_fontIndex - 1];
+    *fontProperties = m_fontProperties[m_fontIndex - 1];
+    *ascent = m_ascents[m_fontIndex - 1];
+    return true;
+}
+
+void UniscribeHelperTextRun::resetFontIndex()
+{
+    m_fontIndex = 0;
+}
+
+}  // namespace WebCore
diff --git a/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h b/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h
new file mode 100644 (file)
index 0000000..b5c54a0
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UniscribeHelperTextRun_h
+#define UniscribeHelperTextRun_h
+
+#include "UniscribeHelper.h"
+
+namespace WebCore {
+
+class Font;
+class TextRun;
+
+// Wrapper around the Uniscribe helper that automatically sets it up with the
+// WebKit types we supply.
+class UniscribeHelperTextRun : public UniscribeHelper {
+public:
+    // Regular constructor used for WebCore text run processing.
+    UniscribeHelperTextRun(const TextRun&, const Font&);
+
+    // Constructor with the same interface as the gfx::UniscribeState. Using
+    // this constructor will not give you font fallback, but it will provide
+    // the ability to load fonts that may not be in the OS cache
+    // ("TryToPreloadFont") if the caller does not have a TextRun/Font.
+    UniscribeHelperTextRun(const wchar_t* input,
+                           int inputLength,
+                           bool isRtl,
+                           HFONT hfont,
+                           SCRIPT_CACHE*,
+                           SCRIPT_FONTPROPERTIES*);
+
+protected:
+    virtual void tryToPreloadFont(HFONT);
+
+private:
+    // This function retrieves the Windows font data (HFONT, etc) for the next
+    // WebKit font in the list. If the font data corresponding to font_index_
+    // has been obtained before, returns the values stored in our internal
+    // vectors (hfonts_, etc).  Otherwise, it gets next SimpleFontData from
+    // WebKit and adds them to in hfonts_ and friends so that font data can be
+    // returned quickly next time they're requested.
+    virtual bool nextWinFontData(HFONT*, SCRIPT_CACHE**, SCRIPT_FONTPROPERTIES**, int* ascent);
+    virtual void resetFontIndex();
+
+    // Reference to WebKit::Font that contains all the information about fonts
+    // we can use to render this input run of text.  It is used in
+    // NextWinFontData to retrieve Windows font data for a series of
+    // non-primary fonts.
+    //
+    // This pointer can be NULL for no font fallback handling.
+    const Font* m_font;
+
+    // It's rare that many fonts are listed in stylesheets.
+    // Four would be large enough in most cases.
+    const static size_t kNumberOfFonts = 4;
+
+    // These vectors are used to store Windows font data for non-primary fonts.
+    Vector<HFONT, kNumberOfFonts> m_hfonts;
+    Vector<SCRIPT_CACHE*, kNumberOfFonts> m_scriptCaches;
+    Vector<SCRIPT_FONTPROPERTIES*, kNumberOfFonts> m_fontProperties;
+    Vector<int, kNumberOfFonts> m_ascents;
+
+    // Index of the fallback font we're currently using for NextWinFontData.
+    int m_fontIndex;
+};
+
+}  // namespace WebCore
+
+#endif  // UniscribeHelperTextRun_h