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