From 3ead8604f889b81aa2f0d98978c7211c8b3351d9 Mon Sep 17 00:00:00 2001 From: "bashi@chromium.org" Date: Fri, 24 Feb 2012 07:00:03 +0000 Subject: [PATCH] [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): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108733 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebCore/ChangeLog | 42 ++++++ Source/WebCore/PlatformBlackBerry.cmake | 1 + Source/WebCore/WebCore.gyp/WebCore.gyp | 1 + Source/WebCore/WebCore.gypi | 2 + .../harfbuzz/ComplexTextControllerHarfBuzz.cpp | 159 ++------------------ .../harfbuzz/ComplexTextControllerHarfBuzz.h | 42 +----- .../platform/graphics/harfbuzz/FontHarfBuzz.cpp | 9 +- .../graphics/harfbuzz/HarfBuzzShaperBase.cpp | 164 +++++++++++++++++++++ .../graphics/harfbuzz/HarfBuzzShaperBase.h | 80 ++++++++++ 9 files changed, 315 insertions(+), 185 deletions(-) create mode 100644 Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp create mode 100644 Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.h diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 08579a4..360f0a6 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,5 +1,47 @@ 2012-02-23 Kenichi Ishibashi + [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 + Adding WebSocket per-frame DEFLATE extension https://bugs.webkit.org/show_bug.cgi?id=77522 diff --git a/Source/WebCore/PlatformBlackBerry.cmake b/Source/WebCore/PlatformBlackBerry.cmake index cec781a..0c45be4 100644 --- a/Source/WebCore/PlatformBlackBerry.cmake +++ b/Source/WebCore/PlatformBlackBerry.cmake @@ -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 diff --git a/Source/WebCore/WebCore.gyp/WebCore.gyp b/Source/WebCore/WebCore.gyp/WebCore.gyp index 42ef505..4ed33da 100644 --- a/Source/WebCore/WebCore.gyp/WebCore.gyp +++ b/Source/WebCore/WebCore.gyp/WebCore.gyp @@ -1485,6 +1485,7 @@ ['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 diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi index a7bc359..c9569a3 100644 --- a/Source/WebCore/WebCore.gypi +++ b/Source/WebCore/WebCore.gypi @@ -3472,6 +3472,8 @@ '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', diff --git a/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp index 9a36365..f244a94 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp +++ b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp @@ -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(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& normalizedRun, OwnArrayPtr& 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 diff --git a/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h index fff36eb..6288d40 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h +++ b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.h @@ -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& normalizedRun, OwnArrayPtr& 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 m_normalizedRun; - OwnArrayPtr 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. }; diff --git a/Source/WebCore/platform/graphics/harfbuzz/FontHarfBuzz.cpp b/Source/WebCore/platform/graphics/harfbuzz/FontHarfBuzz.cpp index ba8aa2e..03f6836 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/FontHarfBuzz.cpp +++ b/Source/WebCore/platform/graphics/harfbuzz/FontHarfBuzz.cpp @@ -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* /* 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 index 0000000..29c0612 --- /dev/null +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.cpp @@ -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 +#include +#include +#include + +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 index 0000000..67bcadb --- /dev/null +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaperBase.h @@ -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 + +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 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 -- 2.7.4