[Chromium] Add HarfBuzzShaperBase class
authorbashi@chromium.org <bashi@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Feb 2012 07:00:03 +0000 (07:00 +0000)
committerbashi@chromium.org <bashi@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Feb 2012 07:00:03 +0000 (07:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=79336

Extract a part of ComplexTextControllerHarfBuzz class as
HarfBuzzShaperBase class. This patch intends to share the code between
old HarfBuzz and HarfBuzz-ng.

Reviewed by Tony Chang.

No new tests. No behavior change. Existing tests in fast/text should pass.

* PlatformBlackBerry.cmake: Added HarfBuzzShaperBase.cpp.
* WebCore.gyp/WebCore.gyp: Added HarfBuzzShaperBase.(cpp|h).
* WebCore.gypi: Ditto.
* platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp:
(WebCore::ComplexTextController::ComplexTextController): Removed redundant arguments.
(WebCore::ComplexTextController::nextScriptRun): Use m_normalizedBuffer and m_normalizedBufferLength instead of m_run.
(WebCore::ComplexTextController::setupFontForScriptRun): Ditto.
(WebCore::ComplexTextController::setGlyphPositions): Ditto.
* platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h:
(ComplexTextController):
* platform/graphics/harfbuzz/FontHarfBuzz.cpp:
(WebCore::Font::drawComplexText): Removed redundant arguments of ComplexTextController constructor.
(WebCore::Font::floatWidthForComplexText): Ditto.
(WebCore::Font::offsetForPositionForComplexText): Ditto.
(WebCore::Font::selectionRectForComplexText): Ditto.
* platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp: Added.
(WebCore):
(WebCore::HarfBuzzShaperBase::HarfBuzzShaperBase):
(WebCore::normalizeSpacesAndMirrorChars):
(WebCore::HarfBuzzShaperBase::setNormalizedBuffer):
(WebCore::HarfBuzzShaperBase::isWordEnd):
(WebCore::HarfBuzzShaperBase::determineWordBreakSpacing):
(WebCore::HarfBuzzShaperBase::setPadding):
* platform/graphics/harfbuzz/HarfBuzzShaperBase.h: Added.
(WebCore):
(HarfBuzzShaperBase):
(WebCore::HarfBuzzShaperBase::~HarfBuzzShaperBase):
(WebCore::HarfBuzzShaperBase::isCodepointSpace):

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

Source/WebCore/ChangeLog
Source/WebCore/PlatformBlackBerry.cmake
Source/WebCore/WebCore.gyp/WebCore.gyp
Source/WebCore/WebCore.gypi
Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp
Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h
Source/WebCore/platform/graphics/harfbuzz/FontHarfBuzz.cpp
Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.h [new file with mode: 0644]

index 08579a4..360f0a6 100644 (file)
@@ -1,5 +1,47 @@
 2012-02-23  Kenichi Ishibashi  <bashi@chromium.org>
 
+        [Chromium] Add HarfBuzzShaperBase class
+        https://bugs.webkit.org/show_bug.cgi?id=79336
+
+        Extract a part of ComplexTextControllerHarfBuzz class as
+        HarfBuzzShaperBase class. This patch intends to share the code between
+        old HarfBuzz and HarfBuzz-ng.
+
+        Reviewed by Tony Chang.
+
+        No new tests. No behavior change. Existing tests in fast/text should pass.
+
+        * PlatformBlackBerry.cmake: Added HarfBuzzShaperBase.cpp.
+        * WebCore.gyp/WebCore.gyp: Added HarfBuzzShaperBase.(cpp|h).
+        * WebCore.gypi: Ditto.
+        * platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp:
+        (WebCore::ComplexTextController::ComplexTextController): Removed redundant arguments.
+        (WebCore::ComplexTextController::nextScriptRun): Use m_normalizedBuffer and m_normalizedBufferLength instead of m_run.
+        (WebCore::ComplexTextController::setupFontForScriptRun): Ditto.
+        (WebCore::ComplexTextController::setGlyphPositions): Ditto.
+        * platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h:
+        (ComplexTextController):
+        * platform/graphics/harfbuzz/FontHarfBuzz.cpp:
+        (WebCore::Font::drawComplexText): Removed redundant arguments of ComplexTextController constructor.
+        (WebCore::Font::floatWidthForComplexText): Ditto.
+        (WebCore::Font::offsetForPositionForComplexText): Ditto.
+        (WebCore::Font::selectionRectForComplexText): Ditto.
+        * platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp: Added.
+        (WebCore):
+        (WebCore::HarfBuzzShaperBase::HarfBuzzShaperBase):
+        (WebCore::normalizeSpacesAndMirrorChars):
+        (WebCore::HarfBuzzShaperBase::setNormalizedBuffer):
+        (WebCore::HarfBuzzShaperBase::isWordEnd):
+        (WebCore::HarfBuzzShaperBase::determineWordBreakSpacing):
+        (WebCore::HarfBuzzShaperBase::setPadding):
+        * platform/graphics/harfbuzz/HarfBuzzShaperBase.h: Added.
+        (WebCore):
+        (HarfBuzzShaperBase):
+        (WebCore::HarfBuzzShaperBase::~HarfBuzzShaperBase):
+        (WebCore::HarfBuzzShaperBase::isCodepointSpace):
+
+2012-02-23  Kenichi Ishibashi  <bashi@chromium.org>
+
         Adding WebSocket per-frame DEFLATE extension
         https://bugs.webkit.org/show_bug.cgi?id=77522
 
index cec781a..0c45be4 100644 (file)
@@ -45,6 +45,7 @@ LIST(APPEND WebCore_SOURCES
     platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp
     platform/graphics/harfbuzz/FontHarfBuzz.cpp
     platform/graphics/harfbuzz/FontPlatformDataHarfBuzz.cpp
+    platform/graphics/harfbuzz/HarfBuzzShaper.cpp
     platform/graphics/harfbuzz/HarfBuzzSkia.cpp
     platform/graphics/skia/FontCacheSkia.cpp
     platform/graphics/skia/GlyphPageTreeNodeSkia.cpp
index 42ef505..4ed33da 100644 (file)
             ['include', 'platform/graphics/harfbuzz/FontHarfBuzz\\.cpp$'],
             ['include', 'platform/graphics/harfbuzz/FontPlatformDataHarfBuzz\\.cpp$'],
             ['include', 'platform/graphics/harfbuzz/HarfBuzzSkia\\.cpp$'],
+            ['include', 'platform/graphics/harfbuzz/HarfBuzzShaperBase\\.(cpp|h)$'],
             ['include', 'platform/graphics/skia/SimpleFontDataSkia\\.cpp$'],
           ],
         }, { # use_x11==0
index a7bc359..c9569a3 100644 (file)
             'platform/graphics/harfbuzz/FontPlatformDataHarfBuzz.h',
             'platform/graphics/harfbuzz/HarfBuzzSkia.cpp',
             'platform/graphics/harfbuzz/HarfBuzzSkia.h',
+            'platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp',
+            'platform/graphics/harfbuzz/HarfBuzzShaperBase.h',
             'platform/graphics/mac/ColorMac.mm',
             'platform/graphics/mac/ComplexTextController.cpp',
             'platform/graphics/mac/ComplexTextController.h',
index 9a36365..f244a94 100644 (file)
@@ -52,35 +52,33 @@ static int truncateFixedPointToInteger(HB_Fixed value)
     return value >> 6;
 }
 
-ComplexTextController::ComplexTextController(const TextRun& run, int startingX, int startingY, unsigned wordSpacing, unsigned letterSpacing, unsigned padding, const Font* font)
-    : m_font(font)
-    , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer))
+ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, int startingX, int startingY)
+    : HarfBuzzShaperBase(font, run)
 {
-    // Do not use |run| inside this constructor. Use |m_run| instead.
+    NormalizeMode mode = m_run.rtl() ? NormalizeMirrorChars : DoNotNormalizeMirrorChars;
+    setNormalizedBuffer(mode);
 
     memset(&m_item, 0, sizeof(m_item));
     // We cannot know, ahead of time, how many glyphs a given script run
     // will produce. We take a guess that script runs will not produce more
     // than twice as many glyphs as there are code points plus a bit of
     // padding and fallback if we find that we are wrong.
-    createGlyphArrays((m_run.length() + 2) * 2);
+    createGlyphArrays((m_normalizedBufferLength + 2) * 2);
 
-    m_item.log_clusters = new unsigned short[m_run.length()];
+    m_item.log_clusters = new unsigned short[m_normalizedBufferLength];
 
     m_item.face = 0;
     m_item.font = allocHarfbuzzFont();
 
     m_item.item.bidiLevel = m_run.rtl();
 
-    m_item.string = m_run.characters();
-    m_item.stringLength = m_run.length();
+    m_item.string = m_normalizedBuffer.get();
+    m_item.stringLength = m_normalizedBufferLength;
 
     reset(startingX);
     m_startingY = startingY;
 
-    setWordSpacingAdjustment(wordSpacing);
-    setLetterSpacingAdjustment(letterSpacing);
-    setPadding(padding);
+    setPadding(m_run.expansion());
 }
 
 ComplexTextController::~ComplexTextController()
@@ -90,59 +88,6 @@ ComplexTextController::~ComplexTextController()
     delete[] m_item.log_clusters;
 }
 
-bool ComplexTextController::isWordBreak(unsigned index)
-{
-    return index && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index - 1]);
-}
-
-int ComplexTextController::determineWordBreakSpacing(unsigned logClustersIndex)
-{
-    int wordBreakSpacing = 0;
-    // The first half of the conjunction works around the case where
-    // output glyphs aren't associated with any codepoints by the
-    // clusters log.
-    if (logClustersIndex < m_item.item.length
-        && isWordBreak(m_item.item.pos + logClustersIndex)) {
-        wordBreakSpacing = m_wordSpacingAdjustment;
-
-        if (m_padding > 0) {
-            int toPad = roundf(m_padPerWordBreak + m_padError);
-            m_padError += m_padPerWordBreak - toPad;
-
-            if (m_padding < toPad)
-                toPad = m_padding;
-            m_padding -= toPad;
-            wordBreakSpacing += toPad;
-        }
-    }
-    return wordBreakSpacing;
-}
-
-// setPadding sets a number of pixels to be distributed across the TextRun.
-// WebKit uses this to justify text.
-void ComplexTextController::setPadding(int padding)
-{
-    m_padding = padding;
-    m_padError = 0;
-    if (!m_padding)
-        return;
-
-    // If we have padding to distribute, then we try to give an equal
-    // amount to each space. The last space gets the smaller amount, if
-    // any.
-    unsigned numWordBreaks = 0;
-
-    for (unsigned i = 0; i < m_item.stringLength; i++) {
-        if (isWordBreak(i))
-            numWordBreaks++;
-    }
-
-    if (numWordBreaks)
-        m_padPerWordBreak = m_padding / numWordBreaks;
-    else
-        m_padPerWordBreak = 0;
-}
-
 void ComplexTextController::reset(int offset)
 {
     m_indexOfNextScriptRun = 0;
@@ -166,9 +111,9 @@ void ComplexTextController::setupForRTL()
 bool ComplexTextController::nextScriptRun()
 {
     // Ensure we're not pointing at the small caps buffer.
-    m_item.string = m_run.characters();
+    m_item.string = m_normalizedBuffer.get();
 
-    if (!hb_utf16_script_run_next(0, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
+    if (!hb_utf16_script_run_next(0, &m_item.item, m_normalizedBuffer.get(), m_normalizedBufferLength, &m_indexOfNextScriptRun))
         return false;
 
     // It is actually wrong to consider script runs at all in this code.
@@ -254,7 +199,7 @@ void ComplexTextController::setupFontForScriptRun()
     // 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_item.string[m_item.item.pos])) {
-        m_smallCapsString = String(m_run.data(m_item.item.pos), m_item.item.length);
+        m_smallCapsString = String(m_normalizedBuffer.get() + m_item.item.pos, m_item.item.length);
         m_smallCapsString.makeUpper();
         m_item.string = m_smallCapsString.characters();
         m_item.item.pos = 0;
@@ -346,16 +291,17 @@ void ComplexTextController::setGlyphPositions(bool isRTL)
 
     // logClustersIndex indexes logClusters for the first codepoint of the current glyph.
     // Each time we advance a glyph, we skip over all the codepoints that contributed to the current glyph.
-    int logClustersIndex = 0;
+    size_t logClustersIndex = 0;
 
     // Iterate through the glyphs in logical order, flipping for RTL where necessary.
     // Glyphs are positioned starting from m_offsetX; in RTL mode they go leftwards from there.
     for (size_t i = 0; i < m_item.num_glyphs; ++i) {
-        while (static_cast<unsigned>(logClustersIndex) < m_item.item.length && m_item.log_clusters[logClustersIndex] < i)
+        while (logClustersIndex < m_item.item.length && m_item.log_clusters[logClustersIndex] < i)
             logClustersIndex++;
 
         // If the current glyph is just after a space, add in the word spacing.
-        position += determineWordBreakSpacing(logClustersIndex);
+        if (logClustersIndex < m_item.item.length && isWordEnd(m_item.item.pos + logClustersIndex))
+            position += determineWordBreakSpacing();
 
         m_glyphs16[i] = m_item.glyphs[i];
         double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
@@ -380,79 +326,6 @@ void ComplexTextController::setGlyphPositions(bool isRTL)
     m_offsetX += m_pixelWidth * rtlFlip;
 }
 
-void ComplexTextController::normalizeSpacesAndMirrorChars(const UChar* source, bool rtl, UChar* destination, int length)
-{
-    int position = 0;
-    bool error = false;
-    // Iterate characters in source and mirror character if needed.
-    while (position < length) {
-        UChar32 character;
-        int nextPosition = position;
-        U16_NEXT(source, nextPosition, length, character);
-        if (Font::treatAsSpace(character))
-            character = ' ';
-        else if (Font::treatAsZeroWidthSpace(character))
-            character = zeroWidthSpace;
-        else if (rtl)
-            character = u_charMirror(character);
-        U16_APPEND(destination, position, length, character, error);
-        ASSERT_UNUSED(error, !error);
-        position = nextPosition;
-    }
-}
-
-const TextRun& ComplexTextController::getNormalizedTextRun(const TextRun& originalRun, OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer)
-{
-    // Normalize the text run in three ways:
-    // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
-    // (U+0300..) are used in the run. This conversion is necessary since most OpenType
-    // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
-    // their GSUB tables.
-    //
-    // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
-    // the API returns FALSE (= not normalized) for complex runs that don't require NFC
-    // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
-    // Harfbuzz will do the same thing for us using the GSUB table.
-    // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
-    // for characters like '\n' otherwise.
-    // 3) Convert mirrored characters such as parenthesis for rtl text.
-
-    // Convert to NFC form if the text has diacritical marks.
-    icu::UnicodeString normalizedString;
-    UErrorCode error = U_ZERO_ERROR;
-
-    for (int i = 0; i < originalRun.length(); ++i) {
-        UChar ch = originalRun[i];
-        if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
-            icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(),
-                                       originalRun.length()), UNORM_NFC, 0 /* no options */,
-                                       normalizedString, error);
-            if (U_FAILURE(error))
-                return originalRun;
-            break;
-        }
-    }
-
-    // Normalize space and mirror parenthesis for rtl text.
-    int normalizedBufferLength;
-    const UChar* sourceText;
-    if (normalizedString.isEmpty()) {
-        normalizedBufferLength = originalRun.length();
-        sourceText = originalRun.characters();
-    } else {
-        normalizedBufferLength = normalizedString.length();
-        sourceText = normalizedString.getBuffer();
-    }
-
-    normalizedBuffer = adoptArrayPtr(new UChar[normalizedBufferLength + 1]);
-
-    normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(), normalizedBufferLength);
-
-    normalizedRun = adoptPtr(new TextRun(originalRun));
-    normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength);
-    return *normalizedRun;
-}
-
 int ComplexTextController::glyphIndexForXPositionInScriptRun(int targetX) const
 {
     // Iterate through the glyphs in logical order, seeing whether targetX falls between the previous
index fff36eb..6288d40 100644 (file)
@@ -31,6 +31,7 @@
 #ifndef ComplexTextControllerHarfBuzz_h
 #define ComplexTextControllerHarfBuzz_h
 
+#include "HarfBuzzShaperBase.h"
 #include "HarfBuzzSkia.h"
 #include "SkPoint.h"
 #include "SkScalar.h"
@@ -63,32 +64,17 @@ class SimpleFontData;
 // Once you have setup the object, call |nextScriptRun| to get the first script
 // run. This will return false when the iteration is complete. At any time you
 // can call |reset| to start over again.
-class ComplexTextController {
+class ComplexTextController : public HarfBuzzShaperBase {
 public:
-    ComplexTextController(const TextRun&, int startingX, int startingY, unsigned wordSpacing, unsigned letterSpacing, unsigned padding, const Font*);
-    ~ComplexTextController();
-
-    bool isWordBreak(unsigned);
-    int determineWordBreakSpacing(unsigned);
-    // setPadding sets a number of pixels to be distributed across the TextRun.
-    // WebKit uses this to justify text.
-    void setPadding(int);
+    ComplexTextController(const Font*, const TextRun&, int startingX, int startingY);
+    virtual ~ComplexTextController();
+
     void reset(int offset);
     // Advance to the next script run, returning false when the end of the
     // TextRun has been reached.
     bool nextScriptRun();
     float widthOfFullRun();
 
-    // setWordSpacingAdjustment sets a delta (in pixels) which is applied at
-    // each word break in the TextRun.
-    void setWordSpacingAdjustment(int wordSpacingAdjustment) { m_wordSpacingAdjustment = wordSpacingAdjustment; }
-
-    // setLetterSpacingAdjustment sets an additional number of pixels that is
-    // added to the advance after each output cluster. This matches the behaviour
-    // of WidthIterator::advance.
-    void setLetterSpacingAdjustment(int letterSpacingAdjustment) { m_letterSpacing = letterSpacingAdjustment; }
-    int letterSpacing() const { return m_letterSpacing; }
-
     void setupForRTL();
     bool rtl() const { return m_run.rtl(); }
     const uint16_t* glyphs() const { return m_glyphs16; }
@@ -126,15 +112,8 @@ private:
     void shapeGlyphs();
     void setGlyphPositions(bool);
 
-    static void normalizeSpacesAndMirrorChars(const UChar* source, bool rtl, UChar* destination, int length);
-    static const TextRun& getNormalizedTextRun(const TextRun& originalRun, OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer);
-
-    // This matches the logic in RenderBlock::findNextLineBreak
-    static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; }
-
     int glyphIndexForXPositionInScriptRun(int targetX) const;
 
-    const Font* const m_font;
     const SimpleFontData* m_currentFontData;
     HB_ShaperItem m_item;
     uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
@@ -145,17 +124,6 @@ private:
     unsigned m_pixelWidth; // Width (in px) of the current script run.
     unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays.
 
-    OwnPtr<TextRun> m_normalizedRun;
-    OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run.
-    const TextRun& m_run;
-    int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break.
-    float m_padding; // pixels to be distributed over the line at word breaks.
-    float m_padPerWordBreak; // pixels to be added to each word break.
-    float m_padError; // |m_padPerWordBreak| might have a fractional component.
-                      // Since we only add a whole number of padding pixels at
-                      // each word break we accumulate error. This is the
-                      // number of pixels that we are behind so far.
-    int m_letterSpacing; // pixels to be added after each glyph.
     String m_smallCapsString; // substring of m_run converted to small caps.
 };
 
index ba8aa2e..03f6836 100644 (file)
@@ -195,8 +195,7 @@ void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
         setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
     }
 
-    ComplexTextController controller(run, point.x(), point.y(), wordSpacing(), letterSpacing(), run.expansion(), this);
-
+    ComplexTextController controller(this, run, point.x(), point.y());
     if (run.rtl())
         controller.setupForRTL();
 
@@ -228,7 +227,7 @@ void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const
 
 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const
 {
-    ComplexTextController controller(run, 0, 0, wordSpacing(), letterSpacing(), run.expansion(), this);
+    ComplexTextController controller(this, run, 0, 0);
     return controller.widthOfFullRun();
 }
 
@@ -242,7 +241,7 @@ int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat,
 
     // (Mac code ignores includePartialGlyphs, and they don't know what it's
     // supposed to do, so we just ignore it as well.)
-    ComplexTextController controller(run, 0, 0, wordSpacing(), letterSpacing(), run.expansion(), this);
+    ComplexTextController controller(this, run, 0, 0);
     if (run.rtl())
         controller.setupForRTL();
     return controller.offsetForPosition(targetX);
@@ -253,7 +252,7 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run,
                                             const FloatPoint& point, int height,
                                             int from, int to) const
 {
-    ComplexTextController controller(run, 0, 0, wordSpacing(), letterSpacing(), run.expansion(), this);
+    ComplexTextController controller(this, run, 0, 0);
     if (run.rtl())
         controller.setupForRTL();
     return controller.selectionRect(point, height, from, to);
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp
new file mode 100644 (file)
index 0000000..29c0612
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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 "HarfBuzzShaperBase.h"
+
+#include "Font.h"
+#include <unicode/normlzr.h>
+#include <unicode/uchar.h>
+#include <wtf/MathExtras.h>
+#include <wtf/unicode/Unicode.h>
+
+namespace WebCore {
+
+HarfBuzzShaperBase::HarfBuzzShaperBase(const Font* font, const TextRun& run)
+    : m_font(font)
+    , m_run(run)
+    , m_wordSpacingAdjustment(font->wordSpacing())
+    , m_letterSpacing(font->letterSpacing())
+{
+}
+
+static void normalizeSpacesAndMirrorChars(const UChar* source, UChar* destination, int length, HarfBuzzShaperBase::NormalizeMode normalizeMode)
+{
+    int position = 0;
+    bool error = false;
+    // Iterate characters in source and mirror character if needed.
+    while (position < length) {
+        UChar32 character;
+        int nextPosition = position;
+        U16_NEXT(source, nextPosition, length, character);
+        if (Font::treatAsSpace(character))
+            character = ' ';
+        else if (Font::treatAsZeroWidthSpace(character))
+            character = zeroWidthSpace;
+        else if (normalizeMode == HarfBuzzShaperBase::NormalizeMirrorChars)
+            character = u_charMirror(character);
+        U16_APPEND(destination, position, length, character, error);
+        ASSERT(!error);
+        position = nextPosition;
+    }
+}
+
+void HarfBuzzShaperBase::setNormalizedBuffer(NormalizeMode normalizeMode)
+{
+    // Normalize the text run in three ways:
+    // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
+    // (U+0300..) are used in the run. This conversion is necessary since most OpenType
+    // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
+    // their GSUB tables.
+    //
+    // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
+    // the API returns FALSE (= not normalized) for complex runs that don't require NFC
+    // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
+    // HarfBuzz will do the same thing for us using the GSUB table.
+    // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
+    // for characters like '\n' otherwise.
+    // 3) Convert mirrored characters such as parenthesis for rtl text.
+
+    // Convert to NFC form if the text has diacritical marks.
+    icu::UnicodeString normalizedString;
+    UErrorCode error = U_ZERO_ERROR;
+
+    for (int i = 0; i < m_run.length(); ++i) {
+        UChar ch = m_run[i];
+        if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
+            icu::Normalizer::normalize(icu::UnicodeString(m_run.characters(),
+                                       m_run.length()), UNORM_NFC, 0 /* no options */,
+                                       normalizedString, error);
+            if (U_FAILURE(error))
+                normalizedString.remove();
+            break;
+        }
+    }
+
+    const UChar* sourceText;
+    if (normalizedString.isEmpty()) {
+        m_normalizedBufferLength = m_run.length();
+        sourceText = m_run.characters();
+    } else {
+        m_normalizedBufferLength = normalizedString.length();
+        sourceText = normalizedString.getBuffer();
+    }
+
+    m_normalizedBuffer = adoptArrayPtr(new UChar[m_normalizedBufferLength + 1]);
+    normalizeSpacesAndMirrorChars(sourceText, m_normalizedBuffer.get(), m_normalizedBufferLength, normalizeMode);
+}
+
+bool HarfBuzzShaperBase::isWordEnd(unsigned index)
+{
+    // This could refer a high-surrogate, but should work.
+    return index && isCodepointSpace(m_normalizedBuffer[index]) && !isCodepointSpace(m_normalizedBuffer[index - 1]);
+}
+
+int HarfBuzzShaperBase::determineWordBreakSpacing()
+{
+    int wordBreakSpacing = m_wordSpacingAdjustment;
+
+    if (m_padding > 0) {
+        int toPad = roundf(m_padPerWordBreak + m_padError);
+        m_padError += m_padPerWordBreak - toPad;
+
+        if (m_padding < toPad)
+            toPad = m_padding;
+        m_padding -= toPad;
+        wordBreakSpacing += toPad;
+    }
+    return wordBreakSpacing;
+}
+
+// setPadding sets a number of pixels to be distributed across the TextRun.
+// WebKit uses this to justify text.
+void HarfBuzzShaperBase::setPadding(int padding)
+{
+    m_padding = padding;
+    m_padError = 0;
+    if (!m_padding)
+        return;
+
+    // If we have padding to distribute, then we try to give an equal
+    // amount to each space. The last space gets the smaller amount, if
+    // any.
+    unsigned numWordEnds = 0;
+
+    for (unsigned i = 0; i < m_normalizedBufferLength; i++) {
+        if (isWordEnd(i))
+            numWordEnds++;
+    }
+
+    if (numWordEnds)
+        m_padPerWordBreak = m_padding / numWordEnds;
+    else
+        m_padPerWordBreak = 0;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.h b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.h
new file mode 100644 (file)
index 0000000..67bcadb
--- /dev/null
@@ -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.
+ */
+
+#ifndef HarfBuzzShaperBase_h
+#define HarfBuzzShaperBase_h
+
+#include "TextRun.h"
+#include <wtf/OwnArrayPtr.h>
+
+namespace WebCore {
+
+class Font;
+
+class HarfBuzzShaperBase {
+public:
+    enum NormalizeMode {
+        DoNotNormalizeMirrorChars,
+        NormalizeMirrorChars
+    };
+
+    HarfBuzzShaperBase(const Font*, const TextRun&);
+    virtual ~HarfBuzzShaperBase() { }
+
+protected:
+    void setNormalizedBuffer(NormalizeMode = DoNotNormalizeMirrorChars);
+
+    bool isWordEnd(unsigned);
+    int determineWordBreakSpacing();
+    // setPadding sets a number of pixels to be distributed across the TextRun.
+    // WebKit uses this to justify text.
+    void setPadding(int);
+
+    // This matches the logic in RenderBlock::findNextLineBreak
+    static bool isCodepointSpace(UChar c) { return c == ' ' || c == '\t'; }
+
+    const Font* m_font;
+    OwnArrayPtr<UChar> m_normalizedBuffer;
+    unsigned m_normalizedBufferLength;
+    const TextRun& m_run;
+
+    int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break.
+    float m_padding; // pixels to be distributed over the line at word breaks.
+    float m_padPerWordBreak; // pixels to be added to each word break.
+    float m_padError; // |m_padPerWordBreak| might have a fractional component.
+                      // Since we only add a whole number of padding pixels at
+                      // each word break we accumulate error. This is the
+                      // number of pixels that we are behind so far.
+    int m_letterSpacing; // pixels to be added after each glyph.
+};
+
+} // namespace WebCore
+
+#endif // HarfBuzzShaperBase_h