Created
June 5, 2012 01:11
-
-
Save springmeyer/2871822 to your computer and use it in GitHub Desktop.
https://bug-69826-attachments.webkit.org/attachment.cgi?id=129617 linked from https://bugs.webkit.org/show_bug.cgi?id=69826
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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