Skip to content

Instantly share code, notes, and snippets.

@springmeyer
Created June 5, 2012 01:11
Show Gist options
  • Save springmeyer/2871822 to your computer and use it in GitHub Desktop.
Save springmeyer/2871822 to your computer and use it in GitHub Desktop.
Subversion Revision: 109293
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 63351bd62c5c6a902c9487b6dbecb5c97b8862c3..8ba4b071b1460a684ef6147b706d75c94655e72c 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,84 @@
+2012-02-29 Kenichi Ishibashi <bashi@chromium.org>
+
+ [Chromium] Implement font shaping with font-feature-settings on Mac
+ https://bugs.webkit.org/show_bug.cgi?id=69826
+
+ Add HarfBuzz-ng text shaper.
+ Chromium mac port uses it as secondary text shaper to support OpenType features.
+ HarfBuzz-ng is only used when -webkit-font-feature-settings is specified and
+ corresponding font is not an AAT font.
+
+ Reviewed by NOBODY (OOPS!).
+
+ No new tests. css3/font-feature-settings-rendering.html should pass on Chromium mac port.
+
+ * WebCore.gyp/WebCore.gyp: Added harfbuzz-ng dependencies.
+ * WebCore.gypi: Added harfbuzz-ng files.
+ * platform/graphics/FontPlatformData.h:
+ (FontPlatformData): Added m_harfbuzzFace.
+ * platform/graphics/cocoa/FontPlatformDataCocoa.mm:
+ (WebCore::FontPlatformData::platformDataInit): Copy m_harfbuzzFace.
+ (WebCore::FontPlatformData::platformDataAssign): Ditto.
+ (WebCore):
+ (WebCore::isAATFont):
+ (WebCore::FontPlatformData::harfbuzzFace):
+ * platform/graphics/harfbuzz/ng/HarfBuzzFace.cpp: Added.
+ (WebCore):
+ (WebCore::harfbuzzFaceCache):
+ (WebCore::HarfBuzzFace::HarfBuzzFace):
+ (WebCore::HarfBuzzFace::~HarfBuzzFace):
+ * platform/graphics/harfbuzz/ng/HarfBuzzFace.h: Added.
+ (WebCore):
+ (HarfBuzzFace):
+ (WebCore::HarfBuzzFace::create):
+ * platform/graphics/harfbuzz/ng/HarfBuzzFaceCoreText.cpp: Added.
+ (WebCore):
+ (WebCore::floatToHarfBuzzPosition):
+ (WebCore::getGlyph):
+ (WebCore::getGlyphHorizontalAdvance):
+ (WebCore::getGlyphHorizontalOrigin):
+ (WebCore::getGlyphExtents):
+ (WebCore::harfbuzzCoreTextGetFontFuncs):
+ (WebCore::releaseTableData):
+ (WebCore::harfbuzzCoreTextGetTable):
+ (WebCore::HarfBuzzFace::createFace):
+ (WebCore::HarfBuzzFace::createFont):
+ (WebCore::HarfBuzzShaper::createGlyphBufferAdvance):
+ * platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp: Added.
+ (WebCore):
+ (WebCore::harfbuzzPositionToFloat):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::xPositionForOffset):
+ (WebCore::HarfBuzzShaper::HarfBuzzShaper):
+ (WebCore::HarfBuzzShaper::~HarfBuzzShaper):
+ (WebCore::HarfBuzzShaper::setFontFeatures):
+ (WebCore::HarfBuzzShaper::shape):
+ (WebCore::HarfBuzzShaper::setupHarfBuzzRun):
+ (WebCore::HarfBuzzShaper::shapeHarfBuzzRun):
+ (WebCore::HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun):
+ (WebCore::HarfBuzzShaper::offsetForPosition):
+ (WebCore::HarfBuzzShaper::selectionRect):
+ * platform/graphics/harfbuzz/ng/HarfBuzzShaper.h: Added.
+ (WebCore):
+ (HarfBuzzShaper):
+ (WebCore::HarfBuzzShaper::totalWidth):
+ (HarfBuzzRun):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::create):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::setWidth):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::numCharacters):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::numGlyphs):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::width):
+ (WebCore::HarfBuzzShaper::HarfBuzzRun::rtl):
+ * platform/graphics/mac/FontComplexTextMac.cpp:
+ (WebCore):
+ (WebCore::preferHarfBuzz): Added.
+ (WebCore::Font::selectionRectForComplexText): Use HarfBuzzShaper if font-feature-settings exists.
+ (WebCore::Font::drawComplexText): Ditto.
+ (WebCore::Font::floatWidthForComplexText): Ditto.
+ (WebCore::Font::offsetForPositionForComplexText): Ditto.
+
2012-02-29 Shinya Kawanaka <shinyak@chromium.org>
Methods in ShadowTree and TreeScopeAdopter should be multiple shadow roots aware.
diff --git a/Source/WebCore/WebCore.gyp/WebCore.gyp b/Source/WebCore/WebCore.gyp/WebCore.gyp
index b777fe067362e9ffd2e8da9ff133aa35e4207906..9538e72f7e948ee394679674fa90f110cbeb12fc 100644
--- a/Source/WebCore/WebCore.gyp/WebCore.gyp
+++ b/Source/WebCore/WebCore.gyp/WebCore.gyp
@@ -214,6 +214,8 @@
'../platform/graphics/mac',
'../platform/mac',
'../platform/text/mac',
+ '../platform/graphics/harfbuzz',
+ '../platform/graphics/harfbuzz/ng',
],
}],
['OS=="mac" and use_skia==1', {
@@ -1528,6 +1530,7 @@
],
'dependencies': [
'webkit_system_interface',
+ '<(chromium_src_dir)/third_party/harfbuzz-ng/harfbuzz.gyp:harfbuzz-ng',
],
'actions': [
{
@@ -1608,6 +1611,12 @@
['exclude', 'platform/ScrollAnimatorNone\\.cpp$'],
['exclude', 'platform/ScrollAnimatorNone\\.h$'],
+ # Mac uses HarfBuzz-ng.
+ ['include', 'platform/graphics/harfbuzz/HarfBuzzShaperBase\\.(cpp|h)$'],
+ ['include', 'platform/graphics/harfbuzz/ng/HarfBuzzFaceCoreText\\.cpp$'],
+ ['include', 'platform/graphics/harfbuzz/ng/HarfBuzzFace\\.(cpp|h)$'],
+ ['include', 'platform/graphics/harfbuzz/ng/HarfBuzzShaper\\.(cpp|h)$'],
+
['include', '/chrome/junk\\.txt$'],
],
},{ # OS!="mac"
diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi
index ae1eac77df3a81d1fa175fc3bd68362138d9702e..d8f625f7ff95fff82565d8ad588da320347fdb1c 100644
--- a/Source/WebCore/WebCore.gypi
+++ b/Source/WebCore/WebCore.gypi
@@ -3509,6 +3509,11 @@
'platform/graphics/harfbuzz/HarfBuzzSkia.h',
'platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp',
'platform/graphics/harfbuzz/HarfBuzzShaperBase.h',
+ 'platform/graphics/harfbuzz/ng/HarfBuzzFaceCoreText.cpp',
+ 'platform/graphics/harfbuzz/ng/HarfBuzzFace.cpp',
+ 'platform/graphics/harfbuzz/ng/HarfBuzzFace.h',
+ 'platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp',
+ 'platform/graphics/harfbuzz/ng/HarfBuzzShaper.h',
'platform/graphics/mac/ColorMac.mm',
'platform/graphics/mac/ComplexTextController.cpp',
'platform/graphics/mac/ComplexTextController.h',
diff --git a/Source/WebCore/platform/graphics/FontPlatformData.h b/Source/WebCore/platform/graphics/FontPlatformData.h
index 705c58897145df8b2c2fa44277011f890e36e7fc..504b313a4134f987e549e35978d531286a781b63 100644
--- a/Source/WebCore/platform/graphics/FontPlatformData.h
+++ b/Source/WebCore/platform/graphics/FontPlatformData.h
@@ -72,6 +72,7 @@ typedef const struct __CTFont* CTFontRef;
#if PLATFORM(CHROMIUM) && OS(DARWIN)
#include "CrossProcessFontLoading.h"
+#include "HarfBuzzFace.h"
#endif
#if PLATFORM(WIN)
@@ -236,6 +237,10 @@ public:
cairo_scaled_font_t* scaledFont() const { return m_scaledFont; }
#endif
+#if PLATFORM(CHROMIUM) && OS(DARWIN)
+ HarfBuzzFace* harfbuzzFace();
+#endif
+
unsigned hash() const
{
#if PLATFORM(WIN) && !USE(CAIRO)
@@ -332,6 +337,7 @@ private:
#if PLATFORM(CHROMIUM) && OS(DARWIN)
RefPtr<MemoryActivatedFont> m_inMemoryFont;
+ RefPtr<HarfBuzzFace> m_harfbuzzFace;
#endif
bool m_isColorBitmapFont;
diff --git a/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm b/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm
index 7b136088343ae084e2e4ae407bbe089ad867c474..88d3f5201ba56193b15bcc393ba5ddca996295f9 100644
--- a/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm
+++ b/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm
@@ -84,6 +84,7 @@ void FontPlatformData::platformDataInit(const FontPlatformData& f)
#if PLATFORM(CHROMIUM) && OS(DARWIN)
m_inMemoryFont = f.m_inMemoryFont;
+ m_harfbuzzFace = f.m_harfbuzzFace;
#endif
}
@@ -100,6 +101,7 @@ const FontPlatformData& FontPlatformData::platformDataAssign(const FontPlatformD
m_CTFont = f.m_CTFont;
#if PLATFORM(CHROMIUM) && OS(DARWIN)
m_inMemoryFont = f.m_inMemoryFont;
+ m_harfbuzzFace = f.m_harfbuzzFace;
#endif
return *this;
}
@@ -276,6 +278,37 @@ CTFontRef FontPlatformData::ctFont() const
return m_CTFont.get();
}
+#if PLATFORM(CHROMIUM) && OS(DARWIN)
+static bool isAATFont(CTFontRef ctFont)
+{
+ CFDataRef table = CTFontCopyTable(ctFont, kCTFontTableMort, kCTFontOptionsDefault);
+ if (table) {
+ CFRelease(table);
+ return true;
+ }
+ table = CTFontCopyTable(ctFont, kCTFontTableMorx, kCTFontOptionsDefault);
+ if (table) {
+ CFRelease(table);
+ return true;
+ }
+ return false;
+}
+
+HarfBuzzFace* FontPlatformData::harfbuzzFace()
+{
+ CTFontRef font = ctFont();
+ // HarfBuzz can't handle AAT font
+ if (isAATFont(font))
+ return 0;
+
+ if (!m_harfbuzzFace) {
+ uint64_t uniqueID = reinterpret_cast<uintptr_t>(font);
+ m_harfbuzzFace = HarfBuzzFace::create(const_cast<FontPlatformData*>(this), uniqueID);
+ }
+ return m_harfbuzzFace.get();
+}
+#endif
+
#ifndef NDEBUG
String FontPlatformData::description() const
{
diff --git a/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFace.cpp b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFace.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..49ddbabb22bda9677a558710a51792fb5baa141e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFace.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 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 "HarfBuzzFace.h"
+
+#include "FontPlatformData.h"
+#include "hb.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+// Though we have FontCache class, which provides the cache mechanism for
+// WebKit's font objects, we also need additional caching layer for HarfBuzz
+// to reduce the memory consumption because hb_face_t should be associated with
+// underling font data (e.g. CTFontRef, FTFace).
+typedef pair<hb_face_t*, unsigned> FaceCacheEntry;
+typedef HashMap<uint64_t, FaceCacheEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > HarfBuzzFaceCache;
+
+static HarfBuzzFaceCache* harfbuzzFaceCache()
+{
+ DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfbuzzFaceCache, ());
+ return &s_harfbuzzFaceCache;
+}
+
+HarfBuzzFace::HarfBuzzFace(FontPlatformData* platformData, uint64_t uniqueID)
+ : m_platformData(platformData)
+ , m_uniqueID(uniqueID)
+{
+ HarfBuzzFaceCache::iterator result = harfbuzzFaceCache()->find(m_uniqueID);
+ if (result == harfbuzzFaceCache()->end()) {
+ m_face = createFace();
+ ASSERT(m_face);
+ harfbuzzFaceCache()->set(m_uniqueID, FaceCacheEntry(m_face, 1));
+ } else {
+ ++(result.get()->second.second);
+ m_face = result.get()->second.first;
+ }
+}
+
+HarfBuzzFace::~HarfBuzzFace()
+{
+ HarfBuzzFaceCache::iterator result = harfbuzzFaceCache()->find(m_uniqueID);
+ ASSERT(result != harfbuzzFaceCache()->end());
+ ASSERT(result.get()->second.second > 0);
+ --(result.get()->second.second);
+ if (!(result.get()->second.second)) {
+ hb_face_destroy(result.get()->second.first);
+ harfbuzzFaceCache()->remove(m_uniqueID);
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFace.h b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFace.h
new file mode 100644
index 0000000000000000000000000000000000000000..afc26b1b1f20c3ffa79b88fb68d99cad792f484b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFace.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012 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 HarfBuzzFace_h
+#define HarfBuzzFace_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+struct _hb_face_t;
+typedef _hb_face_t hb_face_t;
+struct _hb_font_t;
+typedef _hb_font_t hb_font_t;
+
+namespace WebCore {
+
+class FontPlatformData;
+
+class HarfBuzzFace : public RefCounted<HarfBuzzFace> {
+public:
+ static PassRefPtr<HarfBuzzFace> create(FontPlatformData* platformData, uint64_t uniqueID)
+ {
+ return adoptRef(new HarfBuzzFace(platformData, uniqueID));
+ }
+ ~HarfBuzzFace();
+
+ hb_font_t* createFont();
+
+private:
+ HarfBuzzFace(FontPlatformData*, uint64_t);
+
+ hb_face_t* createFace();
+
+ FontPlatformData* m_platformData;
+ uint64_t m_uniqueID;
+ hb_face_t* m_face;
+};
+
+}
+
+#endif // HarfBuzzFace_h
diff --git a/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFaceCoreText.cpp b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFaceCoreText.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..96ceb451796c772cd717c19d7ad43daa9c380759
--- /dev/null
+++ b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzFaceCoreText.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2012 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 "HarfBuzzFace.h"
+
+#include "CoreText/CoreText.h"
+#include "FontPlatformData.h"
+#include "HarfBuzzShaper.h"
+#include "SimpleFontData.h"
+
+#include "hb.h"
+
+namespace WebCore {
+
+static hb_position_t floatToHarfBuzzPosition(CGFloat value)
+{
+ return static_cast<hb_position_t>(value * (1 << 16));
+}
+
+static hb_bool_t getGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
+{
+ CTFontRef ctFont = reinterpret_cast<FontPlatformData*>(fontData)->ctFont();
+ UniChar characters[4];
+ CGGlyph cgGlyphs[4];
+ size_t length = 0;
+ U16_APPEND_UNSAFE(characters, length, unicode);
+ if (!CTFontGetGlyphsForCharacters(ctFont, characters, cgGlyphs, length))
+ return false;
+ *glyph = cgGlyphs[0];
+ return true;
+}
+
+static hb_position_t getGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
+{
+ CTFontRef ctFont = reinterpret_cast<FontPlatformData*>(fontData)->ctFont();
+ CGGlyph cgGlyph = glyph;
+ CGFloat advance = CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &cgGlyph, 0, 1);
+ return floatToHarfBuzzPosition(advance);
+}
+
+static hb_bool_t getGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
+{
+ return true;
+}
+
+static hb_bool_t getGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
+{
+ CTFontRef ctFont = reinterpret_cast<FontPlatformData*>(fontData)->ctFont();
+ CGRect cgRect;
+ CGGlyph cgGlyph = glyph;
+ if (CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontDefaultOrientation, &cgGlyph, &cgRect, 1) == CGRectNull)
+ return false;
+ extents->x_bearing = floatToHarfBuzzPosition(cgRect.origin.x);
+ extents->y_bearing = -floatToHarfBuzzPosition(cgRect.origin.y);
+ extents->width = floatToHarfBuzzPosition(cgRect.size.width);
+ extents->height = floatToHarfBuzzPosition(cgRect.size.height);
+ return true;
+}
+
+static hb_font_funcs_t* harfbuzzCoreTextGetFontFuncs()
+{
+ static hb_font_funcs_t* harfbuzzCoreTextFontFuncs = 0;
+
+ if (!harfbuzzCoreTextFontFuncs) {
+ harfbuzzCoreTextFontFuncs = hb_font_funcs_create();
+ hb_font_funcs_set_glyph_func(harfbuzzCoreTextFontFuncs, getGlyph, 0, 0);
+ hb_font_funcs_set_glyph_h_advance_func(harfbuzzCoreTextFontFuncs, getGlyphHorizontalAdvance, 0, 0);
+ hb_font_funcs_set_glyph_h_origin_func(harfbuzzCoreTextFontFuncs, getGlyphHorizontalOrigin, 0, 0);
+ hb_font_funcs_set_glyph_extents_func(harfbuzzCoreTextFontFuncs, getGlyphExtents, 0, 0);
+ hb_font_funcs_make_immutable(harfbuzzCoreTextFontFuncs);
+ }
+ return harfbuzzCoreTextFontFuncs;
+}
+
+static void releaseTableData(void* userData)
+{
+ CFDataRef cfData = reinterpret_cast<CFDataRef>(userData);
+ CFRelease(cfData);
+}
+
+static hb_blob_t* harfbuzzCoreTextGetTable(hb_face_t* face, hb_tag_t tag, void* userData)
+{
+ FontPlatformData* platformData = reinterpret_cast<FontPlatformData*>(userData);
+ CTFontRef ctFont = platformData->ctFont();
+ CFDataRef cfData = CTFontCopyTable(ctFont, tag, kCTFontTableOptionNoOptions);
+ if (!cfData)
+ return 0;
+
+ const char* data = reinterpret_cast<const char*>(CFDataGetBytePtr(cfData));
+ const size_t length = CFDataGetLength(cfData);
+ if (!data || !length)
+ return 0;
+ return hb_blob_create(data, length, HB_MEMORY_MODE_READONLY, reinterpret_cast<void*>(const_cast<__CFData*>(cfData)), releaseTableData);
+}
+
+hb_face_t* HarfBuzzFace::createFace()
+{
+ hb_face_t* face = hb_face_create_for_tables(harfbuzzCoreTextGetTable, m_platformData, 0);
+ ASSERT(face);
+ return face;
+}
+
+hb_font_t* HarfBuzzFace::createFont()
+{
+ hb_font_t* font = hb_font_create(m_face);
+ hb_font_set_funcs(font, harfbuzzCoreTextGetFontFuncs(), m_platformData, 0);
+ const float size = m_platformData->m_size;
+ hb_font_set_ppem(font, size, size);
+ const int scale = (1 << 16) * static_cast<int>(size);
+ hb_font_set_scale(font, scale, scale);
+ hb_font_make_immutable(font);
+ return font;
+}
+
+GlyphBufferAdvance HarfBuzzShaper::createGlyphBufferAdvance(float width, float height)
+{
+ return CGSizeMake(width, height);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3eed897ba6a11a70f064729bd8923c5d79257736
--- /dev/null
+++ b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2012 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 "HarfBuzzShaper.h"
+
+#include "Font.h"
+#include "SurrogatePairAwareTextIterator.h"
+#include "TextRun.h"
+#include "hb-icu.h"
+#include <unicode/normlzr.h>
+#include <unicode/uchar.h>
+#include <wtf/MathExtras.h>
+#include <wtf/Vector.h>
+#include <wtf/unicode/Unicode.h>
+
+namespace WebCore {
+
+static inline float harfbuzzPositionToFloat(hb_position_t value)
+{
+ return static_cast<float>(value) / (1 << 16);
+}
+
+HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(unsigned numCharacters, TextDirection direction, hb_buffer_t* harfbuzzBuffer)
+ : m_numCharacters(numCharacters)
+ , m_direction(direction)
+{
+ m_numGlyphs = hb_buffer_get_length(harfbuzzBuffer);
+ m_glyphs.resize(m_numGlyphs);
+ m_advances.resize(m_numGlyphs);
+ m_offsets.resize(m_numGlyphs);
+ m_glyphToCharacterIndex.resize(m_numGlyphs);
+ m_logClusters.resize(m_numCharacters);
+
+ hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(harfbuzzBuffer, 0);
+ for (unsigned i = 0; i < m_numGlyphs; ++i)
+ m_glyphToCharacterIndex[i] = infos[i].cluster;
+
+ // Fill logical clusters
+ unsigned index = 0;
+ while (index < m_numGlyphs) {
+ unsigned nextIndex = index + 1;
+ while (nextIndex < m_numGlyphs && infos[index].cluster == infos[nextIndex].cluster)
+ ++nextIndex;
+ if (rtl()) {
+ int nextCluster = nextIndex < m_numGlyphs ? infos[nextIndex].cluster : -1;
+ for (int j = infos[index].cluster; j > nextCluster; --j)
+ m_logClusters[j] = index;
+ } else {
+ unsigned nextCluster = nextIndex < m_numGlyphs ? infos[nextIndex].cluster : m_numCharacters;
+ for (unsigned j = infos[index].cluster; j < nextCluster; ++j)
+ m_logClusters[j] = index;
+ }
+ index = nextIndex;
+ }
+}
+
+void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float x, float y, float advance)
+{
+ m_glyphs[index] = glyphId;
+ m_offsets[index].set(x, y);
+ m_advances[index] = advance;
+}
+
+int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(int targetX)
+{
+ ASSERT(static_cast<unsigned>(targetX) <= m_width);
+ int currentX = 0;
+ float prevAdvance = 0;
+ for (unsigned i = 0; i < m_numGlyphs; ++i) {
+ float currentAdvance = m_advances[i] / 2.0;
+ int nextX = currentX + roundf(prevAdvance + currentAdvance);
+ if (currentX <= targetX && targetX <= nextX)
+ return m_glyphToCharacterIndex[i] + (rtl() ? 1 : 0);
+ currentX = nextX;
+ prevAdvance = currentAdvance;
+ }
+
+ return rtl() ? 0 : m_numCharacters;
+}
+
+int HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
+{
+ ASSERT(offset < m_numCharacters);
+ unsigned glyphIndex = m_logClusters[offset];
+ ASSERT(glyphIndex < m_numGlyphs);
+ float position = m_offsets[glyphIndex].x();
+ if (rtl())
+ position += m_advances[glyphIndex];
+ return roundf(position);
+}
+
+HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run)
+ : HarfBuzzShaperBase(font, run)
+ , m_startIndexOfCurrentRun(0)
+ , m_numCharactersOfCurrentRun(0)
+ , m_harfbuzzBuffer(0)
+{
+ setNormalizedBuffer();
+ setFontFeatures();
+}
+
+HarfBuzzShaper::~HarfBuzzShaper()
+{
+ if (m_harfbuzzBuffer)
+ hb_buffer_destroy(m_harfbuzzBuffer);
+}
+
+void HarfBuzzShaper::setFontFeatures()
+{
+ FontFeatureSettings* settings = m_font->fontDescription().featureSettings();
+ if (!settings)
+ return;
+
+ unsigned numFeatures = settings->size();
+ m_features.resize(numFeatures);
+ for (unsigned i = 0; i < numFeatures; ++i) {
+ const UChar* tag = settings->at(i).tag().characters();
+ m_features[i].tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
+ m_features[i].value = settings->at(i).value();
+ m_features[i].start = 0;
+ m_features[i].end = static_cast<unsigned>(-1);
+ }
+}
+
+bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer)
+{
+ m_totalWidth = 0;
+ while (setupHarfBuzzRun()) {
+ if (!shapeHarfBuzzRun())
+ return false;
+ setGlyphPositionsForHarfBuzzRun(glyphBuffer);
+ }
+
+ if (!m_harfbuzzRuns.size())
+ return false;
+
+ return true;
+}
+
+bool HarfBuzzShaper::setupHarfBuzzRun()
+{
+ m_startIndexOfCurrentRun += m_numCharactersOfCurrentRun;
+
+ // Iterate through the text to take the largest range that stays within
+ // a single font.
+ int endOfRunIndex = m_normalizedBufferLength - m_startIndexOfCurrentRun;
+ SurrogatePairAwareTextIterator iterator(m_normalizedBuffer.get() + m_startIndexOfCurrentRun, 0, endOfRunIndex, endOfRunIndex);
+ UChar32 character;
+ unsigned clusterLength = 0;
+ if (!iterator.consume(character, clusterLength))
+ return false;
+
+ m_currentFontData = m_font->glyphDataForCharacter(character, false).fontData;
+ UErrorCode errorCode = U_ZERO_ERROR;
+ UScriptCode currentScript = uscript_getScript(character, &errorCode);
+ if (U_FAILURE(errorCode))
+ return false;
+ if (currentScript == USCRIPT_INHERITED)
+ currentScript = USCRIPT_COMMON;
+ for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
+ const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
+ if (nextFontData != m_currentFontData)
+ break;
+ UScriptCode nextScript = uscript_getScript(character, &errorCode);
+ if (U_FAILURE(errorCode))
+ return false;
+ if (currentScript == nextScript || nextScript == USCRIPT_INHERITED || nextScript == USCRIPT_COMMON)
+ continue;
+ if (currentScript == USCRIPT_COMMON)
+ currentScript = nextScript;
+ else
+ break;
+ }
+ m_numCharactersOfCurrentRun = iterator.currentCharacter();
+
+ if (!m_harfbuzzBuffer) {
+ m_harfbuzzBuffer = hb_buffer_create();
+ hb_buffer_set_unicode_funcs(m_harfbuzzBuffer, hb_icu_get_unicode_funcs());
+ } else
+ hb_buffer_reset(m_harfbuzzBuffer);
+ hb_buffer_set_script(m_harfbuzzBuffer, hb_icu_script_to_script(currentScript));
+
+ // WebKit always sets direction to LTR during width calculation.
+ // We only set direction when direction is explicitly set to RTL so that
+ // preventng wrong width calculation.
+ if (m_run.rtl())
+ hb_buffer_set_direction(m_harfbuzzBuffer, HB_DIRECTION_RTL);
+
+ // Determine whether this run needs to be converted to small caps.
+ // nextScriptRun() will always send us a run of the same case, because a
+ // case change while in small-caps mode always results in different
+ // FontData, so we only need to check the first character's case.
+ if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[m_startIndexOfCurrentRun])) {
+ String upperText = String(m_normalizedBuffer.get() + m_startIndexOfCurrentRun, m_numCharactersOfCurrentRun);
+ upperText.makeUpper();
+ m_currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData;
+ hb_buffer_add_utf16(m_harfbuzzBuffer, upperText.characters(), m_numCharactersOfCurrentRun, 0, m_numCharactersOfCurrentRun);
+ } else
+ hb_buffer_add_utf16(m_harfbuzzBuffer, m_normalizedBuffer.get() + m_startIndexOfCurrentRun, m_numCharactersOfCurrentRun, 0, m_numCharactersOfCurrentRun);
+
+ return true;
+}
+
+bool HarfBuzzShaper::shapeHarfBuzzRun()
+{
+ FontPlatformData* platformData = const_cast<FontPlatformData*>(&m_currentFontData->platformData());
+ HarfBuzzFace* face = platformData->harfbuzzFace();
+ if (!face)
+ return false;
+ hb_font_t* harfbuzzFont = face->createFont();
+ hb_shape(harfbuzzFont, m_harfbuzzBuffer, m_features.size() > 0 ? m_features.data() : 0, m_features.size());
+ hb_font_destroy(harfbuzzFont);
+ m_harfbuzzRuns.append(HarfBuzzRun::create(m_numCharactersOfCurrentRun, m_run.direction(), m_harfbuzzBuffer));
+ return true;
+}
+
+void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(GlyphBuffer* glyphBuffer)
+{
+ hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(m_harfbuzzBuffer, 0);
+ hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(m_harfbuzzBuffer, 0);
+ HarfBuzzRun* currentRun = m_harfbuzzRuns.last().get();
+
+ unsigned numGlyphs = currentRun->numGlyphs();
+ float totalAdvance = 0;
+ float nextOffsetX = harfbuzzPositionToFloat(glyphPositions[0].x_offset);
+ float nextOffsetY = -harfbuzzPositionToFloat(glyphPositions[0].y_offset);
+ // HarfBuzz returns the shaping result in visual order. We need not to flip them for RTL.
+ for (size_t i = 0; i < numGlyphs; ++i) {
+ bool runEnd = i + 1 == numGlyphs;
+ uint16_t glyph = glyphInfos[i].codepoint;
+ float offsetX = nextOffsetX;
+ float offsetY = nextOffsetY;
+ float advance = harfbuzzPositionToFloat(glyphPositions[i].x_advance);
+ nextOffsetX = runEnd ? 0 : harfbuzzPositionToFloat(glyphPositions[i + 1].x_offset);
+ nextOffsetY = runEnd ? 0 : -harfbuzzPositionToFloat(glyphPositions[i + 1].y_offset);
+
+ unsigned currentCharacterIndex = m_startIndexOfCurrentRun + glyphInfos[i].cluster;
+ bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
+ float spacing = isClusterEnd ? m_letterSpacing : 0;
+
+ if (isClusterEnd && isWordEnd(currentCharacterIndex))
+ spacing += determineWordBreakSpacing();
+
+ if (m_currentFontData->isZeroWidthSpaceGlyph(glyph)) {
+ currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0);
+ if (glyphBuffer)
+ glyphBuffer->add(glyph, m_currentFontData, createGlyphBufferAdvance(0, 0));
+ continue;
+ }
+
+ advance += spacing;
+ currentRun->setGlyphAndPositions(i, glyph, totalAdvance + offsetX, offsetY, advance);
+ if (glyphBuffer) {
+ float glyphAdvanceX = advance + nextOffsetX - offsetX;
+ float glyphAdvanceY = nextOffsetY - offsetY;
+ glyphBuffer->add(glyph, m_currentFontData, createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
+ }
+
+ totalAdvance += advance;
+ }
+ currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0);
+ m_totalWidth += currentRun->width();
+}
+
+int HarfBuzzShaper::offsetForPosition(float targetX)
+{
+ int charactersSoFar = 0;
+ int currentX = 0;
+
+ if (m_run.rtl()) {
+ charactersSoFar = m_normalizedBufferLength;
+ for (int i = m_harfbuzzRuns.size() - 1; i >= 0; --i) {
+ charactersSoFar -= m_harfbuzzRuns[i]->numCharacters();
+ int nextX = currentX + m_harfbuzzRuns[i]->width();
+ if (currentX <= targetX && targetX <= nextX) {
+ // The x value in question is within this script run.
+ const unsigned index = m_harfbuzzRuns[i]->characterIndexForXPosition(targetX - currentX);
+ return charactersSoFar + index;
+ }
+ currentX = nextX;
+ }
+ } else {
+ for (unsigned i = 0; i < m_harfbuzzRuns.size(); ++i) {
+ int nextX = currentX + m_harfbuzzRuns[i]->width();
+ if (currentX <= targetX && targetX <= nextX) {
+ const unsigned index = m_harfbuzzRuns[i]->characterIndexForXPosition(targetX - currentX);
+ return charactersSoFar + index;
+ }
+ charactersSoFar += m_harfbuzzRuns[i]->numCharacters();
+ currentX = nextX;
+ }
+ }
+
+ return charactersSoFar;
+}
+
+FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to)
+{
+ int fromX = -1, toX = -1;
+ int currentX = 0;
+ // Iterate through the script runs in logical order, searching for the run covering the positions of interest.
+ for (unsigned i = 0; i < m_harfbuzzRuns.size(); ++i) {
+ int numCharacters = m_harfbuzzRuns[i]->numCharacters();
+ if (fromX == -1 && from >= 0 && from < numCharacters)
+ fromX = m_harfbuzzRuns[i]->xPositionForOffset(from) + currentX;
+ else
+ from -= numCharacters;
+
+ if (toX == -1 && to >= 0 && to < numCharacters)
+ toX = m_harfbuzzRuns[i]->xPositionForOffset(to) + currentX;
+ else
+ to -= numCharacters;
+
+ if (fromX != -1 && toX != -1)
+ break;
+ currentX += m_harfbuzzRuns[i]->width();
+ }
+
+ // The position in question might be just after the text.
+ if (fromX == -1)
+ fromX = 0;
+ if (toX == -1)
+ toX = m_run.rtl() ? 0 : m_totalWidth;
+
+ ASSERT(fromX != -1 && toX != -1);
+
+ if (fromX < toX)
+ return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
+ return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzShaper.h b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzShaper.h
new file mode 100644
index 0000000000000000000000000000000000000000..7beff5d72cdc54f8888d6c20aca1d6edb033231c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/harfbuzz/ng/HarfBuzzShaper.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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 HarfBuzzShaper_h
+#define HarfBuzzShaper_h
+
+#include "FloatPoint.h"
+#include "GlyphBuffer.h"
+#include "HarfBuzzShaperBase.h"
+#include "TextRun.h"
+#include "hb.h"
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Font;
+class SimpleFontData;
+
+class HarfBuzzShaper : public HarfBuzzShaperBase {
+public:
+ HarfBuzzShaper(const Font*, const TextRun&);
+ virtual ~HarfBuzzShaper();
+
+ bool shape(GlyphBuffer* = 0);
+ float totalWidth() { return m_totalWidth; }
+ int offsetForPosition(float targetX);
+ FloatRect selectionRect(const FloatPoint&, int height, int from, int to);
+
+private:
+ class HarfBuzzRun {
+ public:
+ static PassOwnPtr<HarfBuzzRun> create(unsigned numCharacters, TextDirection direction, hb_buffer_t* buffer)
+ {
+ return adoptPtr(new HarfBuzzRun(numCharacters, direction, buffer));
+ }
+
+ void setGlyphAndPositions(unsigned index, uint16_t glyphId, float x, float y, float);
+ void setWidth(float width) { m_width = width; }
+
+ int characterIndexForXPosition(int targetX);
+ int xPositionForOffset(unsigned offset);
+
+ unsigned numCharacters() const { return m_numCharacters; }
+ unsigned numGlyphs() const { return m_numGlyphs; }
+ float width() { return m_width; }
+
+ private:
+ HarfBuzzRun(unsigned numCharacters, TextDirection, hb_buffer_t*);
+ bool rtl() { return m_direction == RTL; }
+
+ size_t m_numCharacters;
+ unsigned m_numGlyphs;
+ TextDirection m_direction;
+ Vector<uint16_t, 256> m_glyphs;
+ Vector<float, 256> m_advances;
+ Vector<FloatPoint, 256> m_offsets;
+ Vector<uint16_t, 256> m_logClusters;
+ Vector<uint16_t, 256> m_glyphToCharacterIndex;
+ float m_width;
+ };
+
+ void setFontFeatures();
+
+ bool setupHarfBuzzRun();
+ bool shapeHarfBuzzRun();
+ void setGlyphPositionsForHarfBuzzRun(GlyphBuffer*);
+
+ GlyphBufferAdvance createGlyphBufferAdvance(float, float);
+
+ Vector<hb_feature_t, 4> m_features;
+ unsigned m_startIndexOfCurrentRun;
+ unsigned m_numCharactersOfCurrentRun;
+ const SimpleFontData* m_currentFontData;
+ hb_buffer_t* m_harfbuzzBuffer;
+ Vector<OwnPtr<HarfBuzzRun>, 16> m_harfbuzzRuns;
+
+ float m_totalWidth;
+};
+
+} // namespace WebCore
+
+#endif // HarfBuzzShaper_h
diff --git a/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp b/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp
index c3dda155a4f7fa5359d27d61ab5150fcc5a89fb8..a726dc34c2fae2df5fb6e3b4d26ae112145e3b12 100644
--- a/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp
+++ b/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp
@@ -34,13 +34,32 @@
#include "TextRun.h"
#include <wtf/MathExtras.h>
+#if PLATFORM(CHROMIUM)
+#include "HarfBuzzShaper.h"
+#endif
+
using namespace std;
namespace WebCore {
+#if PLATFORM(CHROMIUM)
+static bool preferHarfBuzz(const Font* font)
+{
+ const FontDescription& description = font->fontDescription();
+ return description.featureSettings() && description.featureSettings()->size() > 0;
+}
+#endif
+
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h,
int from, int to) const
{
+#if PLATFORM(CHROMIUM)
+ if (preferHarfBuzz(this)) {
+ HarfBuzzShaper shaper(this, run);
+ if (shaper.shape())
+ return shaper.selectionRect(point, h, from, to);
+ }
+#endif
ComplexTextController controller(this, run);
controller.advance(from);
float beforeWidth = controller.runWidthSoFar();
@@ -82,6 +101,16 @@ float Font::getGlyphsAndAdvancesForComplexText(const TextRun& run, int from, int
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
{
+#if PLATFORM(CHROMIUM)
+ if (preferHarfBuzz(this)) {
+ GlyphBuffer glyphBuffer;
+ HarfBuzzShaper shaper(this, run);
+ if (shaper.shape(&glyphBuffer)) {
+ drawGlyphBuffer(context, run, glyphBuffer, point);
+ return;
+ }
+ }
+#endif
// This glyph buffer holds our glyphs + advances + font data for each glyph.
GlyphBuffer glyphBuffer;
@@ -109,6 +138,13 @@ void Font::drawEmphasisMarksForComplexText(GraphicsContext* context, const TextR
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
{
+#if PLATFORM(CHROMIUM)
+ if (preferHarfBuzz(this)) {
+ HarfBuzzShaper shaper(this, run);
+ if (shaper.shape())
+ return shaper.totalWidth();
+ }
+#endif
ComplexTextController controller(this, run, true, fallbackFonts);
if (glyphOverflow) {
glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent()));
@@ -121,6 +157,13 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon
int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool includePartialGlyphs) const
{
+#if PLATFORM(CHROMIUM)
+ if (preferHarfBuzz(this)) {
+ HarfBuzzShaper shaper(this, run);
+ if (shaper.shape())
+ return shaper.offsetForPosition(x);
+ }
+#endif
ComplexTextController controller(this, run);
return controller.offsetForPosition(x, includePartialGlyphs);
}
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 7f1dd47ea10efa99e385348c10739a1110db1ab0..c5828b41cb09d3fceb390b789e1309fa2bb02d44 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,16 @@
+2012-02-29 Kenichi Ishibashi <bashi@chromium.org>
+
+ [Chromium] Implement font shaping with font-feature-settings on Mac
+ https://bugs.webkit.org/show_bug.cgi?id=69826
+
+ Added the expectation for sss3/font-feature-settings-rendering.html.
+
+ Reviewed by NOBODY (OOPS!).
+
+ * platform/chromium-mac/css3/font-feature-settings-rendering-expected.png: Added.
+ * platform/chromium-mac/css3/font-feature-settings-rendering-expected.txt: Added.
+ * platform/chromium/test_expectations.txt: Removed css3/font-feature-settings-rendering.html.
+
2012-02-29 Kentaro Hara <haraken@chromium.org>
Unreviewed gardening.
diff --git a/LayoutTests/platform/chromium-mac/css3/font-feature-settings-rendering-expected.png b/LayoutTests/platform/chromium-mac/css3/font-feature-settings-rendering-expected.png
new file mode 100644
index 0000000000000000000000000000000000000000..1482fb95439a18a5aa8bb243cac0ebacdb8f63eb
GIT binary patch
literal 14981
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
Y+5L0h`+kK37K|}DYhgtC+xI{IFK5%EGynhq
literal 0
HcmV?d00001
diff --git a/LayoutTests/platform/chromium-mac/css3/font-feature-settings-rendering-expected.txt b/LayoutTests/platform/chromium-mac/css3/font-feature-settings-rendering-expected.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c7be0dfee84031e4ec87a57b52b24e415815055b
--- /dev/null
+++ b/LayoutTests/platform/chromium-mac/css3/font-feature-settings-rendering-expected.txt
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderBlock {P} at (0,0) size 784x36
+ RenderText {#text} at (0,0) size 783x36
+ text run at (0,0) width 243: "Test for font-feature-settings property. "
+ text run at (243,0) width 540: "The first word of the following three words should be displayed like \"WebKit\", while"
+ text run at (0,18) width 267: "others should be displayed as black boxes."
+ RenderBlock {DIV} at (0,52) size 784x17
+ RenderText {#text} at (0,0) size 31x16
+ text run at (0,0) width 31: "WebKit"
+ RenderBlock {DIV} at (0,69) size 784x17
+ RenderText {#text} at (0,0) size 96x16
+ text run at (0,0) width 96: "WebKit"
+ RenderBlock {DIV} at (0,86) size 784x17
+ RenderText {#text} at (0,0) size 96x16
+ text run at (0,0) width 96: "WebKit"
diff --git a/LayoutTests/platform/chromium/test_expectations.txt b/LayoutTests/platform/chromium/test_expectations.txt
index 5a21c07eeb0b4218f1019474a4a877237a4f39d9..4dec80f7fc4fdfc3c52f8ce116e39bd570297c6b 100644
--- a/LayoutTests/platform/chromium/test_expectations.txt
+++ b/LayoutTests/platform/chromium/test_expectations.txt
@@ -3172,8 +3172,6 @@ BUGCR97716 : fast/clip/overflow-border-radius-combinations.html = IMAGE
// Caused by r92618
BUGWK65874 : fast/js/preventExtensions.html = TEXT
-BUGWK63796 MAC : css3/font-feature-settings-rendering.html = MISSING FAIL
-
BUGWK66417 WIN : fast/text/midword-break-before-surrogate-pair-2.html = IMAGE+TEXT
BUGWK66417 MAC : fast/text/midword-break-before-surrogate-pair-2.html = IMAGE
BUGWK66417 LINUX : fast/text/midword-break-before-surrogate-pair-2.html = IMAGE+TEXT TIMEOUT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment