summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorclaireho <chinglanho@gmail.com>2010-11-04 11:06:08 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-11-04 11:06:08 -0700
commitd27bbc21b96d36e40cd257148a939e4cbff18b26 (patch)
tree401dfe5d0445e8e6a14b86864649c10cd8135909
parent9d2b5a35cbe2528f07d9aa33b2b8cf025f266090 (diff)
parentc68e499ce88a566485062518510f31f9869f41a2 (diff)
downloadexternal_webkit-d27bbc21b96d36e40cd257148a939e4cbff18b26.zip
external_webkit-d27bbc21b96d36e40cd257148a939e4cbff18b26.tar.gz
external_webkit-d27bbc21b96d36e40cd257148a939e4cbff18b26.tar.bz2
Merge "Sync up with chromium/FontLinux.cpp"
-rw-r--r--WebCore/platform/graphics/android/FontAndroid.cpp801
1 files changed, 400 insertions, 401 deletions
diff --git a/WebCore/platform/graphics/android/FontAndroid.cpp b/WebCore/platform/graphics/android/FontAndroid.cpp
index ec98af9..b2abfcf 100644
--- a/WebCore/platform/graphics/android/FontAndroid.cpp
+++ b/WebCore/platform/graphics/android/FontAndroid.cpp
@@ -304,44 +304,19 @@ static int truncateFixedPointToInteger(HB_Fixed value)
// can call |reset| to start over again.
class TextRunWalker {
public:
- TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
- : m_font(font)
- , m_startingX(startingX)
- , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer))
- , m_iterateBackwards(m_run.rtl())
- , m_wordSpacingAdjustment(0)
- , m_padding(0)
- , m_padPerWordBreak(0)
- , m_padError(0)
- , m_letterSpacing(0)
- {
- // Do not use |run| inside this constructor. Use |m_run| instead.
-
- 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);
-
- m_item.log_clusters = new unsigned short[m_run.length()];
-
- 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();
- reset();
- }
+ TextRunWalker(const TextRun&, unsigned, const Font*);
+ ~TextRunWalker();
- ~TextRunWalker()
- {
- fastFree(m_item.font);
- deleteGlyphArrays();
- delete[] m_item.log_clusters;
- }
+ bool isWordBreak(unsigned, bool);
+ // setPadding sets a number of pixels to be distributed across the TextRun.
+ // WebKit uses this to justify text.
+ void setPadding(int);
+ void reset();
+ void setBackwardsIteration(bool);
+ // 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.
@@ -364,427 +339,451 @@ public:
// setWordAndLetterSpacing calls setWordSpacingAdjustment() and
// setLetterSpacingAdjustment() to override m_wordSpacingAdjustment
// and m_letterSpacing.
- void setWordAndLetterSpacing(int wordSpacingAdjustment,
- int letterSpacingAdjustment) {
- setWordSpacingAdjustment(wordSpacingAdjustment);
- setLetterSpacingAdjustment(letterSpacingAdjustment);
- }
-
- bool isWordBreak(unsigned i, bool isRTL)
- {
- if (isRTL)
- return i != m_item.stringLength - 1
- && isCodepointSpace(m_item.string[i])
- && !isCodepointSpace(m_item.string[i + 1]);
-
- return i && isCodepointSpace(m_item.string[i])
- && !isCodepointSpace(m_item.string[i - 1]);
- }
-
- // setPadding sets a number of pixels to be distributed across the TextRun.
- // WebKit uses this to justify text.
- void setPadding(int padding)
- {
- m_padding = padding;
- 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;
- bool isRTL = m_iterateBackwards;
-
- for (unsigned i = 0; i < m_item.stringLength; i++) {
- if (isWordBreak(i, isRTL))
- numWordBreaks++;
- }
-
- if (numWordBreaks)
- m_padPerWordBreak = m_padding / numWordBreaks;
- else
- m_padPerWordBreak = 0;
- }
-
- void reset()
- {
- if (m_iterateBackwards)
- m_indexOfNextScriptRun = m_run.length() - 1;
- else
- m_indexOfNextScriptRun = 0;
- m_offsetX = m_startingX;
- }
+ void setWordAndLetterSpacing(int wordSpacingAdjustment, int letterSpacingAdjustment);
// Set the x offset for the next script run. This affects the values in
// |xPositions|
- void setXOffsetToZero()
- {
- m_offsetX = 0;
- }
-
- bool rtl() const
- {
- return m_run.rtl();
- }
-
- void setBackwardsIteration(bool isBackwards)
- {
- m_iterateBackwards = isBackwards;
- reset();
- }
-
- // Advance to the next script run, returning false when the end of the
- // TextRun has been reached.
- bool nextScriptRun()
- {
- if (m_iterateBackwards) {
- // In right-to-left mode we need to render the shaped glyph backwards and
- // also render the script runs themselves backwards. So given a TextRun:
- // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai)
- // we render:
- // TTTTTTCAAAAAAA
- // (and the glyphs in each A, C and T section are backwards too)
- if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item,
- m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
- return false;
- } else {
- if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item,
- m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
- return false;
-
- // It is actually wrong to consider script runs at all in this code.
- // Other WebKit code (e.g. Mac) segments complex text just by finding
- // the longest span of text covered by a single font.
- // But we currently need to call hb_utf16_script_run_next anyway to fill
- // in the harfbuzz data structures to e.g. pick the correct script's shaper.
- // So we allow that to run first, then do a second pass over the range it
- // found and take the largest subregion that stays within a single font.
- const FontData* glyphData = m_font->glyphDataForCharacter(
- m_item.string[m_item.item.pos], false, false).fontData;
- int endOfRun;
- for (endOfRun = 1; endOfRun < static_cast<int>(m_item.item.length); ++endOfRun) {
- const FontData* nextGlyphData = m_font->glyphDataForCharacter(
- m_item.string[m_item.item.pos + endOfRun], false, false).fontData;
- if (nextGlyphData != glyphData)
- break;
- }
- m_item.item.length = endOfRun;
- m_indexOfNextScriptRun = m_item.item.pos + endOfRun;
- }
-
- setupFontForScriptRun();
- shapeGlyphs();
- setGlyphXPositions(rtl());
- return true;
- }
-
- const uint16_t* glyphs() const
- {
- return m_glyphs16;
- }
+ void setXOffsetToZero() { m_offsetX = 0; }
+ bool rtl() const { return m_run.rtl(); }
+ const uint16_t* glyphs() const { return m_glyphs16; }
// Return the length of the array returned by |glyphs|
- unsigned length() const
- {
- return m_item.num_glyphs;
- }
+ unsigned length() const { return m_item.num_glyphs; }
// Return the x offset for each of the glyphs. Note that this is translated
// by the current x offset and that the x offset is updated for each script
// run.
- const SkScalar* xPositions() const
- {
- return m_xPositions;
- }
+ const SkScalar* xPositions() const { return m_xPositions; }
// Get the advances (widths) for each glyph.
- const HB_Fixed* advances() const
- {
- return m_item.advances;
- }
+ const HB_Fixed* advances() const { return m_item.advances; }
// Return the width (in px) of the current script run.
- unsigned width() const
- {
- return m_pixelWidth;
- }
+ unsigned width() const { return m_pixelWidth; }
// Return the cluster log for the current script run. For example:
- // script run: f i a n c é (fi gets ligatured)
+ // script run: f i a n c é (fi gets ligatured)
// log clutrs: 0 0 1 2 3 4
// So, for each input code point, the log tells you which output glyph was
// generated for it.
- const unsigned short* logClusters() const
- {
- return m_item.log_clusters;
- }
+ const unsigned short* logClusters() const { return m_item.log_clusters; }
// return the number of code points in the current script run
- unsigned numCodePoints() const
- {
- return m_numCodePoints;
- }
+ unsigned numCodePoints() const { return m_numCodePoints; }
- const FontPlatformData* fontPlatformDataForScriptRun()
- {
+ const FontPlatformData* fontPlatformDataForScriptRun() {
return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
}
- float widthOfFullRun()
- {
- float widthSum = 0;
- while (nextScriptRun())
- widthSum += width();
+private:
+ void setupFontForScriptRun();
+ HB_FontRec* allocHarfbuzzFont();
+ void deleteGlyphArrays();
+ void createGlyphArrays(int);
+ void resetGlyphArrays();
+ void shapeGlyphs();
+ void setGlyphXPositions(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'; }
+
+ const Font* const m_font;
+ HB_ShaperItem m_item;
+ uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
+ SkScalar* m_xPositions; // A vector of x positions for each glyph.
+ ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
+ const unsigned m_startingX; // Offset in pixels of the first script run.
+ unsigned m_offsetX; // Offset in pixels to the start of the next script run.
+ unsigned m_pixelWidth; // Width (in px) of the current script run.
+ unsigned m_numCodePoints; // Code points in current script run.
+ unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays.
- return widthSum;
+ OwnPtr<TextRun> m_normalizedRun;
+ OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run.
+ const TextRun& m_run;
+ bool m_iterateBackwards;
+ 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.
+ unsigned m_letterSpacing; // pixels to be added after each glyph.
+};
+
+
+TextRunWalker::TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
+ : m_font(font)
+ , m_startingX(startingX)
+ , m_offsetX(m_startingX)
+ , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer))
+ , m_iterateBackwards(m_run.rtl())
+ , m_wordSpacingAdjustment(0)
+ , m_padding(0)
+ , m_padPerWordBreak(0)
+ , m_padError(0)
+ , m_letterSpacing(0)
+{
+ // Do not use |run| inside this constructor. Use |m_run| instead.
+
+ 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);
+
+ m_item.log_clusters = new unsigned short[m_run.length()];
+
+ 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();
+
+ reset();
+}
+
+TextRunWalker::~TextRunWalker()
+{
+ fastFree(m_item.font);
+ deleteGlyphArrays();
+ delete[] m_item.log_clusters;
+}
+
+bool TextRunWalker::isWordBreak(unsigned index, bool isRTL)
+{
+ if (!isRTL)
+ return index && isCodepointSpace(m_item.string[index])
+ && !isCodepointSpace(m_item.string[index - 1]);
+ return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index])
+ && !isCodepointSpace(m_item.string[index + 1]);
+}
+
+// setPadding sets a number of pixels to be distributed across the TextRun.
+// WebKit uses this to justify text.
+void TextRunWalker::setPadding(int padding)
+{
+ m_padding = padding;
+ 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;
+ bool isRTL = m_iterateBackwards;
+
+ for (unsigned i = 0; i < m_item.stringLength; i++) {
+ if (isWordBreak(i, isRTL))
+ numWordBreaks++;
}
-private:
- const TextRun& 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 (int16_t 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;
+ if (numWordBreaks)
+ m_padPerWordBreak = m_padding / numWordBreaks;
+ else
+ m_padPerWordBreak = 0;
+}
+
+void TextRunWalker::reset()
+{
+ if (m_iterateBackwards)
+ m_indexOfNextScriptRun = m_run.length() - 1;
+ else
+ m_indexOfNextScriptRun = 0;
+ m_offsetX = m_startingX;
+}
+
+void TextRunWalker::setBackwardsIteration(bool isBackwards)
+{
+ m_iterateBackwards = isBackwards;
+ reset();
+}
+
+// Advance to the next script run, returning false when the end of the
+// TextRun has been reached.
+bool TextRunWalker::nextScriptRun()
+{
+ if (m_iterateBackwards) {
+ // In right-to-left mode we need to render the shaped glyph backwards and
+ // also render the script runs themselves backwards. So given a TextRun:
+ // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai)
+ // we render:
+ // TTTTTTCAAAAAAA
+ // (and the glyphs in each A, C and T section are backwards too)
+ if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(),
+ m_run.length(), &m_indexOfNextScriptRun))
+ return false;
+ } else {
+ if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(),
+ m_run.length(), &m_indexOfNextScriptRun))
+ return false;
+
+ // It is actually wrong to consider script runs at all in this code.
+ // Other WebKit code (e.g. Mac) segments complex text just by finding
+ // the longest span of text covered by a single font.
+ // But we currently need to call hb_utf16_script_run_next anyway to fill
+ // in the harfbuzz data structures to e.g. pick the correct script's shaper.
+ // So we allow that to run first, then do a second pass over the range it
+ // found and take the largest subregion that stays within a single font.
+ const FontData* glyphData = m_font->glyphDataForCharacter(
+ m_item.string[m_item.item.pos], false, false).fontData;
+ unsigned endOfRun;
+ for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) {
+ const FontData* nextGlyphData = m_font->glyphDataForCharacter(
+ m_item.string[m_item.item.pos + endOfRun], false, false).fontData;
+ if (nextGlyphData != glyphData)
break;
- }
}
+ m_item.item.length = endOfRun;
+ m_indexOfNextScriptRun = m_item.item.pos + endOfRun;
+ }
- // 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();
- }
+ setupFontForScriptRun();
+ shapeGlyphs();
+ setGlyphXPositions(rtl());
- normalizedBuffer.set(new UChar[normalizedBufferLength + 1]);
+ return true;
+}
- normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(),
- normalizedBufferLength);
+float TextRunWalker::widthOfFullRun()
+{
+ float widthSum = 0;
+ while (nextScriptRun())
+ widthSum += width();
- normalizedRun.set(new TextRun(originalRun));
- normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength);
- return *normalizedRun;
- }
+ return widthSum;
+}
- void setupFontForScriptRun()
- {
- const FontData* fontData = m_font->glyphDataForCharacter(
- m_item.string[m_item.item.pos], false, false).fontData;
- const FontPlatformData& platformData =
- fontData->fontDataForCharacter(' ')->platformData();
- m_item.face = platformData.harfbuzzFace();
- void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
- m_item.font->userData = opaquePlatformData;
- }
+void TextRunWalker::setWordAndLetterSpacing(int wordSpacingAdjustment,
+ int letterSpacingAdjustment)
+{
+ setWordSpacingAdjustment(wordSpacingAdjustment);
+ setLetterSpacingAdjustment(letterSpacingAdjustment);
+}
- HB_FontRec* allocHarfbuzzFont()
- {
- HB_FontRec* font =
- reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
- memset(font, 0, sizeof(HB_FontRec));
- font->klass = &harfbuzzSkiaClass;
- font->userData = 0;
- // The values which harfbuzzSkiaClass returns are already scaled to
- // pixel units, so we just set all these to one to disable further
- // scaling.
- font->x_ppem = 1;
- font->y_ppem = 1;
- font->x_scale = 1;
- font->y_scale = 1;
-
- return font;
- }
+void TextRunWalker::setupFontForScriptRun()
+{
+ const FontData* fontData = m_font->glyphDataForCharacter(
+ m_item.string[m_item.item.pos], false, false).fontData;
+ const FontPlatformData& platformData =
+ fontData->fontDataForCharacter(' ')->platformData();
+ m_item.face = platformData.harfbuzzFace();
+ void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
+ m_item.font->userData = opaquePlatformData;
+}
- void deleteGlyphArrays()
- {
- delete[] m_item.glyphs;
- delete[] m_item.attributes;
- delete[] m_item.advances;
- delete[] m_item.offsets;
- delete[] m_glyphs16;
- delete[] m_xPositions;
- }
+HB_FontRec* TextRunWalker::allocHarfbuzzFont()
+{
+ HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
+ memset(font, 0, sizeof(HB_FontRec));
+ font->klass = &harfbuzzSkiaClass;
+ font->userData = 0;
+ // The values which harfbuzzSkiaClass returns are already scaled to
+ // pixel units, so we just set all these to one to disable further
+ // scaling.
+ font->x_ppem = 1;
+ font->y_ppem = 1;
+ font->x_scale = 1;
+ font->y_scale = 1;
+
+ return font;
+}
- void createGlyphArrays(int size)
- {
- m_item.glyphs = new HB_Glyph[size];
- m_item.attributes = new HB_GlyphAttributes[size];
- m_item.advances = new HB_Fixed[size];
- m_item.offsets = new HB_FixedPoint[size];
+void TextRunWalker::deleteGlyphArrays()
+{
+ delete[] m_item.glyphs;
+ delete[] m_item.attributes;
+ delete[] m_item.advances;
+ delete[] m_item.offsets;
+ delete[] m_glyphs16;
+ delete[] m_xPositions;
+}
- m_glyphs16 = new uint16_t[size];
- m_xPositions = new SkScalar[size];
+void TextRunWalker::createGlyphArrays(int size)
+{
+ m_item.glyphs = new HB_Glyph[size];
+ m_item.attributes = new HB_GlyphAttributes[size];
+ m_item.advances = new HB_Fixed[size];
+ m_item.offsets = new HB_FixedPoint[size];
- m_item.num_glyphs = size;
- m_glyphsArraySize = size; // Save the GlyphArrays size.
- }
+ m_glyphs16 = new uint16_t[size];
+ m_xPositions = new SkScalar[size];
- void resetGlyphArrays()
- {
- int size = m_glyphsArraySize;
-
- // All the types here don't have pointers. It is safe to reset to
- // zero unless Harfbuzz breaks the compatibility in the future.
- memset(m_item.glyphs, 0, size * sizeof(m_item.glyphs[0]));
- memset(m_item.attributes, 0, size * sizeof(m_item.attributes[0]));
- memset(m_item.advances, 0, size * sizeof(m_item.advances[0]));
- memset(m_item.offsets, 0, size * sizeof(m_item.offsets[0]));
- memset(m_glyphs16, 0, size * sizeof(m_glyphs16[0]));
- memset(m_xPositions, 0, size * sizeof(m_xPositions[0]));
-
- // Reset the array limit becuase HB_ShapeItem() overrides the
- // m_item.num_glyphs.
- m_item.num_glyphs = size;
- }
+ m_item.num_glyphs = size;
+ m_glyphsArrayCapacity = size; // Save the GlyphArrays size.
+}
- void shapeGlyphs()
- {
+void TextRunWalker::resetGlyphArrays()
+{
+ int size = m_item.num_glyphs;
+ // All the types here don't have pointers. It is safe to reset to
+ // zero unless Harfbuzz breaks the compatibility in the future.
+ memset(m_item.glyphs, 0, size * sizeof(m_item.glyphs[0]));
+ memset(m_item.attributes, 0, size * sizeof(m_item.attributes[0]));
+ memset(m_item.advances, 0, size * sizeof(m_item.advances[0]));
+ memset(m_item.offsets, 0, size * sizeof(m_item.offsets[0]));
+ memset(m_glyphs16, 0, size * sizeof(m_glyphs16[0]));
+ memset(m_xPositions, 0, size * sizeof(m_xPositions[0]));
+}
+
+void TextRunWalker::shapeGlyphs()
+{
+ // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to
+ // HB_ShapeItem() used less space than was available, the capacity of
+ // the array may be larger than the current value of m_item.num_glyphs.
+ // So, we need to reset the num_glyphs to the capacity of the array.
+ m_item.num_glyphs = m_glyphsArrayCapacity;
+ resetGlyphArrays();
+ while (!HB_ShapeItem(&m_item)) {
+ // We overflowed our arrays. Resize and retry.
+ // HB_ShapeItem fills in m_item.num_glyphs with the needed size.
+ deleteGlyphArrays();
+ createGlyphArrays(m_item.num_glyphs << 1);
resetGlyphArrays();
- while (!HB_ShapeItem(&m_item)) {
- // We overflowed our arrays. Resize and retry.
- // HB_ShapeItem fills in m_item.num_glyphs with the needed size.
- deleteGlyphArrays();
- createGlyphArrays(m_item.num_glyphs << 1);
- resetGlyphArrays();
- }
}
+}
- void setGlyphXPositions(bool isRTL)
- {
- int position = 0;
- // logClustersIndex indexes logClusters for the first (or last when
- // RTL) codepoint of the current glyph. Each time we advance a glyph
- // we skip over all the codepoints that contributed to the current
- // glyph.
- unsigned logClustersIndex = isRTL && m_item.num_glyphs ? m_item.num_glyphs - 1 : 0;
-
- for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) {
- // Glyphs are stored in logical order, but for layout purposes we
- // always go left to right.
- int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
-
- m_glyphs16[i] = m_item.glyphs[i];
- int offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
- m_xPositions[i] = SkIntToScalar(m_offsetX + position + offsetX);
-
- int advance = truncateFixedPointToInteger(m_item.advances[i]);
- // 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, isRTL)) {
- advance += 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;
- advance += toPad;
- }
+void TextRunWalker::setGlyphXPositions(bool isRTL)
+{
+ int position = 0;
+ // logClustersIndex indexes logClusters for the first (or last when
+ // RTL) codepoint of the current glyph. Each time we advance a glyph,
+ // we skip over all the codepoints that contributed to the current
+ // glyph.
+ unsigned logClustersIndex = isRTL && m_item.num_glyphs ? m_item.num_glyphs - 1 : 0;
+
+ for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) {
+ // Glyphs are stored in logical order, but for layout purposes we
+ // always go left to right.
+ int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
+
+ m_glyphs16[i] = m_item.glyphs[i];
+ int offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
+ m_xPositions[i] = SkIntToScalar(m_offsetX + position + offsetX);
+
+ int advance = truncateFixedPointToInteger(m_item.advances[i]);
+ // 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, isRTL)) {
+ advance += 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;
+ advance += toPad;
}
+ }
- // TODO We would like to add m_letterSpacing after each cluster, but
- // I don't know where the cluster information is. This is typically
- // fine for Roman languages, but breaks more complex languages
- // terribly.
- // advance += m_letterSpacing;
-
- if (isRTL) {
- while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
- logClustersIndex--;
- } else {
- while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i)
- logClustersIndex++;
- }
+ // TODO We would like to add m_letterSpacing after each cluster, but I
+ // don't know where the cluster information is. This is typically
+ // fine for Roman languages, but breaks more complex languages
+ // terribly.
+ // advance += m_letterSpacing;
- position += advance;
+ if (isRTL) {
+ while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
+ logClustersIndex--;
+ } else {
+ while (logClustersIndex < m_item.item.length
+ && logClusters()[logClustersIndex] == i)
+ logClustersIndex++;
}
- m_pixelWidth = position;
- m_offsetX += m_pixelWidth;
+
+ position += advance;
}
- static void 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 (rtl)
- character = u_charMirror(character);
- U16_APPEND(destination, position, length, character, error);
- ASSERT(!error);
- position = nextPosition;
+ m_pixelWidth = position;
+ m_offsetX += m_pixelWidth;
+}
+
+void TextRunWalker::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 (rtl)
+ character = u_charMirror(character);
+ U16_APPEND(destination, position, length, character, error);
+ ASSERT(!error);
+ position = nextPosition;
+ }
+}
+
+const TextRun& TextRunWalker::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 (int16_t 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;
}
}
- static bool isCodepointSpace(HB_UChar16 c)
- {
- // This matches the logic in RenderBlock::findNextLineBreak.
- return c == ' ' || c == '\t';
+ // 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();
}
- const Font* const m_font;
- HB_ShaperItem m_item;
- uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
- SkScalar* m_xPositions; // A vector of x positions for each glyph.
- ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
- const unsigned m_startingX; // Offset in pixels of the first script run.
- unsigned m_offsetX; // Offset in pixels to the start of the next script run.
- unsigned m_pixelWidth; // Width (in px) of the current script run.
- unsigned m_numCodePoints; // Code points in current script run.
- unsigned m_glyphsArraySize; // Current size of all the Harfbuzz arrays.
+ normalizedBuffer.set(new UChar[normalizedBufferLength + 1]);
- OwnPtr<TextRun> m_normalizedRun;
- OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run.
- const TextRun& m_run;
- bool m_iterateBackwards;
- 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.
- unsigned m_letterSpacing; // pixels to be added after each glyph.
-};
+ normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(),
+ normalizedBufferLength);
+ normalizedRun.set(new TextRun(originalRun));
+ normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength);
+ return *normalizedRun;
+}
FloatRect Font::selectionRectForComplexText(const TextRun& run,
const FloatPoint& point, int height, int from, int to) const