Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / fonts / harfbuzz / HarfBuzzShaper.cpp
index 4ac6eb0..f4dc954 100644 (file)
 #include "config.h"
 #include "platform/fonts/harfbuzz/HarfBuzzShaper.h"
 
-#include "RuntimeEnabledFeatures.h"
 #include "hb.h"
+#include "platform/LayoutUnit.h"
+#include "platform/RuntimeEnabledFeatures.h"
 #include "platform/fonts/Character.h"
 #include "platform/fonts/Font.h"
+#include "platform/fonts/GlyphBuffer.h"
 #include "platform/fonts/harfbuzz/HarfBuzzFace.h"
 #include "platform/text/SurrogatePairAwareTextIterator.h"
 #include "platform/text/TextBreakIterator.h"
@@ -50,7 +52,7 @@
 #include <map>
 #include <string>
 
-namespace WebCore {
+namespace blink {
 
 template<typename T>
 class HarfBuzzScopedPtr {
@@ -85,12 +87,13 @@ typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap;
 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU;
 
 struct CachedShapingResults {
-    CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir);
+    CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir, const String& newLocale);
     ~CachedShapingResults();
 
     hb_buffer_t* buffer;
     Font font;
     hb_direction_t dir;
+    String locale;
     CachedShapingResultsLRU::iterator lru;
 };
 
@@ -101,10 +104,11 @@ struct CachedShapingResultsLRUNode {
     CachedShapingResultsMap::iterator entry;
 };
 
-CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData)
+CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData, const String& newLocale)
     : buffer(harfBuzzBuffer)
     , font(*fontData)
     , dir(dirData)
+    , locale(newLocale)
 {
 }
 
@@ -231,7 +235,7 @@ static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, un
     return numGraphemes < 0 ? 0 : numGraphemes;
 }
 
-inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script)
+inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, hb_direction_t direction, hb_script_t script)
     : m_fontData(fontData)
     , m_startIndex(startIndex)
     , m_numCharacters(numCharacters)
@@ -270,16 +274,6 @@ inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzB
     m_offsets.resize(m_numGlyphs);
 }
 
-inline void HarfBuzzShaper::HarfBuzzRun::copyShapeResultAndGlyphPositions(const HarfBuzzRun& run)
-{
-    m_numGlyphs = run.m_numGlyphs;
-    m_glyphs = run.m_glyphs;
-    m_advances = run.m_advances;
-    m_glyphToCharacterIndexes = run.m_glyphToCharacterIndexes;
-    m_offsets = run.m_offsets;
-    m_width = run.m_width;
-}
-
 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY)
 {
     m_glyphs[index] = glyphId;
@@ -301,6 +295,7 @@ int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
     if (targetX <= currentAdvance)
         return rtl() ? m_numCharacters : 0;
 
+    currentX = currentAdvance;
     ++glyphIndex;
     while (glyphIndex < m_numGlyphs) {
         unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1];
@@ -313,7 +308,6 @@ int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
         if (currentX <= targetX && targetX <= nextX)
             return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphIndex];
         currentX = nextX;
-        prevAdvance = currentAdvance;
         ++glyphIndex;
     }
 
@@ -363,16 +357,19 @@ static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest
         UChar32 character;
         U16_NEXT(source, position, length, character);
         // Don't normalize tabs as they are not treated as spaces for word-end.
-        if (Character::treatAsSpace(character) && character != '\t')
-            character = ' ';
+        if (run.normalizeSpace() && Character::isNormalizedCanvasSpaceCharacter(character))
+            character = space;
+        else if (Character::treatAsSpace(character) && character != characterTabulation)
+            character = space;
         else if (Character::treatAsZeroWidthSpaceInComplexScript(character))
             character = zeroWidthSpace;
+
         U16_APPEND(destination, *destinationLength, length, character, error);
         ASSERT_UNUSED(error, !error);
     }
 }
 
-HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis)
+HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis, HashSet<const SimpleFontData*>* fallbackFonts)
     : m_font(font)
     , m_normalizedBufferLength(0)
     , m_run(run)
@@ -385,6 +382,7 @@ HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmph
     , m_toIndex(m_run.length())
     , m_forTextEmphasis(forTextEmphasis)
     , m_glyphBoundingBox(std::numeric_limits<float>::max(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::max())
+    , m_fallbackFonts(fallbackFonts)
 {
     m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]);
     normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength);
@@ -392,10 +390,16 @@ HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmph
     setFontFeatures();
 }
 
-bool HarfBuzzShaper::isWordEnd(unsigned index)
+// In complex text word-spacing affects each line-break, space (U+0020) and non-breaking space (U+00A0).
+static inline bool isCodepointSpace(UChar c)
+{
+    return c == space || c == noBreakSpace || c == newlineCharacter;
+}
+
+static inline bool isWordEnd(const UChar* normalizedBuffer, unsigned index)
 {
     // This could refer a high-surrogate, but should work.
-    return index && isCodepointSpace(m_normalizedBuffer[index]);
+    return index && isCodepointSpace(normalizedBuffer[index]);
 }
 
 int HarfBuzzShaper::determineWordBreakSpacing()
@@ -429,7 +433,7 @@ void HarfBuzzShaper::setPadding(int padding)
     unsigned numWordEnds = 0;
 
     for (unsigned i = 0; i < m_normalizedBufferLength; i++) {
-        if (isWordEnd(i))
+        if (isWordEnd(m_normalizedBuffer.get(), i))
             numWordEnds++;
     }
 
@@ -518,6 +522,23 @@ void HarfBuzzShaper::setFontFeatures()
         break;
     }
 
+    static hb_feature_t hwid = { HB_TAG('h', 'w', 'i', 'd'), 1, 0, static_cast<unsigned>(-1) };
+    static hb_feature_t twid = { HB_TAG('t', 'w', 'i', 'd'), 1, 0, static_cast<unsigned>(-1) };
+    static hb_feature_t qwid = { HB_TAG('d', 'w', 'i', 'd'), 1, 0, static_cast<unsigned>(-1) };
+    switch (description.widthVariant()) {
+    case HalfWidth:
+        m_features.append(hwid);
+        break;
+    case ThirdWidth:
+        m_features.append(twid);
+        break;
+    case QuarterWidth:
+        m_features.append(qwid);
+        break;
+    case RegularWidth:
+        break;
+    }
+
     FontFeatureSettings* settings = description.featureSettings();
     if (!settings)
         return;
@@ -594,17 +615,18 @@ struct CandidateRun {
 };
 
 static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
-    size_t bufferLength, const Font* font, Vector<CandidateRun>* runs)
+    size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize)
 {
     const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength;
     SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength);
     UChar32 character;
     unsigned clusterLength = 0;
     unsigned startIndexOfCurrentRun = 0;
+
     if (!iterator.consume(character, clusterLength))
         return false;
 
-    const SimpleFontData* nextFontData = font->glyphDataForCharacter(character, false).fontData;
+    const SimpleFontData* nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData;
     UErrorCode errorCode = U_ZERO_ERROR;
     UScriptCode nextScript = uscript_getScript(character, &errorCode);
     if (U_FAILURE(errorCode))
@@ -626,7 +648,7 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
                 continue;
             }
 
-            nextFontData = font->glyphDataForCharacter(character, false).fontData;
+            nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData;
             nextScript = uscript_getScript(character, &errorCode);
             if (U_FAILURE(errorCode))
                 return false;
@@ -641,7 +663,6 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
         CandidateRun run = { character, startIndexOfCurrentRun, iterator.currentCharacter(), currentFontData, currentScript };
         runs->append(run);
 
-        currentFontData = nextFontData;
         startIndexOfCurrentRun = iterator.currentCharacter();
     } while (iterator.consume(character, clusterLength));
 
@@ -736,7 +757,7 @@ bool HarfBuzzShaper::createHarfBuzzRuns()
 {
     Vector<CandidateRun> candidateRuns;
     if (!collectCandidateRuns(m_normalizedBuffer.get(),
-        m_normalizedBufferLength, m_font, &candidateRuns))
+        m_normalizedBufferLength, m_font, &candidateRuns, m_run.normalizeSpace()))
         return false;
 
     if (!resolveCandidateRuns(candidateRuns))
@@ -767,6 +788,11 @@ static inline hb_script_t ICUScriptToHBScript(UScriptCode script)
     return hb_script_from_string(uscript_getShortName(script), -1);
 }
 
+static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir)
+{
+    return dir == RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
+}
+
 
 void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter,
     unsigned endCharacter, const SimpleFontData* fontData,
@@ -774,9 +800,12 @@ void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter,
 {
     ASSERT(endCharacter > startCharacter);
     ASSERT(script != USCRIPT_INVALID_CODE);
+    if (m_fallbackFonts)
+        m_fallbackFonts->add(fontData);
     return m_harfBuzzRuns.append(HarfBuzzRun::create(fontData,
         startCharacter, endCharacter - startCharacter,
-        m_run.direction(), ICUScriptToHBScript(script)));
+        TextDirectionToHBDirection(m_run.direction()),
+        ICUScriptToHBScript(script)));
 }
 
 static const uint16_t* toUint16(const UChar* src)
@@ -793,6 +822,9 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns()
     HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);
 
     HarfBuzzRunCache& runCache = harfBuzzRunCache();
+    const FontDescription& fontDescription = m_font->fontDescription();
+    const String& localeString = fontDescription.locale();
+    CString locale = localeString.latin1();
 
     for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
         unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i;
@@ -806,18 +838,16 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns()
         if (!face)
             return false;
 
+        hb_buffer_set_language(harfBuzzBuffer.get(), hb_language_from_string(locale.data(), locale.length()));
         hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script());
-        hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
-
-        hb_segment_properties_t props;
-        hb_buffer_get_segment_properties(harfBuzzBuffer.get(), &props);
+        hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction());
 
         const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex();
         std::wstring key(src, src + currentRun->numCharacters());
 
         CachedShapingResults* cachedResults = runCache.find(key);
         if (cachedResults) {
-            if (cachedResults->dir == props.direction && cachedResults->font == *m_font) {
+            if (cachedResults->dir == currentRun->direction() && cachedResults->font == *m_font && cachedResults->locale == localeString) {
                 currentRun->applyShapeResult(cachedResults->buffer);
                 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer);
 
@@ -833,19 +863,18 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns()
 
         // Add a space as pre-context to the buffer. This prevents showing dotted-circle
         // for combining marks at the beginning of runs.
-        static const uint16_t preContext = ' ';
+        static const uint16_t preContext = space;
         hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0);
 
-        if (m_font->fontDescription().variant() && u_islower(m_normalizedBuffer[currentRun->startIndex()])) {
+        if (fontDescription.variant() == FontVariantSmallCaps && u_islower(m_normalizedBuffer[currentRun->startIndex()])) {
             String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper();
-            currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData;
             ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, therefore upperText is 16 bit, even after we call makeUpper().
             hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.characters16()), currentRun->numCharacters(), 0, currentRun->numCharacters());
         } else {
             hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters());
         }
 
-        if (m_font->fontDescription().orientation() == Vertical)
+        if (fontDescription.orientation() == Vertical)
             face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get());
 
         HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy);
@@ -854,7 +883,7 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns()
         currentRun->applyShapeResult(harfBuzzBuffer.get());
         setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get());
 
-        runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, props.direction));
+        runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, currentRun->direction(), localeString));
 
         harfBuzzBuffer.set(hb_buffer_create());
     }
@@ -896,7 +925,7 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
         if (isClusterEnd && !Character::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
             spacing += m_letterSpacing;
 
-        if (isClusterEnd && isWordEnd(currentCharacterIndex))
+        if (isClusterEnd && isWordEnd(m_normalizedBuffer.get(), currentCharacterIndex))
             spacing += determineWordBreakSpacing();
 
         if (currentFontData->isZeroWidthSpaceGlyph(glyph)) {
@@ -942,12 +971,12 @@ void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha
             if (currentCharacterIndex >= m_toIndex)
                 m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
             else if (currentCharacterIndex >= m_fromIndex)
-                glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
+                glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY));
         } else {
             if (currentCharacterIndex < m_fromIndex)
                 m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
             else if (currentCharacterIndex < m_toIndex)
-                glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
+                glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY));
         }
     }
 }
@@ -997,8 +1026,8 @@ void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, Ha
             float glyphAdvanceX = clusterAdvance / graphemesInCluster;
             for (unsigned j = 0; j < graphemesInCluster; ++j) {
                 // Do not put emphasis marks on space, separator, and control characters.
-                GlyphBufferGlyph glyphToAdd = Character::canReceiveTextEmphasis(m_run[currentCharacterIndex]) ? 1 : 0;
-                glyphBuffer->add(glyphToAdd, currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, 0));
+                Glyph glyphToAdd = Character::canReceiveTextEmphasis(m_run[currentCharacterIndex]) ? 1 : 0;
+                glyphBuffer->add(glyphToAdd, currentRun->fontData(), glyphAdvanceX);
             }
             clusterStart = clusterEnd;
             clusterAdvance = 0;
@@ -1013,6 +1042,10 @@ bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer)
         m_startOffset = m_harfBuzzRuns.last()->offsets()[0];
         for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) {
             HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
+            if (!currentRun->hasGlyphToCharacterIndexes()) {
+                // FIXME: bug 337886, 359664
+                continue;
+            }
             FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0];
             if (m_forTextEmphasis == ForTextEmphasis)
                 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
@@ -1023,6 +1056,10 @@ bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer)
         m_startOffset = m_harfBuzzRuns.first()->offsets()[0];
         for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) {
             HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
+            if (!currentRun->hasGlyphToCharacterIndexes()) {
+                // FIXME: bug 337886, 359664
+                continue;
+            }
             FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0];
             if (m_forTextEmphasis == ForTextEmphasis)
                 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
@@ -1105,10 +1142,15 @@ FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int
     if (!foundToX)
         toX = m_run.rtl() ? 0 : m_totalWidth;
 
-    // Using floorf() and roundf() as the same as mac port.
-    if (fromX < toX)
-        return FloatRect(floorf(point.x() + fromX), point.y(), roundf(toX - fromX), height);
-    return FloatRect(floorf(point.x() + toX), point.y(), roundf(fromX - toX), height);
+    if (fromX < toX) {
+        return Font::pixelSnappedSelectionRect(
+            point.x() + fromX, point.x() + toX,
+            point.y(), height);
+    }
+
+    return Font::pixelSnappedSelectionRect(
+        point.x() + toX, point.x() + fromX,
+        point.y(), height);
 }
 
-} // namespace WebCore
+} // namespace blink