#include "config.h"
#include "Font.h"
#include "CharacterNames.h"
#include "FloatRect.h"
#include "FontCache.h"
#include "FontFallbackList.h"
#include "IntPoint.h"
#include "GlyphBuffer.h"
#include <wtf/unicode/Unicode.h>
#include <wtf/MathExtras.h>
#if USE(ICU_UNICODE)
#include <unicode/unorm.h>
#endif
using namespace WTF;
using namespace Unicode;
namespace WebCore {
const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8;
const uint8_t Font::gRoundingHackCharacterTable[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1 , 1 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
Font::CodePath Font::codePath = Auto;
struct WidthIterator {
WidthIterator(const Font* font, const TextRun& run);
void advance(int to, GlyphBuffer* glyphBuffer = 0);
bool advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer = 0);
const Font* m_font;
const TextRun& m_run;
int m_end;
unsigned m_currentCharacter;
float m_runWidthSoFar;
float m_runHeightSoFar;
float m_padding;
float m_padPerSpace;
float m_finalRoundingWidth;
private:
UChar32 normalizeVoicingMarks(int currentCharacter);
};
WidthIterator::WidthIterator(const Font* font, const TextRun& run)
: m_font(font)
, m_run(run)
, m_end(run.length())
, m_currentCharacter(0)
, m_runWidthSoFar(0)
, m_runHeightSoFar(0)
, m_finalRoundingWidth(0)
{
m_padding = m_run.padding();
if (!m_padding)
m_padPerSpace = 0;
else {
float numSpaces = 0;
for (int i = 0; i < run.length(); i++)
if (Font::treatAsSpace(m_run[i]))
numSpaces++;
if (numSpaces == 0)
m_padPerSpace = 0;
else
m_padPerSpace = ceilf(m_run.padding() / numSpaces);
}
}
void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
{
if (offset > m_end)
offset = m_end;
int currentCharacter = m_currentCharacter;
const UChar* cp = m_run.data(currentCharacter);
bool rtl = m_run.rtl();
bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m_padding;
float runWidthSoFar = m_runWidthSoFar;
float lastRoundingWidth = m_finalRoundingWidth;
while (currentCharacter < offset) {
UChar32 c = *cp;
unsigned clusterLength = 1;
if (c >= 0x3041) {
if (c <= 0x30FE) {
UChar32 normalized = normalizeVoicingMarks(currentCharacter);
if (normalized) {
c = normalized;
clusterLength = 2;
}
} else if (U16_IS_SURROGATE(c)) {
if (!U16_IS_SURROGATE_LEAD(c))
break;
if (currentCharacter + 1 >= m_run.length())
break;
UChar low = cp[1];
if (!U16_IS_TRAIL(low))
break;
c = U16_GET_SUPPLEMENTARY(c, low);
clusterLength = 2;
}
}
#define HANGUL_SBASE 0xAC00
#define HANGUL_LBASE 0x1100
#define HANGUL_VBASE 0x1161
#define HANGUL_TBASE 0x11A7
#define HANGUL_SCOUNT 11172
#define HANGUL_LCOUNT 19
#define HANGUL_VCOUNT 21
#define HANGUL_TCOUNT 28
#define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT)
else if (c >= 0x41 && c <= 0x7a && currentCharacter + 1 < m_run.length()) {
UChar diacritical = cp[1];
if (diacritical >= 0x300 && diacritical <= 0x36f) {
UniChar characters[2];
characters[0] = c;
characters[1] = diacritical;
CFMutableStringRef string = (CFMutableStringRef)CFStringCreateMutable(NULL, 2);
CFStringAppendCharacters(string, characters, 2);
CFStringNormalize(string, kCFStringNormalizationFormKC);
if (CFStringGetLength(string) == 1) {
c = CFStringGetCharacterAtIndex (string, 0);
clusterLength = 2;
}
CFRelease (string);
}
} else if ((c >= HANGUL_LBASE) && (c < (HANGUL_LBASE + 0xFF))) { int8_t lIndex = c - HANGUL_LBASE;
if ((currentCharacter + 1 < m_run.length()) && (0 <= lIndex) && (lIndex <= HANGUL_LCOUNT)) {
int16_t vIndex = cp[1] - HANGUL_VBASE;
if ((vIndex >= 0) && (vIndex <= HANGUL_VCOUNT)) {
int16_t tIndex = 0;
clusterLength = 2;
if (currentCharacter + 2 < m_run.length()) {
tIndex = cp[2] - HANGUL_TBASE;
if ((tIndex < 0) || (tIndex > HANGUL_TCOUNT)) {
tIndex = 0;
} else {
clusterLength = 3;
}
}
c = (lIndex * HANGUL_VCOUNT + vIndex) * HANGUL_TCOUNT + tIndex + HANGUL_SBASE;
}
}
}
else if (c == 0x200E || c == 0x200F) {
c = 0x200B; }
const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl);
Glyph glyph = glyphData.glyph;
const SimpleFontData* fontData = glyphData.fontData;
ASSERT(fontData);
float fontLineSpacing = fontData->lineSpacing();
if (fontLineSpacing > m_runHeightSoFar)
m_runHeightSoFar = fontLineSpacing;
float width;
if (c == '\t' && m_run.allowTabs()) {
float tabWidth = m_font->tabWidth();
width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth);
} else {
width = fontData->widthForGlyph(glyph);
if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding())
width = fontData->m_adjustedSpaceWidth;
}
if (hasExtraSpacing && !m_run.spacingDisabled()) {
if (width && m_font->letterSpacing())
width += m_font->letterSpacing();
if (Font::treatAsSpace(c)) {
if (m_padding) {
if (m_padding < m_padPerSpace) {
width += m_padding;
m_padding = 0;
} else {
width += m_padPerSpace;
m_padding -= m_padPerSpace;
}
}
if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing())
width += m_font->wordSpacing();
}
}
cp += clusterLength;
currentCharacter += clusterLength;
float oldWidth = width;
if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c))
width = ceilf(width);
if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp))
|| (m_run.applyRunRounding() && currentCharacter >= m_end)) {
float totalWidth = runWidthSoFar + width;
width += ceilf(totalWidth) - totalWidth;
}
runWidthSoFar += width;
if (glyphBuffer)
glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
lastRoundingWidth = width - oldWidth;
}
m_currentCharacter = currentCharacter;
m_runWidthSoFar = runWidthSoFar;
m_finalRoundingWidth = lastRoundingWidth;
}
bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer)
{
glyphBuffer->clear();
advance(m_currentCharacter + 1, glyphBuffer);
float w = 0;
for (int i = 0; i < glyphBuffer->size(); ++i)
w += glyphBuffer->advanceAt(i);
width = w;
return !glyphBuffer->isEmpty();
}
UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter)
{
if (currentCharacter + 1 < m_end) {
if (combiningClass(m_run[currentCharacter + 1]) == hiraganaKatakanaVoicingMarksCombiningClass) {
#if USE(ICU_UNICODE)
UChar normalizedCharacters[2] = { 0, 0 };
UErrorCode uStatus = U_ZERO_ERROR;
int32_t resultLength = unorm_normalize(m_run.data(currentCharacter), 2,
UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus);
if (resultLength == 1 && uStatus == 0)
return normalizedCharacters[0];
#elif USE(QT4_UNICODE)
QString tmp(reinterpret_cast<const QChar*>(m_run.data(currentCharacter)), 2);
QString res = tmp.normalized(QString::NormalizationForm_C, QChar::Unicode_3_2);
if (res.length() == 1)
return res.at(0).unicode();
#endif
}
}
return 0;
}
Font::Font()
: m_pageZero(0)
, m_cachedPrimaryFont(0)
, m_letterSpacing(0)
, m_wordSpacing(0)
, m_isPlatformFont(false)
{
}
Font::Font(const FontDescription& fd, float letterSpacing, float wordSpacing)
: m_fontDescription(fd)
, m_pageZero(0)
, m_cachedPrimaryFont(0)
, m_letterSpacing(letterSpacing)
, m_wordSpacing(wordSpacing)
, m_isPlatformFont(false)
{
}
Font::Font(const FontPlatformData& fontData, bool isPrinterFont)
: m_fontList(new FontFallbackList)
, m_pageZero(0)
, m_cachedPrimaryFont(0)
, m_letterSpacing(0)
, m_wordSpacing(0)
, m_isPlatformFont(true)
{
m_fontDescription.setUsePrinterFont(isPrinterFont);
m_fontList->setPlatformFont(fontData);
m_fontDescription.setSpecifiedSize (GSFontGetSize(fontData.font()));
m_fontDescription.setComputedSize (GSFontGetSize(fontData.font()));
m_fontDescription.setItalic (GSFontGetTraits(fontData.font()) & GSItalicFontMask);
m_fontDescription.setWeight ((GSFontGetTraits(fontData.font()) & GSBoldFontMask) ? cBoldWeight : cNormalWeight);
}
Font::Font(const Font& other)
: m_fontDescription(other.m_fontDescription)
, m_fontList(other.m_fontList)
, m_pages(other.m_pages)
, m_pageZero(other.m_pageZero)
, m_cachedPrimaryFont(other.m_cachedPrimaryFont)
, m_letterSpacing(other.m_letterSpacing)
, m_wordSpacing(other.m_wordSpacing)
, m_isPlatformFont(other.m_isPlatformFont)
{
}
Font& Font::operator=(const Font& other)
{
m_fontDescription = other.m_fontDescription;
m_fontList = other.m_fontList;
m_pages = other.m_pages;
m_pageZero = other.m_pageZero;
m_cachedPrimaryFont = other.m_cachedPrimaryFont;
m_letterSpacing = other.m_letterSpacing;
m_wordSpacing = other.m_wordSpacing;
m_isPlatformFont = other.m_isPlatformFont;
return *this;
}
Font::~Font()
{
}
bool Font::operator==(const Font& other) const
{
if ((m_fontList && m_fontList->loadingCustomFonts()) ||
(other.m_fontList && other.m_fontList->loadingCustomFonts()))
return false;
FontSelector* first = m_fontList ? m_fontList->fontSelector() : 0;
FontSelector* second = other.m_fontList ? other.m_fontList->fontSelector() : 0;
return first == second
&& m_fontDescription == other.m_fontDescription
&& m_letterSpacing == other.m_letterSpacing
&& m_wordSpacing == other.m_wordSpacing;
}
const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const
{
bool useSmallCapsFont = forceSmallCaps;
if (m_fontDescription.smallCaps()) {
UChar32 upperC = Unicode::toUpper(c);
if (upperC != c) {
c = upperC;
useSmallCapsFont = true;
}
}
if (mirror)
c = mirroredChar(c);
unsigned pageNumber = (c / GlyphPage::size);
GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero;
if (!node) {
node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber);
if (pageNumber)
m_pages.set(pageNumber, node);
else
m_pageZero = node;
}
GlyphPage* page;
if (!useSmallCapsFont) {
while (true) {
page = node->page();
if (page) {
const GlyphData& data = page->glyphDataForCharacter(c);
if (data.fontData)
return data;
if (node->isSystemFallback())
break;
}
node = node->getChild(fontDataAt(node->level()), pageNumber);
if (pageNumber)
m_pages.set(pageNumber, node);
else
m_pageZero = node;
}
} else {
while (true) {
page = node->page();
if (page) {
const GlyphData& data = page->glyphDataForCharacter(c);
if (data.fontData) {
const SimpleFontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription);
if (!smallCapsFontData)
return data;
GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber);
const GlyphData& data = smallCapsNode->page()->glyphDataForCharacter(c);
if (data.fontData)
return data;
return smallCapsFontData->missingGlyphData();
}
if (node->isSystemFallback())
break;
}
node = node->getChild(fontDataAt(node->level()), pageNumber);
if (pageNumber)
m_pages.set(pageNumber, node);
else
m_pageZero = node;
}
}
ASSERT(page);
ASSERT(node->isSystemFallback());
UChar codeUnits[2];
int codeUnitsLength;
if (c <= 0xFFFF) {
UChar c16 = c;
if (Font::treatAsSpace(c16))
codeUnits[0] = ' ';
else if (Font::treatAsZeroWidthSpace(c16))
codeUnits[0] = zeroWidthSpace;
else
codeUnits[0] = c16;
codeUnitsLength = 1;
} else {
codeUnits[0] = U16_LEAD(c);
codeUnits[1] = U16_TRAIL(c);
codeUnitsLength = 2;
}
const SimpleFontData* characterFontData = FontCache::getFontDataForCharacters(*this, codeUnits, codeUnitsLength);
if (useSmallCapsFont)
characterFontData = characterFontData->smallCapsFontData(m_fontDescription);
if (characterFontData) {
GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page();
const GlyphData& data = fallbackPage && fallbackPage->glyphDataForCharacter(c).fontData ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData();
if (!useSmallCapsFont)
page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
return data;
}
const GlyphData& data = primaryFont()->missingGlyphData();
if (!useSmallCapsFont)
page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
return data;
}
void Font::cachePrimaryFont() const
{
ASSERT(m_fontList);
ASSERT(!m_cachedPrimaryFont);
m_cachedPrimaryFont = m_fontList->primaryFont(this)->fontDataForCharacter(' ');
}
const FontData* Font::fontDataAt(unsigned index) const
{
ASSERT(m_fontList);
return m_fontList->fontDataAt(this, index);
}
const FontData* Font::fontDataForCharacters(const UChar* characters, int length) const
{
ASSERT(m_fontList);
return m_fontList->fontDataForCharacters(this, characters, length);
}
void Font::update(PassRefPtr<FontSelector> fontSelector) const
{
if (!m_fontList)
m_fontList = new FontFallbackList();
m_fontList->invalidate(fontSelector);
m_cachedPrimaryFont = 0;
m_pageZero = 0;
m_pages.clear();
}
int Font::width(const TextRun& run) const
{
return lroundf(floatWidth(run));
}
int Font::ascent() const
{
return primaryFont()->ascent();
}
int Font::descent() const
{
return primaryFont()->descent();
}
int Font::lineSpacing() const
{
return primaryFont()->lineSpacing();
}
float Font::xHeight() const
{
return primaryFont()->xHeight();
}
unsigned Font::unitsPerEm() const
{
return primaryFont()->unitsPerEm();
}
int Font::spaceWidth() const
{
return (int)ceilf(primaryFont()->m_adjustedSpaceWidth + m_letterSpacing);
}
bool Font::isFixedPitch() const
{
ASSERT(m_fontList);
return m_fontList->isFixedPitch(this);
}
void Font::setCodePath(CodePath p)
{
codePath = p;
}
inline
bool Font::canUseGlyphCache(const TextRun& run) const
{
return true;
}
FloatSize Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
{
GlyphBuffer glyphBuffer;
float startX = point.x();
WidthIterator it(this, run);
it.advance(from);
float beforeWidth = it.m_runWidthSoFar;
it.advance(to, &glyphBuffer);
if (glyphBuffer.isEmpty())
return FloatSize();
float afterWidth = it.m_runWidthSoFar;
if (run.rtl()) {
float finalRoundingWidth = it.m_finalRoundingWidth;
it.advance(run.length());
startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth;
} else
startX += beforeWidth;
if (run.rtl())
for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end)
glyphBuffer.swap(i, end);
FloatPoint startPoint(startX, point.y());
drawGlyphBuffer(context, glyphBuffer, run, startPoint);
return FloatSize(startPoint.x() - startX, it.m_runHeightSoFar);
}
void Font::drawGlyphBuffer(GraphicsContext* context, const GlyphBuffer& glyphBuffer,
const TextRun& run, FloatPoint& point) const
{
const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
FloatSize offset = glyphBuffer.offsetAt(0);
FloatPoint startPoint(point);
float nextX = startPoint.x();
int lastFrom = 0;
int nextGlyph = 0;
while (nextGlyph < glyphBuffer.size()) {
const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph);
if (nextFontData != fontData || nextOffset != offset) {
drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
lastFrom = nextGlyph;
fontData = nextFontData;
offset = nextOffset;
startPoint.setX(nextX);
}
nextX += glyphBuffer.advanceAt(nextGlyph);
nextGlyph++;
}
drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
point.setX(nextX);
}
FloatSize Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
{
if (m_fontList && m_fontList->loadingCustomFonts())
return FloatSize();
to = (to == -1 ? run.length() : to);
#if ENABLE(SVG_FONTS)
if (primaryFont()->isSVGFont()) {
drawTextUsingSVGFont(context, run, point, from, to);
return FloatSize();
}
#endif
return drawSimpleText(context, run, point, from, to);
}
FloatSize Font::floatSize(const TextRun& run) const
{
WidthIterator it(this, run);
it.advance(run.length(), 0);
FloatSize size;
size.setWidth(it.m_runWidthSoFar);
size.setHeight(it.m_runHeightSoFar);
return size;
}
float Font::floatWidth(const TextRun& run) const
{
#if ENABLE(SVG_FONTS)
if (primaryFont()->isSVGFont())
return floatWidthUsingSVGFont(run);
#endif
return floatWidthForSimpleText(run, 0);
}
float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer) const
{
WidthIterator it(this, run);
it.advance(run.length(), glyphBuffer);
return it.m_runWidthSoFar;
}
FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
{
#if ENABLE(SVG_FONTS)
if (primaryFont()->isSVGFont())
return selectionRectForTextUsingSVGFont(run, point, h, from, to);
#endif
to = (to == -1 ? run.length() : to);
return selectionRectForSimpleText(run, point, h, from, to);
}
FloatRect Font::selectionRectForSimpleText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
{
WidthIterator it(this, run);
it.advance(from);
float beforeWidth = it.m_runWidthSoFar;
it.advance(to);
float afterWidth = it.m_runWidthSoFar;
if (run.rtl()) {
it.advance(run.length());
float totalWidth = it.m_runWidthSoFar;
return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h);
} else {
return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
}
}
int Font::offsetForPosition(const TextRun& run, int x, bool includePartialGlyphs) const
{
#if ENABLE(SVG_FONTS)
if (primaryFont()->isSVGFont())
return offsetForPositionForTextUsingSVGFont(run, x, includePartialGlyphs);
#endif
return offsetForPositionForSimpleText(run, x, includePartialGlyphs);
}
int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool includePartialGlyphs) const
{
float delta = (float)x;
WidthIterator it(this, run);
GlyphBuffer localGlyphBuffer;
unsigned offset;
if (run.rtl()) {
delta -= floatWidthForSimpleText(run, 0);
while (1) {
offset = it.m_currentCharacter;
float w;
if (!it.advanceOneCharacter(w, &localGlyphBuffer))
break;
delta += w;
if (includePartialGlyphs) {
if (delta - w / 2 >= 0)
break;
} else {
if (delta >= 0)
break;
}
}
} else {
while (1) {
offset = it.m_currentCharacter;
float w;
if (!it.advanceOneCharacter(w, &localGlyphBuffer))
break;
delta -= w;
if (includePartialGlyphs) {
if (delta + w / 2 <= 0)
break;
} else {
if (delta <= 0)
break;
}
}
}
return offset;
}
FontSelector* Font::fontSelector() const
{
return m_fontList ? m_fontList->fontSelector() : 0;
}
}