summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/mac
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:15 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:15 -0800
commit1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch)
tree4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/platform/graphics/mac
parent9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff)
downloadexternal_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip
external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz
external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/platform/graphics/mac')
-rw-r--r--WebCore/platform/graphics/mac/ColorMac.mm32
-rw-r--r--WebCore/platform/graphics/mac/CoreTextController.cpp533
-rw-r--r--WebCore/platform/graphics/mac/CoreTextController.h114
-rw-r--r--WebCore/platform/graphics/mac/FontCacheMac.mm160
-rw-r--r--WebCore/platform/graphics/mac/FontCustomPlatformData.cpp2
-rw-r--r--WebCore/platform/graphics/mac/FontCustomPlatformData.h3
-rw-r--r--WebCore/platform/graphics/mac/FontMac.mm591
-rw-r--r--WebCore/platform/graphics/mac/FontMacATSUI.mm623
-rw-r--r--WebCore/platform/graphics/mac/FontMacCoreText.cpp102
-rw-r--r--WebCore/platform/graphics/mac/FontPlatformData.h64
-rw-r--r--WebCore/platform/graphics/mac/FontPlatformDataMac.mm47
-rw-r--r--WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp17
-rw-r--r--WebCore/platform/graphics/mac/IconMac.mm26
-rw-r--r--WebCore/platform/graphics/mac/ImageMac.mm24
-rw-r--r--WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h17
-rw-r--r--WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm246
-rw-r--r--WebCore/platform/graphics/mac/SimpleFontDataMac.mm122
17 files changed, 1940 insertions, 783 deletions
diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm
index 5c89715..96fdc39 100644
--- a/WebCore/platform/graphics/mac/ColorMac.mm
+++ b/WebCore/platform/graphics/mac/ColorMac.mm
@@ -39,8 +39,6 @@ namespace WebCore {
// NSColor calls don't throw, so no need to block Cocoa exceptions in this file
static RGBA32 oldAquaFocusRingColor = 0xFF7DADD9;
-static bool tintIsKnown;
-static void (*tintChangeFunction)();
static RGBA32 systemFocusRingColor;
static bool useOldAquaFocusRingColor;
@@ -126,29 +124,17 @@ CGColorRef cgColor(const Color& c)
return CGColorFromNSColor(nsColor(c));
}
-static void observeTint()
-{
- ASSERT(!tintIsKnown);
- [[NSNotificationCenter defaultCenter] addObserver:[WebCoreControlTintObserver class]
- selector:@selector(controlTintDidChange)
- name:NSControlTintDidChangeNotification
- object:NSApp];
- [WebCoreControlTintObserver controlTintDidChange];
- tintIsKnown = true;
-}
-
-void setFocusRingColorChangeFunction(void (*function)())
-{
- ASSERT(!tintChangeFunction);
- tintChangeFunction = function;
- if (!tintIsKnown)
- observeTint();
-}
-
Color focusRingColor()
{
- if (!tintIsKnown)
- observeTint();
+ static bool tintIsKnown = false;
+ if (!tintIsKnown) {
+ [[NSNotificationCenter defaultCenter] addObserver:[WebCoreControlTintObserver class]
+ selector:@selector(controlTintDidChange)
+ name:NSControlTintDidChangeNotification
+ object:NSApp];
+ [WebCoreControlTintObserver controlTintDidChange];
+ tintIsKnown = true;
+ }
if (usesTestModeFocusRingColor())
return oldAquaFocusRingColor;
diff --git a/WebCore/platform/graphics/mac/CoreTextController.cpp b/WebCore/platform/graphics/mac/CoreTextController.cpp
new file mode 100644
index 0000000..171a7ec
--- /dev/null
+++ b/WebCore/platform/graphics/mac/CoreTextController.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2007, 2008 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "CoreTextController.h"
+
+#if USE(CORE_TEXT)
+
+#include "CharacterNames.h"
+#include "Font.h"
+#include "FontCache.h"
+#include "SimpleFontData.h"
+#include "TextBreakIterator.h"
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static inline CGFloat roundCGFloat(CGFloat f)
+{
+ if (sizeof(CGFloat) == sizeof(float))
+ return roundf(static_cast<float>(f));
+ return static_cast<CGFloat>(round(f));
+}
+
+static inline CGFloat ceilCGFloat(CGFloat f)
+{
+ if (sizeof(CGFloat) == sizeof(float))
+ return ceilf(static_cast<float>(f));
+ return static_cast<CGFloat>(ceil(f));
+}
+
+CoreTextController::CoreTextRun::CoreTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength)
+ : m_CTRun(ctRun)
+ , m_fontData(fontData)
+ , m_characters(characters)
+ , m_stringLocation(stringLocation)
+ , m_stringLength(stringLength)
+{
+ m_glyphCount = CTRunGetGlyphCount(ctRun);
+ m_indices = CTRunGetStringIndicesPtr(ctRun);
+ if (!m_indices) {
+ m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex)));
+ CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex));
+ m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get()));
+ CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices));
+ }
+}
+
+// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on
+// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path.
+CoreTextController::CoreTextRun::CoreTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
+ : m_fontData(fontData)
+ , m_characters(characters)
+ , m_stringLocation(stringLocation)
+ , m_stringLength(stringLength)
+{
+ Vector<CFIndex, 16> indices;
+ unsigned r = 0;
+ while (r < stringLength) {
+ indices.append(r);
+ if (U_IS_SURROGATE(characters[r])) {
+ ASSERT(r + 1 < stringLength);
+ ASSERT(U_IS_SURROGATE_LEAD(characters[r]));
+ ASSERT(U_IS_TRAIL(characters[r + 1]));
+ r += 2;
+ } else
+ r++;
+ }
+ m_glyphCount = indices.size();
+ if (!ltr) {
+ for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end)
+ std::swap(indices[r], indices[end]);
+ }
+ m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex)));
+ CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex));
+ m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get()));
+}
+
+CoreTextController::CoreTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection)
+ : m_font(*font)
+ , m_run(run)
+ , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
+ , m_currentCharacter(0)
+ , m_end(run.length())
+ , m_totalWidth(0)
+ , m_runWidthSoFar(0)
+ , m_numGlyphsSoFar(0)
+ , m_currentRun(0)
+ , m_glyphInCurrentRun(0)
+ , m_finalRoundingWidth(0)
+ , m_lastRoundingGlyph(0)
+{
+ m_padding = m_run.padding();
+ if (!m_padding)
+ m_padPerSpace = 0;
+ else {
+ float numSpaces = 0;
+ for (int s = 0; s < m_run.length(); s++)
+ if (Font::treatAsSpace(m_run[s]))
+ numSpaces++;
+
+ if (numSpaces == 0)
+ m_padPerSpace = 0;
+ else
+ m_padPerSpace = ceilf(m_run.padding() / numSpaces);
+ }
+
+ collectCoreTextRuns();
+ adjustGlyphsAndAdvances();
+}
+
+int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs)
+{
+ // FIXME: For positions occurring within a ligature, we should return the closest "ligature caret" or
+ // approximate it by dividing the width of the ligature by the number of characters it encompasses.
+ // However, Core Text does not expose a low-level API for directly finding
+ // out how many characters a ligature encompasses (the "attachment count").
+ if (h >= m_totalWidth)
+ return m_run.ltr() ? m_end : 0;
+ if (h < 0)
+ return m_run.ltr() ? 0 : m_end;
+
+ CGFloat x = h;
+
+ size_t runCount = m_coreTextRuns.size();
+ size_t offsetIntoAdjustedGlyphs = 0;
+
+ for (size_t r = 0; r < runCount; ++r) {
+ const CoreTextRun& coreTextRun = m_coreTextRuns[r];
+ for (unsigned j = 0; j < coreTextRun.glyphCount(); ++j) {
+ CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
+ if (x <= adjustedAdvance) {
+ CFIndex hitIndex = coreTextRun.indexAt(j);
+ int stringLength = coreTextRun.stringLength();
+ TextBreakIterator* characterIterator = characterBreakIterator(coreTextRun.characters(), stringLength);
+ int clusterStart;
+ if (isTextBreak(characterIterator, hitIndex))
+ clusterStart = hitIndex;
+ else {
+ clusterStart = textBreakPreceding(characterIterator, hitIndex);
+ if (clusterStart == TextBreakDone)
+ clusterStart = 0;
+ }
+
+ if (!includePartialGlyphs)
+ return coreTextRun.stringLocation() + clusterStart;
+
+ int clusterEnd = textBreakFollowing(characterIterator, hitIndex);
+ if (clusterEnd == TextBreakDone)
+ clusterEnd = stringLength;
+
+ CGFloat clusterWidth = adjustedAdvance;
+ // FIXME: The search stops at the boundaries of coreTextRun. In theory, it should go on into neighboring CoreTextRuns
+ // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
+ // reordering and on font fallback should occur within a CTLine.
+ if (clusterEnd - clusterStart > 1) {
+ int firstGlyphBeforeCluster = j - 1;
+ while (firstGlyphBeforeCluster && coreTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
+ CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
+ clusterWidth += width;
+ x += width;
+ firstGlyphBeforeCluster--;
+ }
+ unsigned firstGlyphAfterCluster = j + 1;
+ while (firstGlyphAfterCluster < coreTextRun.glyphCount() && coreTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
+ clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
+ firstGlyphAfterCluster++;
+ }
+ }
+ if (x <= clusterWidth / 2)
+ return coreTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
+ else
+ return coreTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
+ }
+ x -= adjustedAdvance;
+ }
+ offsetIntoAdjustedGlyphs += coreTextRun.glyphCount();
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void CoreTextController::collectCoreTextRuns()
+{
+ if (!m_end)
+ return;
+
+ // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
+ const UChar* cp = m_run.characters();
+ bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen;
+
+ if (m_font.isSmallCaps() || hasTrailingSoftHyphen)
+ m_smallCapsBuffer.resize(m_end);
+
+ unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
+ const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp;
+ const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
+
+ // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its
+ // SOFT-HYPHEN glyph, and has no HYPHEN glyph.
+ static const UChar hyphen = '-';
+
+ if (hasTrailingSoftHyphen && m_run.rtl()) {
+ collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
+ indexOfFontTransition--;
+ curr--;
+ }
+
+ GlyphData glyphData;
+ GlyphData nextGlyphData;
+
+ bool isSurrogate = U16_IS_SURROGATE(*curr);
+ if (isSurrogate) {
+ if (m_run.ltr()) {
+ if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
+ return;
+ nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
+ } else {
+ if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
+ return;
+ nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
+ }
+ } else
+ nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
+
+ UChar newC = 0;
+
+ bool isSmallCaps;
+ bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
+
+ if (nextIsSmallCaps)
+ m_smallCapsBuffer[curr - cp] = newC;
+
+ while (true) {
+ curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
+ if (curr == end)
+ break;
+
+ glyphData = nextGlyphData;
+ isSmallCaps = nextIsSmallCaps;
+ int index = curr - cp;
+ isSurrogate = U16_IS_SURROGATE(*curr);
+ UChar c = *curr;
+ bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
+ if (isSurrogate) {
+ if (m_run.ltr()) {
+ if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
+ return;
+ nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
+ } else {
+ if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
+ return;
+ nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
+ }
+ } else
+ nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps);
+
+ if (!isSurrogate && m_font.isSmallCaps()) {
+ nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
+ if (nextIsSmallCaps)
+ m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
+ }
+
+ if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
+ int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition;
+ int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
+ collectCoreTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
+ indexOfFontTransition = index;
+ }
+ }
+
+ int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0);
+ if (itemLength) {
+ int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
+ collectCoreTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
+ }
+
+ if (hasTrailingSoftHyphen && m_run.ltr())
+ collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
+}
+
+void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
+{
+ // FIXME: For offsets falling inside a ligature, we should advance only as far as the appropriate "ligature caret"
+ // or divide the width of the ligature by the number of offsets it encompasses and make an advance proportional
+ // to the offsets into the ligature. However, Core Text does not expose a low-level API for
+ // directly finding out how many characters a ligature encompasses (the "attachment count").
+ if (static_cast<int>(offset) > m_end)
+ offset = m_end;
+
+ if (offset <= m_currentCharacter)
+ return;
+
+ m_currentCharacter = offset;
+
+ size_t runCount = m_coreTextRuns.size();
+
+ bool ltr = m_run.ltr();
+
+ unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
+ while (m_currentRun < runCount) {
+ const CoreTextRun& coreTextRun = m_coreTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
+ size_t glyphCount = coreTextRun.glyphCount();
+ unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
+ while (m_glyphInCurrentRun < glyphCount) {
+ if (coreTextRun.indexAt(g) + coreTextRun.stringLocation() >= m_currentCharacter)
+ return;
+ CGSize adjustedAdvance = m_adjustedAdvances[k];
+ if (glyphBuffer)
+ glyphBuffer->add(m_adjustedGlyphs[k], coreTextRun.fontData(), adjustedAdvance);
+ m_runWidthSoFar += adjustedAdvance.width;
+ m_numGlyphsSoFar++;
+ m_glyphInCurrentRun++;
+ if (ltr) {
+ g++;
+ k++;
+ } else {
+ g--;
+ k--;
+ }
+ }
+ m_currentRun++;
+ m_glyphInCurrentRun = 0;
+ }
+ if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
+ m_runWidthSoFar += m_finalRoundingWidth;
+}
+
+void CoreTextController::collectCoreTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
+{
+ if (!fontData) {
+ // Create a run of missing glyphs from the primary font.
+ m_coreTextRuns.append(CoreTextRun(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr()));
+ return;
+ }
+
+ RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull));
+
+ RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes()));
+
+ RetainPtr<CTTypesetterRef> typesetter;
+
+ if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) {
+ static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel };
+ static const void* ltrOptionValues[] = { kCFBooleanFalse };
+ static const void* rtlOptionValues[] = { kCFBooleanTrue };
+ static RetainPtr<CFDictionaryRef> ltrTypesetterOptions(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ static RetainPtr<CFDictionaryRef> rtlTypesetterOptions(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions.get() : rtlTypesetterOptions.get()));
+ } else
+ typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get()));
+
+ RetainPtr<CTLineRef> line(AdoptCF, CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0)));
+
+ CFArrayRef runArray = CTLineGetGlyphRuns(line.get());
+
+ CFIndex runCount = CFArrayGetCount(runArray);
+
+ for (CFIndex r = 0; r < runCount; r++) {
+ CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r));
+ ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID());
+ m_coreTextRuns.append(CoreTextRun(ctRun, fontData, cp, stringLocation, length));
+ }
+}
+
+void CoreTextController::adjustGlyphsAndAdvances()
+{
+ size_t runCount = m_coreTextRuns.size();
+ for (size_t r = 0; r < runCount; ++r) {
+ const CoreTextRun& coreTextRun = m_coreTextRuns[r];
+ unsigned glyphCount = coreTextRun.glyphCount();
+ const SimpleFontData* fontData = coreTextRun.fontData();
+
+ Vector<CGGlyph, 256> glyphsVector;
+ const CGGlyph* glyphs;
+
+ Vector<CGSize, 256> advancesVector;
+ const CGSize* advances;
+
+ if (coreTextRun.ctRun()) {
+ glyphs = CTRunGetGlyphsPtr(coreTextRun.ctRun());
+ if (!glyphs) {
+ glyphsVector.grow(glyphCount);
+ CTRunGetGlyphs(coreTextRun.ctRun(), CFRangeMake(0, 0), glyphsVector.data());
+ glyphs = glyphsVector.data();
+ }
+
+ advances = CTRunGetAdvancesPtr(coreTextRun.ctRun());
+ if (!advances) {
+ advancesVector.grow(glyphCount);
+ CTRunGetAdvances(coreTextRun.ctRun(), CFRangeMake(0, 0), advancesVector.data());
+ advances = advancesVector.data();
+ }
+ } else {
+ // Synthesize a run of missing glyphs.
+ glyphsVector.fill(0, glyphCount);
+ glyphs = glyphsVector.data();
+ advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), glyphCount);
+ advances = advancesVector.data();
+ }
+
+ bool lastRun = r + 1 == runCount;
+ const UChar* cp = coreTextRun.characters();
+ CGFloat roundedSpaceWidth = roundCGFloat(fontData->m_spaceWidth);
+ bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
+ bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled();
+
+
+ for (unsigned i = 0; i < glyphCount; i++) {
+ CFIndex characterIndex = coreTextRun.indexAt(i);
+ UChar ch = *(cp + characterIndex);
+ bool lastGlyph = lastRun && i + 1 == glyphCount;
+ UChar nextCh;
+ if (lastGlyph)
+ nextCh = ' ';
+ else if (i + 1 < glyphCount)
+ nextCh = *(cp + coreTextRun.indexAt(i + 1));
+ else
+ nextCh = *(m_coreTextRuns[r + 1].characters() + m_coreTextRuns[r + 1].indexAt(0));
+
+ bool treatAsSpace = Font::treatAsSpace(ch);
+ CGGlyph glyph = treatAsSpace ? fontData->m_spaceGlyph : glyphs[i];
+ CGSize advance = treatAsSpace ? CGSizeMake(fontData->m_spaceWidth, advances[i].height) : advances[i];
+
+ if (ch == '\t' && m_run.allowTabs()) {
+ float tabWidth = m_font.tabWidth();
+ advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth);
+ } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
+ advance.width = 0;
+ glyph = fontData->m_spaceGlyph;
+ }
+
+ float roundedAdvanceWidth = roundf(advance.width);
+ if (roundsAdvances)
+ advance.width = roundedAdvanceWidth;
+
+ advance.width += fontData->m_syntheticBoldOffset;
+
+ // We special case spaces in two ways when applying word rounding.
+ // First, we round spaces to an adjusted width in all fonts.
+ // Second, in fixed-pitch fonts we ensure that all glyphs that
+ // match the width of the space glyph have the same width as the space glyph.
+ if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding())
+ advance.width = fontData->m_adjustedSpaceWidth;
+
+ if (hasExtraSpacing) {
+ // If we're a glyph with an advance, go ahead and add in letter-spacing.
+ // That way we weed out zero width lurkers. This behavior matches the fast text code path.
+ if (advance.width && m_font.letterSpacing())
+ advance.width += m_font.letterSpacing();
+
+ // Handle justification and word-spacing.
+ if (glyph == fontData->m_spaceGlyph) {
+ // Account for padding. WebCore uses space padding to justify text.
+ // We distribute the specified padding over the available spaces in the run.
+ if (m_padding) {
+ // Use leftover padding if not evenly divisible by number of spaces.
+ if (m_padding < m_padPerSpace) {
+ advance.width += m_padding;
+ m_padding = 0;
+ } else {
+ advance.width += m_padPerSpace;
+ m_padding -= m_padPerSpace;
+ }
+ }
+
+ // Account for word-spacing.
+ if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
+ advance.width += m_font.wordSpacing();
+ }
+ }
+
+ // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters
+ // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
+ // We adjust the width of the last character of a "word" to ensure an integer width.
+ // Force characters that are used to determine word boundaries for the rounding hack
+ // to be integer width, so the following words will start on an integer boundary.
+ if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
+ advance.width = ceilCGFloat(advance.width);
+
+ // Check to see if the next character is a "rounding hack character", if so, adjust the
+ // width so that the total run width will be on an integer boundary.
+ if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) {
+ CGFloat totalWidth = m_totalWidth + advance.width;
+ CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth;
+ if (m_run.ltr())
+ advance.width += extraWidth;
+ else {
+ m_totalWidth += extraWidth;
+ if (m_lastRoundingGlyph)
+ m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
+ else
+ m_finalRoundingWidth = extraWidth;
+ m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
+ }
+ }
+
+ m_totalWidth += advance.width;
+ advance.height *= -1;
+ m_adjustedAdvances.append(advance);
+ m_adjustedGlyphs.append(glyph);
+ }
+ }
+}
+
+} // namespace WebCore
+
+#endif // USE(CORE_TEXT)
diff --git a/WebCore/platform/graphics/mac/CoreTextController.h b/WebCore/platform/graphics/mac/CoreTextController.h
new file mode 100644
index 0000000..8dbb7fb
--- /dev/null
+++ b/WebCore/platform/graphics/mac/CoreTextController.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007, 2008 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 CoreTextController_h
+#define CoreTextController_h
+
+#if USE(CORE_TEXT)
+
+#include "Font.h"
+#include "GlyphBuffer.h"
+#include <wtf/RetainPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CoreTextController {
+public:
+ CoreTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false);
+
+ // Advance and emit glyphs up to the specified character.
+ void advance(unsigned to, GlyphBuffer* = 0);
+
+ // Compute the character offset for a given x coordinate.
+ int offsetForPosition(int x, bool includePartialGlyphs);
+
+ // Returns the width of everything we've consumed so far.
+ float runWidthSoFar() const { return m_runWidthSoFar; }
+
+ float totalWidth() const { return m_totalWidth; }
+
+ // Extra width to the left of the leftmost glyph.
+ float finalRoundingWidth() const { return m_finalRoundingWidth; }
+
+private:
+ class CoreTextRun {
+ public:
+ CoreTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength);
+ CoreTextRun(const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr);
+
+ CTRunRef ctRun() const { return m_CTRun.get(); }
+ unsigned glyphCount() const { return m_glyphCount; }
+ const SimpleFontData* fontData() const { return m_fontData; }
+ const UChar* characters() const { return m_characters; }
+ unsigned stringLocation() const { return m_stringLocation; }
+ size_t stringLength() const { return m_stringLength; }
+ CFIndex indexAt(size_t i) const { return m_indices[i]; }
+
+ private:
+ RetainPtr<CTRunRef> m_CTRun;
+ unsigned m_glyphCount;
+ const SimpleFontData* m_fontData;
+ const UChar* m_characters;
+ unsigned m_stringLocation;
+ size_t m_stringLength;
+ const CFIndex* m_indices;
+ // Used only if CTRunGet*Ptr fails or if this is a missing glyphs run.
+ RetainPtr<CFMutableDataRef> m_indicesData;
+ };
+
+ void collectCoreTextRuns();
+ void collectCoreTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*);
+ void adjustGlyphsAndAdvances();
+
+ const Font& m_font;
+ const TextRun& m_run;
+ bool m_mayUseNaturalWritingDirection;
+
+ Vector<UChar, 256> m_smallCapsBuffer;
+
+ Vector<CoreTextRun, 16> m_coreTextRuns;
+ Vector<CGSize, 256> m_adjustedAdvances;
+ Vector<CGGlyph, 256> m_adjustedGlyphs;
+
+ unsigned m_currentCharacter;
+ int m_end;
+
+ CGFloat m_totalWidth;
+
+ float m_runWidthSoFar;
+ unsigned m_numGlyphsSoFar;
+ size_t m_currentRun;
+ unsigned m_glyphInCurrentRun;
+ float m_finalRoundingWidth;
+ float m_padding;
+ float m_padPerSpace;
+
+ unsigned m_lastRoundingGlyph;
+};
+
+} // namespace WebCore
+#endif // USE(CORE_TEXT)
+#endif // CoreTextController_h
diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm
index ffb3068..e7cda66 100644
--- a/WebCore/platform/graphics/mac/FontCacheMac.mm
+++ b/WebCore/platform/graphics/mac/FontCacheMac.mm
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -41,84 +42,37 @@ typedef int NSInteger;
namespace WebCore {
-static bool getAppDefaultValue(CFStringRef key, int *v)
+static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*)
{
- CFPropertyListRef value;
-
- value = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication,
- kCFPreferencesAnyUser,
- kCFPreferencesAnyHost);
- if (value == 0) {
- value = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication,
- kCFPreferencesCurrentUser,
- kCFPreferencesAnyHost);
- if (value == 0)
- return false;
- }
-
- if (CFGetTypeID(value) == CFNumberGetTypeID()) {
- if (v != 0)
- CFNumberGetValue((const CFNumberRef)value, kCFNumberIntType, v);
- } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
- if (v != 0)
- *v = CFStringGetIntValue((const CFStringRef)value);
- } else {
- CFRelease(value);
- return false;
- }
-
- CFRelease(value);
- return true;
+ FontCache::invalidate();
}
-static bool getUserDefaultValue(CFStringRef key, int *v)
+void FontCache::platformInit()
{
- CFPropertyListRef value;
-
- value = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication,
- kCFPreferencesCurrentUser,
- kCFPreferencesCurrentHost);
- if (value == 0)
- return false;
-
- if (CFGetTypeID(value) == CFNumberGetTypeID()) {
- if (v != 0)
- CFNumberGetValue((const CFNumberRef)value, kCFNumberIntType, v);
- } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
- if (v != 0)
- *v = CFStringGetIntValue((const CFStringRef)value);
- } else {
- CFRelease(value);
- return false;
- }
-
- CFRelease(value);
- return true;
+ wkSetUpFontCache();
+ // FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway.
+ ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0);
}
-static int getLCDScaleParameters(void)
+static int toAppKitFontWeight(FontWeight fontWeight)
{
- int mode;
- CFStringRef key;
-
- key = CFSTR("AppleFontSmoothing");
- if (!getAppDefaultValue(key, &mode)) {
- if (!getUserDefaultValue(key, &mode))
- return 1;
- }
-
- if (wkFontSmoothingModeIsLCD(mode))
- return 4;
- return 1;
+ static int appKitFontWeights[] = {
+ 2, // FontWeight100
+ 3, // FontWeight200
+ 4, // FontWeight300
+ 5, // FontWeight400
+ 6, // FontWeight500
+ 8, // FontWeight600
+ 9, // FontWeight700
+ 10, // FontWeight800
+ 12, // FontWeight900
+ };
+ return appKitFontWeights[fontWeight];
}
-#define MINIMUM_GLYPH_CACHE_SIZE 1536 * 1024
-
-void FontCache::platformInit()
+static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight)
{
- size_t s = MINIMUM_GLYPH_CACHE_SIZE*getLCDScaleParameters();
-
- wkSetUpFontCache(s);
+ return appKitFontWeight >= 7;
}
const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
@@ -126,8 +80,7 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons
const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(characters[0])->platformData();
NSFont *nsFont = platformData.font();
- NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters)
- length:length freeWhenDone:NO];
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) length:length freeWhenDone:NO];
NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length));
[string release];
@@ -135,49 +88,45 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons
substituteFont = wkGetFontInLanguageForCharacter(nsFont, characters[0]);
if (!substituteFont)
return 0;
-
+
// Use the family name from the AppKit-supplied substitute font, requesting the
// traits, weight, and size we want. One way this does better than the original
// AppKit request is that it takes synthetic bold and oblique into account.
// But it does create the possibility that we could end up with a font that
// doesn't actually cover the characters we need.
- NSFontManager *manager = [NSFontManager sharedFontManager];
+ NSFontManager *fontManager = [NSFontManager sharedFontManager];
NSFontTraitMask traits;
NSInteger weight;
CGFloat size;
if (nsFont) {
- traits = [manager traitsOfFont:nsFont];
+ traits = [fontManager traitsOfFont:nsFont];
if (platformData.m_syntheticBold)
traits |= NSBoldFontMask;
if (platformData.m_syntheticOblique)
- traits |= NSItalicFontMask;
- weight = [manager weightOfFont:nsFont];
+ traits |= NSFontItalicTrait;
+ weight = [fontManager weightOfFont:nsFont];
size = [nsFont pointSize];
} else {
// For custom fonts nsFont is nil.
- traits = (font.bold() ? NSBoldFontMask : 0) | (font.italic() ? NSItalicFontMask : 0);
- weight = 5;
+ traits = font.italic() ? NSFontItalicTrait : 0;
+ weight = toAppKitFontWeight(font.weight());
size = font.pixelSize();
}
- NSFont *bestVariation = [manager fontWithFamily:[substituteFont familyName]
- traits:traits
- weight:weight
- size:size];
- if (bestVariation)
+ if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size])
substituteFont = bestVariation;
- substituteFont = font.fontDescription().usePrinterFont()
- ? [substituteFont printerFont] : [substituteFont screenFont];
+ substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont];
- NSFontTraitMask substituteFontTraits = [manager traitsOfFont:substituteFont];
+ NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont];
+ NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont];
FontPlatformData alternateFont(substituteFont,
- !font.isPlatformFont() && (traits & NSBoldFontMask) && !(substituteFontTraits & NSBoldFontMask),
- !font.isPlatformFont() && (traits & NSItalicFontMask) && !(substituteFontTraits & NSItalicFontMask));
+ !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight),
+ !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait));
return getCachedFontData(&alternateFont);
}
@@ -220,42 +169,33 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fo
return platformFont;
}
-bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family)
+void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
{
- NSFontTraitMask traits = 0;
- if (fontDescription.italic())
- traits |= NSItalicFontMask;
- if (fontDescription.bold())
- traits |= NSBoldFontMask;
- float size = fontDescription.computedPixelSize();
-
- NSFont* nsFont = [WebFontCache fontWithFamily:family traits:traits size:size];
- return nsFont != 0;
+ [WebFontCache getTraits:traitsMasks inFamily:familyName];
}
FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
{
- NSFontTraitMask traits = 0;
- if (fontDescription.italic())
- traits |= NSItalicFontMask;
- if (fontDescription.bold())
- traits |= NSBoldFontMask;
+ NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0;
+ NSInteger weight = toAppKitFontWeight(fontDescription.weight());
float size = fontDescription.computedPixelSize();
-
- NSFont* nsFont = [WebFontCache fontWithFamily:family traits:traits size:size];
+
+ NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size];
if (!nsFont)
return 0;
+ NSFontManager *fontManager = [NSFontManager sharedFontManager];
NSFontTraitMask actualTraits = 0;
- if (fontDescription.bold() || fontDescription.italic())
- actualTraits = [[NSFontManager sharedFontManager] traitsOfFont:nsFont];
-
+ if (fontDescription.italic())
+ actualTraits = [fontManager traitsOfFont:nsFont];
+ NSInteger actualWeight = [fontManager weightOfFont:nsFont];
+
FontPlatformData* result = new FontPlatformData;
-
+
// Use the correct font for print vs. screen.
result->setFont(fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]);
- result->m_syntheticBold = (traits & NSBoldFontMask) && !(actualTraits & NSBoldFontMask);
- result->m_syntheticOblique = (traits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask);
+ result->m_syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight);
+ result->m_syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait);
return result;
}
diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp
index f143458..1fb144c 100644
--- a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp
+++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp
@@ -33,7 +33,7 @@ FontCustomPlatformData::~FontCustomPlatformData()
CGFontRelease(m_cgFont);
}
-FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic)
+FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode)
{
return FontPlatformData(m_cgFont, (ATSUFontID)m_atsFont, size, bold, italic);
}
diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/WebCore/platform/graphics/mac/FontCustomPlatformData.h
index d2e83ca..1e73ae0 100644
--- a/WebCore/platform/graphics/mac/FontCustomPlatformData.h
+++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.h
@@ -21,6 +21,7 @@
#ifndef FontCustomPlatformData_h
#define FontCustomPlatformData_h
+#include "FontRenderingMode.h"
#include <wtf/Noncopyable.h>
typedef struct CGFont* CGFontRef;
@@ -38,7 +39,7 @@ struct FontCustomPlatformData : Noncopyable {
{}
~FontCustomPlatformData();
- FontPlatformData fontPlatformData(int size, bool bold, bool italic);
+ FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode);
ATSFontContainerRef m_atsContainer;
ATSFontRef m_atsFont;
diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm
index 06d8d9e..bef18d0 100644
--- a/WebCore/platform/graphics/mac/FontMac.mm
+++ b/WebCore/platform/graphics/mac/FontMac.mm
@@ -1,6 +1,4 @@
-/**
- * This file is part of the html renderer for KDE.
- *
+/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2000 Dirk Mueller (mueller@kde.org)
@@ -20,20 +18,14 @@
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
- *
*/
#import "config.h"
#import "Font.h"
-#import "BlockExceptions.h"
-#import "CharacterNames.h"
-#import "FontFallbackList.h"
#import "GlyphBuffer.h"
#import "GraphicsContext.h"
-#import "IntRect.h"
#import "Logging.h"
-#import "ShapeArabic.h"
#import "SimpleFontData.h"
#import "WebCoreSystemInterface.h"
#import "WebCoreTextRenderer.h"
@@ -50,559 +42,6 @@ using namespace std;
namespace WebCore {
-// =================================================================
-// Font Class (Platform-Specific Portion)
-// =================================================================
-
-struct ATSULayoutParameters
-{
- ATSULayoutParameters(const TextRun& run)
- : m_run(run)
- , m_font(0)
- , m_fonts(0)
- , m_charBuffer(0)
- , m_hasSyntheticBold(false)
- , m_syntheticBoldPass(false)
- , m_padPerSpace(0)
- {}
-
- void initialize(const Font*, const GraphicsContext* = 0);
-
- const TextRun& m_run;
-
- const Font* m_font;
-
- ATSUTextLayout m_layout;
- const SimpleFontData **m_fonts;
-
- UChar *m_charBuffer;
- bool m_hasSyntheticBold;
- bool m_syntheticBoldPass;
- float m_padPerSpace;
-};
-
-// Be sure to free the array allocated by this function.
-static TextRun addDirectionalOverride(const TextRun& run, bool rtl)
-{
- UChar* charactersWithOverride = new UChar[run.length() + 2];
- charactersWithOverride[0] = rtl ? rightToLeftOverride : leftToRightOverride;
- memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length());
- charactersWithOverride[run.length() + 1] = popDirectionalFormatting;
-
- TextRun result = run;
- result.setText(charactersWithOverride, run.length() + 2);
- return result;
-}
-
-static void initializeATSUStyle(const SimpleFontData* fontData)
-{
- // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions.
-
- if (!fontData->m_ATSUStyleInitialized) {
- OSStatus status;
- ByteCount propTableSize;
-
- status = ATSUCreateStyle(&fontData->m_ATSUStyle);
- if (status != noErr)
- LOG_ERROR("ATSUCreateStyle failed (%d)", status);
-
- ATSUFontID fontID = fontData->platformData().m_atsuFontID;
- if (fontID == 0) {
- ATSUDisposeStyle(fontData->m_ATSUStyle);
- LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font());
- return;
- }
-
- CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
- if (fontData->m_font.m_syntheticOblique)
- transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0));
- Fixed fontSize = FloatToFixed(fontData->platformData().m_size);
-
- // Turn off automatic kerning until it is supported in the CG code path (6136 in bugzilla)
- Fract kerningInhibitFactor = FloatToFract(1.0);
- ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag };
- ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) };
- ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor };
- status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues);
- if (status != noErr)
- LOG_ERROR("ATSUSetAttributes failed (%d)", status);
- status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize);
- if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info
- fontData->m_ATSUMirrors = true;
- else if (status == kATSInvalidFontTableAccess)
- fontData->m_ATSUMirrors = false;
- else
- LOG_ERROR("ATSFontGetTable failed (%d)", status);
-
- // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bugzilla 6135 is fixed.
- // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are
- // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example.
- // See bugzilla 5166.
- if ([[fontData->m_font.font() coveredCharacterSet] characterIsMember:'a']) {
- ATSUFontFeatureType featureTypes[] = { kLigaturesType };
- ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector };
- status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors);
- }
-
- fontData->m_ATSUStyleInitialized = true;
- }
-}
-
-static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon,
- void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
-{
- ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon;
- OSStatus status;
- ItemCount count;
- ATSLayoutRecord *layoutRecords;
-
- if (params->m_run.applyWordRounding()) {
- status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
- if (status != noErr) {
- *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
- return status;
- }
-
- Fixed lastNativePos = 0;
- float lastAdjustedPos = 0;
- const UChar* characters = params->m_charBuffer ? params->m_charBuffer : params->m_run.characters();
- const SimpleFontData **renderers = params->m_fonts;
- const SimpleFontData *renderer;
- const SimpleFontData *lastRenderer = 0;
- UChar ch, nextCh;
- ByteCount offset = layoutRecords[0].originalOffset;
- nextCh = *(UChar *)(((char *)characters)+offset);
- bool shouldRound = false;
- bool syntheticBoldPass = params->m_syntheticBoldPass;
- Fixed syntheticBoldOffset = 0;
- ATSGlyphRef spaceGlyph = 0;
- bool hasExtraSpacing = params->m_font->letterSpacing() || params->m_font->wordSpacing() | params->m_run.padding();
- float padding = params->m_run.padding();
- // In the CoreGraphics code path, the rounding hack is applied in logical order.
- // Here it is applied in visual left-to-right order, which may be better.
- ItemCount lastRoundingChar = 0;
- ItemCount i;
- for (i = 1; i < count; i++) {
- bool isLastChar = i == count - 1;
- renderer = renderers[offset / 2];
- if (renderer != lastRenderer) {
- lastRenderer = renderer;
- spaceGlyph = renderer->m_spaceGlyph;
- // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems
- // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI
- // does in any of its device-metrics modes.
- shouldRound = [renderer->m_font.font() renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
- if (syntheticBoldPass)
- syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset);
- }
- float width;
- if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) {
- width = 0;
- layoutRecords[i-1].glyphID = spaceGlyph;
- } else {
- width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
- if (shouldRound)
- width = roundf(width);
- width += renderer->m_syntheticBoldOffset;
- if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace))
- width = renderer->m_adjustedSpaceWidth;
- }
- lastNativePos = layoutRecords[i].realPos;
-
- if (hasExtraSpacing) {
- if (width && params->m_font->letterSpacing())
- width +=params->m_font->letterSpacing();
- if (Font::treatAsSpace(nextCh)) {
- if (params->m_run.padding()) {
- if (padding < params->m_padPerSpace) {
- width += padding;
- padding = 0;
- } else {
- width += params->m_padPerSpace;
- padding -= params->m_padPerSpace;
- }
- }
- if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
- width += params->m_font->wordSpacing();
- }
- }
-
- ch = nextCh;
- offset = layoutRecords[i].originalOffset;
- // Use space for nextCh at the end of the loop so that we get inside the rounding hack code.
- // We won't actually round unless the other conditions are satisfied.
- nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset);
-
- if (Font::isRoundingHackCharacter(ch))
- width = ceilf(width);
- lastAdjustedPos = lastAdjustedPos + width;
- if (Font::isRoundingHackCharacter(nextCh) && (!isLastChar || params->m_run.applyRunRounding())){
- if (params->m_run.ltr())
- lastAdjustedPos = ceilf(lastAdjustedPos);
- else {
- float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
- Fixed rw = FloatToFixed(roundingWidth);
- ItemCount j;
- for (j = lastRoundingChar; j < i; j++)
- layoutRecords[j].realPos += rw;
- lastRoundingChar = i;
- lastAdjustedPos += roundingWidth;
- }
- }
- if (syntheticBoldPass) {
- if (syntheticBoldOffset)
- layoutRecords[i-1].realPos += syntheticBoldOffset;
- else
- layoutRecords[i-1].glyphID = spaceGlyph;
- }
- layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
- }
-
- status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
- }
- *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
- return noErr;
-}
-
-static inline bool isArabicLamWithAlefLigature(UChar c)
-{
- return c >= 0xfef5 && c <= 0xfefc;
-}
-
-static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart)
-{
- while (shapingStart < totalLength) {
- unsigned shapingEnd;
- // We do not want to pass a Lam with Alef ligature followed by a space to the shaper,
- // since we want to be able to identify this sequence as the result of shaping a Lam
- // followed by an Alef and padding with a space.
- bool foundLigatureSpace = false;
- for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd)
- foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' ';
- shapingEnd++;
-
- UErrorCode shapingError = U_ZERO_ERROR;
- unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError);
-
- if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) {
- for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) {
- if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ')
- dest[++j] = zeroWidthSpace;
- }
- if (foundLigatureSpace) {
- dest[shapingEnd] = ' ';
- shapingEnd++;
- } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) {
- // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef,
- // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR.
- ASSERT(dest[shapingStart] == ' ');
- dest[shapingStart] = zeroWidthSpace;
- }
- } else {
- // Something went wrong. Abandon shaping and just copy the rest of the buffer.
- LOG_ERROR("u_shapeArabic failed(%d)", shapingError);
- shapingEnd = totalLength;
- memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar));
- }
- shapingStart = shapingEnd;
- }
-}
-
-void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext)
-{
- m_font = font;
-
- const SimpleFontData* fontData = font->primaryFont();
- m_fonts = new const SimpleFontData*[m_run.length()];
- m_charBuffer = font->isSmallCaps() ? new UChar[m_run.length()] : 0;
-
- ATSUTextLayout layout;
- OSStatus status;
- ATSULayoutOperationOverrideSpecifier overrideSpecifier;
-
- initializeATSUStyle(fontData);
-
- // FIXME: This is currently missing the following required features that the CoreGraphics code path has:
- // - \n, \t, and nonbreaking space render as a space.
-
- UniCharCount runLength = m_run.length();
-
- if (m_charBuffer)
- memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar));
-
- status = ATSUCreateTextLayoutWithTextPtr(
- (m_charBuffer ? m_charBuffer : m_run.characters()),
- 0, // offset
- runLength, // length
- runLength, // total length
- 1, // styleRunCount
- &runLength, // length of style run
- &fontData->m_ATSUStyle,
- &layout);
- if (status != noErr)
- LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
- m_layout = layout;
- ATSUSetTextLayoutRefCon(m_layout, (URefCon)this);
-
- // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext
- // measuring text runs with floatWidthForComplexText is one example.
- // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0)
- // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified! So we're in a bind.
- // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context. Nothing we can do about it (yet).
- CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
-
- ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
- Boolean rtl = m_run.rtl();
- overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
- overrideSpecifier.overrideUPP = overrideLayoutOperation;
- ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag };
- ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) };
- ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier };
-
- status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values);
- if (status != noErr)
- LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
-
- status = ATSUSetTransientFontMatching(layout, YES);
- if (status != noErr)
- LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
-
- m_hasSyntheticBold = false;
- ATSUFontID ATSUSubstituteFont;
- UniCharArrayOffset substituteOffset = 0;
- UniCharCount substituteLength;
- UniCharArrayOffset lastOffset;
- const SimpleFontData* substituteFontData = 0;
-
- while (substituteOffset < runLength) {
- // FIXME: Using ATSUMatchFontsToText() here results in several problems: the CSS font family list is not necessarily followed for the 2nd
- // and onwards unmatched characters; segmented fonts do not work correctly; behavior does not match the simple text and Uniscribe code
- // paths. Change this function to use Font::glyphDataForCharacter() for each character instead.
- lastOffset = substituteOffset;
- status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
- if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
- const FontData* fallbackFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength);
- substituteFontData = fallbackFontData ? fallbackFontData->fontDataForCharacter(m_run[0]) : 0;
- if (substituteFontData) {
- initializeATSUStyle(substituteFontData);
- if (substituteFontData->m_ATSUStyle)
- ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength);
- } else
- substituteFontData = fontData;
- } else {
- substituteOffset = runLength;
- substituteLength = 0;
- }
-
- bool shapedArabic = false;
- bool isSmallCap = false;
- UniCharArrayOffset firstSmallCap = 0;
- const SimpleFontData *r = fontData;
- UniCharArrayOffset i;
- for (i = lastOffset; ; i++) {
- if (i == substituteOffset || i == substituteOffset + substituteLength) {
- if (isSmallCap) {
- isSmallCap = false;
- initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription()));
- ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
- }
- if (i == substituteOffset && substituteLength > 0)
- r = substituteFontData;
- else
- break;
- }
- if (!shapedArabic && WTF::Unicode::isArabicChar(m_run[i]) && !r->shapesArabic()) {
- shapedArabic = true;
- if (!m_charBuffer) {
- m_charBuffer = new UChar[runLength];
- memcpy(m_charBuffer, m_run.characters(), i * sizeof(UChar));
- ATSUTextMoved(layout, m_charBuffer);
- }
- shapeArabic(m_run.characters(), m_charBuffer, runLength, i);
- }
- if (m_run.rtl() && !r->m_ATSUMirrors) {
- UChar mirroredChar = u_charMirror(m_run[i]);
- if (mirroredChar != m_run[i]) {
- if (!m_charBuffer) {
- m_charBuffer = new UChar[runLength];
- memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar));
- ATSUTextMoved(layout, m_charBuffer);
- }
- m_charBuffer[i] = mirroredChar;
- }
- }
- if (m_font->isSmallCaps()) {
- const SimpleFontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription());
- UChar c = m_charBuffer[i];
- UChar newC;
- if (U_GET_GC_MASK(c) & U_GC_M_MASK)
- m_fonts[i] = isSmallCap ? smallCapsData : r;
- else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
- m_charBuffer[i] = newC;
- if (!isSmallCap) {
- isSmallCap = true;
- firstSmallCap = i;
- }
- m_fonts[i] = smallCapsData;
- } else {
- if (isSmallCap) {
- isSmallCap = false;
- initializeATSUStyle(smallCapsData);
- ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
- }
- m_fonts[i] = r;
- }
- } else
- m_fonts[i] = r;
- if (m_fonts[i]->m_syntheticBoldOffset)
- m_hasSyntheticBold = true;
- }
- substituteOffset += substituteLength;
- }
- if (m_run.padding()) {
- float numSpaces = 0;
- unsigned k;
- for (k = 0; k < runLength; k++)
- if (Font::treatAsSpace(m_run[k]))
- numSpaces++;
-
- if (numSpaces == 0)
- m_padPerSpace = 0;
- else
- m_padPerSpace = ceilf(m_run.padding() / numSpaces);
- } else
- m_padPerSpace = 0;
-}
-
-static void disposeATSULayoutParameters(ATSULayoutParameters *params)
-{
- ATSUDisposeTextLayout(params->m_layout);
- delete []params->m_charBuffer;
- delete []params->m_fonts;
-}
-
-FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
-{
- TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
- if (run.directionalOverride()) {
- from++;
- to++;
- }
-
- ATSULayoutParameters params(adjustedRun);
- params.initialize(this);
-
- ATSTrapezoid firstGlyphBounds;
- ItemCount actualNumBounds;
-
- OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
- if (status != noErr || actualNumBounds != 1) {
- static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
- firstGlyphBounds = zeroTrapezoid;
- }
- disposeATSULayoutParameters(&params);
-
- float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x));
- float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x));
-
- FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
-
- if (run.directionalOverride())
- delete []adjustedRun.characters();
-
- return rect;
-}
-
-void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const
-{
- OSStatus status;
-
- int drawPortionLength = to - from;
- TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
- if (run.directionalOverride())
- from++;
-
- ATSULayoutParameters params(adjustedRun);
- params.initialize(this, graphicsContext);
-
- // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
- CGContextRef context = graphicsContext->platformContext();
-
- CGContextTranslateCTM(context, point.x(), point.y());
- status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
- if (status == noErr && params.m_hasSyntheticBold) {
- // Force relayout for the bold pass
- ATSUClearLayoutCache(params.m_layout, 0);
- params.m_syntheticBoldPass = true;
- status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
- }
- CGContextTranslateCTM(context, -point.x(), -point.y());
-
- if (status != noErr)
- // Nothing to do but report the error (dev build only).
- LOG_ERROR("ATSUDrawText() failed(%d)", status);
-
- disposeATSULayoutParameters(&params);
-
- if (run.directionalOverride())
- delete []adjustedRun.characters();
-}
-
-float Font::floatWidthForComplexText(const TextRun& run) const
-{
- if (run.length() == 0)
- return 0;
-
- ATSULayoutParameters params(run);
- params.initialize(this);
-
- OSStatus status;
-
- ATSTrapezoid firstGlyphBounds;
- ItemCount actualNumBounds;
- status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
- if (status != noErr)
- LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
- if (actualNumBounds != 1)
- LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
-
- disposeATSULayoutParameters(&params);
-
- return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
- MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
-}
-
-int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
-{
- TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
-
- ATSULayoutParameters params(adjustedRun);
- params.initialize(this);
-
- UniCharArrayOffset primaryOffset = 0;
-
- // FIXME: No idea how to avoid including partial glyphs.
- // Not even sure if that's the behavior this yields now.
- Boolean isLeading;
- UniCharArrayOffset secondaryOffset = 0;
- OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
- unsigned offset;
- if (status == noErr) {
- offset = (unsigned)primaryOffset;
- if (run.directionalOverride() && offset > 0)
- offset--;
- } else
- // Failed to find offset! Return 0 offset.
- offset = 0;
-
- disposeATSULayoutParameters(&params);
-
- if (run.directionalOverride())
- delete []adjustedRun.characters();
-
- return offset;
-}
-
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
{
CGContextRef cgContext = context->platformContext();
@@ -628,7 +67,7 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons
[[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
}
- CGContextSetFont(cgContext, platformData.m_cgFont);
+ CGContextSetFont(cgContext, platformData.cgFont());
CGAffineTransform matrix = CGAffineTransformIdentity;
if (drawFont)
@@ -644,7 +83,28 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons
CGContextSetFontSize(cgContext, 1.0f);
} else
CGContextSetFontSize(cgContext, platformData.m_size);
-
+
+ IntSize shadowSize;
+ int shadowBlur;
+ Color shadowColor;
+ context->getShadow(shadowSize, shadowBlur, shadowColor);
+
+ bool hasSimpleShadow = context->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur;
+ if (hasSimpleShadow) {
+ // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
+ context->clearShadow();
+ Color fillColor = context->fillColor();
+ Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
+ context->setFillColor(shadowFillColor);
+ CGContextSetTextPosition(cgContext, point.x() + shadowSize.width(), point.y() + shadowSize.height());
+ CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
+ if (font->m_syntheticBoldOffset) {
+ CGContextSetTextPosition(cgContext, point.x() + shadowSize.width() + font->m_syntheticBoldOffset, point.y() + shadowSize.height());
+ CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
+ }
+ context->setFillColor(fillColor);
+ }
+
CGContextSetTextPosition(cgContext, point.x(), point.y());
CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
if (font->m_syntheticBoldOffset) {
@@ -652,6 +112,9 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons
CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
}
+ if (hasSimpleShadow)
+ context->setShadow(shadowSize, shadowBlur, shadowColor);
+
if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing)
CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
}
diff --git a/WebCore/platform/graphics/mac/FontMacATSUI.mm b/WebCore/platform/graphics/mac/FontMacATSUI.mm
new file mode 100644
index 0000000..9a45c5a
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontMacATSUI.mm
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#import "config.h"
+#import "Font.h"
+
+#if USE(ATSUI)
+
+#import "CharacterNames.h"
+#import "GraphicsContext.h"
+#import "Logging.h"
+#import "ShapeArabic.h"
+#import "SimpleFontData.h"
+#import <wtf/OwnArrayPtr.h>
+
+#define SYNTHETIC_OBLIQUE_ANGLE 14
+
+#ifdef __LP64__
+#define URefCon void*
+#else
+#define URefCon UInt32
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+struct ATSULayoutParameters : Noncopyable
+{
+ ATSULayoutParameters(const TextRun& run)
+ : m_run(run)
+ , m_font(0)
+ , m_hasSyntheticBold(false)
+ , m_syntheticBoldPass(false)
+ , m_padPerSpace(0)
+ {}
+
+ ~ATSULayoutParameters()
+ {
+ ATSUDisposeTextLayout(m_layout);
+ }
+
+ void initialize(const Font*, const GraphicsContext* = 0);
+
+ const TextRun& m_run;
+
+ const Font* m_font;
+
+ ATSUTextLayout m_layout;
+ OwnArrayPtr<const SimpleFontData*> m_fonts;
+
+ OwnArrayPtr<UChar> m_charBuffer;
+ bool m_hasSyntheticBold;
+ bool m_syntheticBoldPass;
+ float m_padPerSpace;
+};
+
+static TextRun copyRunForDirectionalOverrideIfNecessary(const TextRun& run, OwnArrayPtr<UChar>& charactersWithOverride)
+{
+ if (!run.directionalOverride())
+ return run;
+
+ charactersWithOverride.set(new UChar[run.length() + 2]);
+ charactersWithOverride[0] = run.rtl() ? rightToLeftOverride : leftToRightOverride;
+ memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length());
+ charactersWithOverride[run.length() + 1] = popDirectionalFormatting;
+
+ TextRun result = run;
+ result.setText(charactersWithOverride.get(), run.length() + 2);
+ return result;
+}
+
+static bool fontHasMirroringInfo(ATSUFontID fontID)
+{
+ ByteCount propTableSize;
+ OSStatus status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize);
+ if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info
+ return true;
+ else if (status != kATSInvalidFontTableAccess) // anything other than a missing table is logged as an error
+ LOG_ERROR("ATSFontGetTable failed (%d)", status);
+
+ return false;
+}
+
+static void disableLigatures(const SimpleFontData* fontData)
+{
+ // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are
+ // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example.
+ // See bugzilla 5166.
+ if (fontData->platformData().allowsLigatures())
+ return;
+
+ ATSUFontFeatureType featureTypes[] = { kLigaturesType };
+ ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector };
+ OSStatus status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors);
+ if (status != noErr)
+ LOG_ERROR("ATSUSetFontFeatures failed (%d) -- ligatures remain enabled", status);
+}
+
+static void initializeATSUStyle(const SimpleFontData* fontData)
+{
+ if (fontData->m_ATSUStyleInitialized)
+ return;
+
+ ATSUFontID fontID = fontData->platformData().m_atsuFontID;
+ if (!fontID) {
+ LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font());
+ return;
+ }
+
+ OSStatus status = ATSUCreateStyle(&fontData->m_ATSUStyle);
+ if (status != noErr)
+ // Who knows how many ATSU functions will crash when passed a NULL style...
+ LOG_ERROR("ATSUCreateStyle failed (%d)", status);
+
+ CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
+ if (fontData->m_font.m_syntheticOblique)
+ transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0));
+ Fixed fontSize = FloatToFixed(fontData->platformData().m_size);
+ ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) };
+ // Turn off automatic kerning until it is supported in the CG code path (bug 6136)
+ Fract kerningInhibitFactor = FloatToFract(1.0);
+
+ ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag };
+ ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor };
+ status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues);
+ if (status != noErr)
+ LOG_ERROR("ATSUSetAttributes failed (%d)", status);
+
+ fontData->m_ATSUMirrors = fontHasMirroringInfo(fontID);
+
+ // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bug 6135 is fixed.
+ disableLigatures(fontData);
+
+ fontData->m_ATSUStyleInitialized = true;
+}
+
+static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon,
+ void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
+{
+ ATSULayoutParameters* params = reinterpret_cast<ATSULayoutParameters*>(iRefCon);
+ OSStatus status;
+ ItemCount count;
+ ATSLayoutRecord *layoutRecords;
+
+ if (params->m_run.applyWordRounding()) {
+ status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
+ if (status != noErr) {
+ *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
+ return status;
+ }
+
+ Fixed lastNativePos = 0;
+ float lastAdjustedPos = 0;
+ const UChar* characters = params->m_charBuffer ? params->m_charBuffer.get() : params->m_run.characters();
+ const SimpleFontData** renderers = params->m_fonts.get();
+ const SimpleFontData* renderer;
+ const SimpleFontData* lastRenderer = 0;
+ ByteCount offset = layoutRecords[0].originalOffset;
+ UChar nextCh = *(UChar *)(((char *)characters)+offset);
+ bool shouldRound = false;
+ bool syntheticBoldPass = params->m_syntheticBoldPass;
+ Fixed syntheticBoldOffset = 0;
+ ATSGlyphRef spaceGlyph = 0;
+ bool hasExtraSpacing = (params->m_font->letterSpacing() || params->m_font->wordSpacing() || params->m_run.padding()) && !params->m_run.spacingDisabled();
+ float padding = params->m_run.padding();
+ // In the CoreGraphics code path, the rounding hack is applied in logical order.
+ // Here it is applied in visual left-to-right order, which may be better.
+ ItemCount lastRoundingChar = 0;
+ ItemCount i;
+ for (i = 1; i < count; i++) {
+ bool isLastChar = i == count - 1;
+ renderer = renderers[offset / 2];
+ if (renderer != lastRenderer) {
+ lastRenderer = renderer;
+ spaceGlyph = renderer->m_spaceGlyph;
+ // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems
+ // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI
+ // does in any of its device-metrics modes.
+ shouldRound = renderer->platformData().roundsGlyphAdvances();
+ if (syntheticBoldPass)
+ syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset);
+ }
+ float width;
+ if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) {
+ width = 0;
+ layoutRecords[i-1].glyphID = spaceGlyph;
+ } else {
+ width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
+ if (shouldRound)
+ width = roundf(width);
+ width += renderer->m_syntheticBoldOffset;
+ if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace))
+ width = renderer->m_adjustedSpaceWidth;
+ }
+ lastNativePos = layoutRecords[i].realPos;
+
+ if (hasExtraSpacing) {
+ if (width && params->m_font->letterSpacing())
+ width +=params->m_font->letterSpacing();
+ if (Font::treatAsSpace(nextCh)) {
+ if (params->m_run.padding()) {
+ if (padding < params->m_padPerSpace) {
+ width += padding;
+ padding = 0;
+ } else {
+ width += params->m_padPerSpace;
+ padding -= params->m_padPerSpace;
+ }
+ }
+ if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
+ width += params->m_font->wordSpacing();
+ }
+ }
+
+ UChar ch = nextCh;
+ offset = layoutRecords[i].originalOffset;
+ // Use space for nextCh at the end of the loop so that we get inside the rounding hack code.
+ // We won't actually round unless the other conditions are satisfied.
+ nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset);
+
+ if (Font::isRoundingHackCharacter(ch))
+ width = ceilf(width);
+ lastAdjustedPos = lastAdjustedPos + width;
+ if (Font::isRoundingHackCharacter(nextCh) && (!isLastChar || params->m_run.applyRunRounding())){
+ if (params->m_run.ltr())
+ lastAdjustedPos = ceilf(lastAdjustedPos);
+ else {
+ float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
+ Fixed rw = FloatToFixed(roundingWidth);
+ ItemCount j;
+ for (j = lastRoundingChar; j < i; j++)
+ layoutRecords[j].realPos += rw;
+ lastRoundingChar = i;
+ lastAdjustedPos += roundingWidth;
+ }
+ }
+ if (syntheticBoldPass) {
+ if (syntheticBoldOffset)
+ layoutRecords[i-1].realPos += syntheticBoldOffset;
+ else
+ layoutRecords[i-1].glyphID = spaceGlyph;
+ }
+ layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
+ }
+
+ status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
+ }
+ *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
+ return noErr;
+}
+
+static inline bool isArabicLamWithAlefLigature(UChar c)
+{
+ return c >= 0xfef5 && c <= 0xfefc;
+}
+
+static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart)
+{
+ while (shapingStart < totalLength) {
+ unsigned shapingEnd;
+ // We do not want to pass a Lam with Alef ligature followed by a space to the shaper,
+ // since we want to be able to identify this sequence as the result of shaping a Lam
+ // followed by an Alef and padding with a space.
+ bool foundLigatureSpace = false;
+ for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd)
+ foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' ';
+ shapingEnd++;
+
+ UErrorCode shapingError = U_ZERO_ERROR;
+ unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError);
+
+ if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) {
+ for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) {
+ if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ')
+ dest[++j] = zeroWidthSpace;
+ }
+ if (foundLigatureSpace) {
+ dest[shapingEnd] = ' ';
+ shapingEnd++;
+ } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) {
+ // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef,
+ // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR.
+ ASSERT(dest[shapingStart] == ' ');
+ dest[shapingStart] = zeroWidthSpace;
+ }
+ } else {
+ // Something went wrong. Abandon shaping and just copy the rest of the buffer.
+ LOG_ERROR("u_shapeArabic failed(%d)", shapingError);
+ shapingEnd = totalLength;
+ memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar));
+ }
+ shapingStart = shapingEnd;
+ }
+}
+
+void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext)
+{
+ m_font = font;
+
+ const SimpleFontData* fontData = font->primaryFont();
+ m_fonts.set(new const SimpleFontData*[m_run.length()]);
+ if (font->isSmallCaps())
+ m_charBuffer.set(new UChar[m_run.length()]);
+
+ ATSUTextLayout layout;
+ OSStatus status;
+ ATSULayoutOperationOverrideSpecifier overrideSpecifier;
+
+ initializeATSUStyle(fontData);
+
+ // FIXME: This is currently missing the following required features that the CoreGraphics code path has:
+ // - \n, \t, and nonbreaking space render as a space.
+
+ UniCharCount runLength = m_run.length();
+
+ if (m_charBuffer)
+ memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar));
+
+ status = ATSUCreateTextLayoutWithTextPtr(
+ (m_charBuffer ? m_charBuffer.get() : m_run.characters()),
+ 0, // offset
+ runLength, // length
+ runLength, // total length
+ 1, // styleRunCount
+ &runLength, // length of style run
+ &fontData->m_ATSUStyle,
+ &layout);
+ if (status != noErr)
+ LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
+ m_layout = layout;
+ ATSUSetTextLayoutRefCon(m_layout, (URefCon)this);
+
+ // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext
+ // measuring text runs with floatWidthForComplexText is one example.
+ // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0)
+ // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified! So we're in a bind.
+ // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context. Nothing we can do about it (yet).
+ CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+
+ ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
+ Boolean rtl = m_run.rtl();
+ overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
+ overrideSpecifier.overrideUPP = overrideLayoutOperation;
+ ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag };
+ ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) };
+ ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier };
+
+ status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values);
+ if (status != noErr)
+ LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
+
+ status = ATSUSetTransientFontMatching(layout, YES);
+ if (status != noErr)
+ LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
+
+ m_hasSyntheticBold = false;
+ ATSUFontID ATSUSubstituteFont;
+ UniCharArrayOffset substituteOffset = 0;
+ UniCharCount substituteLength;
+ UniCharArrayOffset lastOffset;
+ const SimpleFontData* substituteFontData = 0;
+
+ while (substituteOffset < runLength) {
+ // FIXME: Using ATSUMatchFontsToText() here results in several problems: the CSS font family list is not necessarily followed for the 2nd
+ // and onwards unmatched characters; segmented fonts do not work correctly; behavior does not match the simple text and Uniscribe code
+ // paths. Change this function to use Font::glyphDataForCharacter() for each character instead.
+ lastOffset = substituteOffset;
+ status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
+ if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
+ const FontData* fallbackFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength);
+ substituteFontData = fallbackFontData ? fallbackFontData->fontDataForCharacter(m_run[0]) : 0;
+ if (substituteFontData) {
+ initializeATSUStyle(substituteFontData);
+ if (substituteFontData->m_ATSUStyle)
+ ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength);
+ } else
+ substituteFontData = fontData;
+ } else {
+ substituteOffset = runLength;
+ substituteLength = 0;
+ }
+
+ bool shapedArabic = false;
+ bool isSmallCap = false;
+ UniCharArrayOffset firstSmallCap = 0;
+ const SimpleFontData *r = fontData;
+ UniCharArrayOffset i;
+ for (i = lastOffset; ; i++) {
+ if (i == substituteOffset || i == substituteOffset + substituteLength) {
+ if (isSmallCap) {
+ isSmallCap = false;
+ initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription()));
+ ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
+ }
+ if (i == substituteOffset && substituteLength > 0)
+ r = substituteFontData;
+ else
+ break;
+ }
+ if (!shapedArabic && WTF::Unicode::isArabicChar(m_run[i]) && !r->shapesArabic()) {
+ shapedArabic = true;
+ if (!m_charBuffer) {
+ m_charBuffer.set(new UChar[runLength]);
+ memcpy(m_charBuffer.get(), m_run.characters(), i * sizeof(UChar));
+ ATSUTextMoved(layout, m_charBuffer.get());
+ }
+ shapeArabic(m_run.characters(), m_charBuffer.get(), runLength, i);
+ }
+ if (m_run.rtl() && !r->m_ATSUMirrors) {
+ UChar mirroredChar = u_charMirror(m_run[i]);
+ if (mirroredChar != m_run[i]) {
+ if (!m_charBuffer) {
+ m_charBuffer.set(new UChar[runLength]);
+ memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar));
+ ATSUTextMoved(layout, m_charBuffer.get());
+ }
+ m_charBuffer[i] = mirroredChar;
+ }
+ }
+ if (m_font->isSmallCaps()) {
+ const SimpleFontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription());
+ UChar c = m_charBuffer[i];
+ UChar newC;
+ if (U_GET_GC_MASK(c) & U_GC_M_MASK)
+ m_fonts[i] = isSmallCap ? smallCapsData : r;
+ else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
+ m_charBuffer[i] = newC;
+ if (!isSmallCap) {
+ isSmallCap = true;
+ firstSmallCap = i;
+ }
+ m_fonts[i] = smallCapsData;
+ } else {
+ if (isSmallCap) {
+ isSmallCap = false;
+ initializeATSUStyle(smallCapsData);
+ ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
+ }
+ m_fonts[i] = r;
+ }
+ } else
+ m_fonts[i] = r;
+ if (m_fonts[i]->m_syntheticBoldOffset)
+ m_hasSyntheticBold = true;
+ }
+ substituteOffset += substituteLength;
+ }
+ if (m_run.padding()) {
+ float numSpaces = 0;
+ unsigned k;
+ for (k = 0; k < runLength; k++)
+ if (Font::treatAsSpace(m_run[k]))
+ numSpaces++;
+
+ if (numSpaces == 0)
+ m_padPerSpace = 0;
+ else
+ m_padPerSpace = ceilf(m_run.padding() / numSpaces);
+ } else
+ m_padPerSpace = 0;
+}
+
+FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
+{
+ OwnArrayPtr<UChar> charactersWithOverride;
+ TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride);
+ if (run.directionalOverride()) {
+ from++;
+ to++;
+ }
+
+ ATSULayoutParameters params(adjustedRun);
+ params.initialize(this);
+
+ ATSTrapezoid firstGlyphBounds;
+ ItemCount actualNumBounds;
+
+ OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
+ if (status != noErr || actualNumBounds != 1) {
+ static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+ firstGlyphBounds = zeroTrapezoid;
+ }
+
+ float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x));
+ float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x));
+
+ FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
+
+ return rect;
+}
+
+void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const
+{
+ OSStatus status;
+
+ int drawPortionLength = to - from;
+ OwnArrayPtr<UChar> charactersWithOverride;
+ TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride);
+ if (run.directionalOverride())
+ from++;
+
+ ATSULayoutParameters params(adjustedRun);
+ params.initialize(this, graphicsContext);
+
+ // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
+ CGContextRef context = graphicsContext->platformContext();
+ CGContextTranslateCTM(context, point.x(), point.y());
+
+ IntSize shadowSize;
+ int shadowBlur;
+ Color shadowColor;
+ graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor);
+
+ bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur;
+ if (hasSimpleShadow) {
+ // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
+ graphicsContext->clearShadow();
+ Color fillColor = graphicsContext->fillColor();
+ Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
+ graphicsContext->setFillColor(shadowFillColor);
+ CGContextTranslateCTM(context, shadowSize.width(), shadowSize.height());
+ status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
+ if (status == noErr && params.m_hasSyntheticBold) {
+ // Force relayout for the bold pass
+ ATSUClearLayoutCache(params.m_layout, 0);
+ params.m_syntheticBoldPass = true;
+ status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
+ // Force relayout for the next pass
+ ATSUClearLayoutCache(params.m_layout, 0);
+ params.m_syntheticBoldPass = false;
+ }
+ CGContextTranslateCTM(context, -shadowSize.width(), -shadowSize.height());
+ graphicsContext->setFillColor(fillColor);
+ }
+
+ status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
+ if (status == noErr && params.m_hasSyntheticBold) {
+ // Force relayout for the bold pass
+ ATSUClearLayoutCache(params.m_layout, 0);
+ params.m_syntheticBoldPass = true;
+ status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
+ }
+ CGContextTranslateCTM(context, -point.x(), -point.y());
+
+ if (status != noErr)
+ // Nothing to do but report the error (dev build only).
+ LOG_ERROR("ATSUDrawText() failed(%d)", status);
+
+ if (hasSimpleShadow)
+ graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor);
+}
+
+float Font::floatWidthForComplexText(const TextRun& run) const
+{
+ if (run.length() == 0)
+ return 0;
+
+ ATSULayoutParameters params(run);
+ params.initialize(this);
+
+ OSStatus status;
+
+ ATSTrapezoid firstGlyphBounds;
+ ItemCount actualNumBounds;
+ status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
+ if (status != noErr)
+ LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
+ if (actualNumBounds != 1)
+ LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
+
+ return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
+ MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
+}
+
+int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
+{
+ OwnArrayPtr<UChar> charactersWithOverride;
+ TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride);
+
+ ATSULayoutParameters params(adjustedRun);
+ params.initialize(this);
+
+ UniCharArrayOffset primaryOffset = 0;
+
+ // FIXME: No idea how to avoid including partial glyphs.
+ // Not even sure if that's the behavior this yields now.
+ Boolean isLeading;
+ UniCharArrayOffset secondaryOffset = 0;
+ OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
+ unsigned offset;
+ if (status == noErr) {
+ offset = (unsigned)primaryOffset;
+ if (run.directionalOverride() && offset > 0)
+ offset--;
+ } else
+ // Failed to find offset! Return 0 offset.
+ offset = 0;
+
+ return offset;
+}
+
+}
+#endif // USE(ATSUI)
diff --git a/WebCore/platform/graphics/mac/FontMacCoreText.cpp b/WebCore/platform/graphics/mac/FontMacCoreText.cpp
new file mode 100644
index 0000000..5fb9d5d
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontMacCoreText.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "Font.h"
+
+#if USE(CORE_TEXT)
+
+#include "CoreTextController.h"
+#include "FontFallbackList.h"
+#include "GlyphBuffer.h"
+#include "GraphicsContext.h"
+#include "IntRect.h"
+#include "SimpleFontData.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h,
+ int from, int to) const
+{
+ CoreTextController controller(this, run);
+ controller.advance(from);
+ float beforeWidth = controller.runWidthSoFar();
+ controller.advance(to);
+ float afterWidth = controller.runWidthSoFar();
+
+ // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning
+ if (run.rtl()) {
+ float totalWidth = controller.totalWidth();
+ return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h);
+ }
+
+ return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
+}
+
+void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point,
+ int from, int to) const
+{
+ // This glyph buffer holds our glyphs + advances + font data for each glyph.
+ GlyphBuffer glyphBuffer;
+
+ float startX = point.x();
+ CoreTextController controller(this, run);
+ controller.advance(from);
+ float beforeWidth = controller.runWidthSoFar();
+ controller.advance(to, &glyphBuffer);
+
+ // We couldn't generate any glyphs for the run. Give up.
+ if (glyphBuffer.isEmpty())
+ return;
+
+ float afterWidth = controller.runWidthSoFar();
+
+ if (run.rtl()) {
+ startX += controller.totalWidth() + controller.finalRoundingWidth() - afterWidth;
+ for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end)
+ glyphBuffer.swap(i, end);
+ } else
+ startX += beforeWidth;
+
+ // Draw the glyph buffer now at the starting point returned in startX.
+ FloatPoint startPoint(startX, point.y());
+ drawGlyphBuffer(context, glyphBuffer, run, startPoint);
+}
+
+float Font::floatWidthForComplexText(const TextRun& run) const
+{
+ CoreTextController controller(this, run, true);
+ return controller.totalWidth();
+}
+
+int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
+{
+ CoreTextController controller(this, run);
+ return controller.offsetForPosition(x, includePartialGlyphs);
+}
+
+}
+#endif // USE(CORE_TEXT)
diff --git a/WebCore/platform/graphics/mac/FontPlatformData.h b/WebCore/platform/graphics/mac/FontPlatformData.h
index 8f118e0..40a2dbd 100644
--- a/WebCore/platform/graphics/mac/FontPlatformData.h
+++ b/WebCore/platform/graphics/mac/FontPlatformData.h
@@ -1,8 +1,8 @@
/*
- * This file is part of the internal font implementation. It should not be included by anyone other than
- * FontMac.cpp, FontWin.cpp and Font.cpp.
+ * This file is part of the internal font implementation.
+ * It should not be included by source files outside it.
*
- * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -34,46 +34,52 @@ class NSFont;
typedef struct CGFont* CGFontRef;
typedef UInt32 ATSUFontID;
+#ifndef BUILDING_ON_TIGER
+typedef const struct __CTFont* CTFontRef;
+#endif
#include <CoreFoundation/CFBase.h>
#include <objc/objc-auto.h>
+#include <wtf/RetainPtr.h>
namespace WebCore {
-struct FontPlatformData {
- class Deleted {};
-
- FontPlatformData(Deleted)
- : m_syntheticBold(false), m_syntheticOblique(false), m_cgFont(0), m_atsuFontID(0), m_size(0), m_font((NSFont*)-1)
- {}
+#ifndef BUILDING_ON_TIGER
+inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); }
+#endif
- FontPlatformData(float s, bool b, bool o)
- : m_syntheticBold(b)
- , m_syntheticOblique(o)
- , m_cgFont(0)
+struct FontPlatformData {
+ FontPlatformData(float size, bool syntheticBold, bool syntheticOblique)
+ : m_syntheticBold(syntheticBold)
+ , m_syntheticOblique(syntheticOblique)
, m_atsuFontID(0)
- , m_size(s)
+ , m_size(size)
, m_font(0)
+#ifdef BUILDING_ON_TIGER
+ , m_cgFont(0)
+#endif
{
}
- FontPlatformData(NSFont* f = 0, bool b = false, bool o = false);
+ FontPlatformData(NSFont * = 0, bool syntheticBold = false, bool syntheticOblique = false);
FontPlatformData(CGFontRef f, ATSUFontID fontID, float s, bool b , bool o)
- : m_syntheticBold(b), m_syntheticOblique(o), m_cgFont(f), m_atsuFontID(fontID), m_size(s), m_font(0)
+ : m_syntheticBold(b), m_syntheticOblique(o), m_atsuFontID(fontID), m_size(s), m_font(0), m_cgFont(f)
{
}
- FontPlatformData(const FontPlatformData& f);
+ FontPlatformData(const FontPlatformData&);
~FontPlatformData();
+ FontPlatformData(WTF::HashTableDeletedValueType) : m_font(hashTableDeletedFontValue()) { }
+ bool isHashTableDeletedValue() const { return m_font == hashTableDeletedFontValue(); }
+
float size() const { return m_size; }
bool m_syntheticBold;
bool m_syntheticOblique;
-
- CGFontRef m_cgFont; // It is not necessary to refcount this, since either an NSFont owns it or some CachedFont has it referenced.
+
ATSUFontID m_atsuFontID;
float m_size;
@@ -84,6 +90,8 @@ struct FontPlatformData {
return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar));
}
+ const FontPlatformData& operator=(const FontPlatformData& f);
+
bool operator==(const FontPlatformData& other) const
{
return m_font == other.m_font && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique &&
@@ -91,10 +99,26 @@ struct FontPlatformData {
}
NSFont *font() const { return m_font; }
- void setFont(NSFont* font);
+ void setFont(NSFont *font);
+
+ bool roundsGlyphAdvances() const;
+ bool allowsLigatures() const;
+
+#ifndef BUILDING_ON_TIGER
+ CGFontRef cgFont() const { return m_cgFont.get(); }
+#else
+ CGFontRef cgFont() const { return m_cgFont; }
+#endif
private:
+ static NSFont *hashTableDeletedFontValue() { return reinterpret_cast<NSFont *>(-1); }
+
NSFont *m_font;
+#ifndef BUILDING_ON_TIGER
+ RetainPtr<CGFontRef> m_cgFont;
+#else
+ CGFontRef m_cgFont; // It is not necessary to refcount this, since either an NSFont owns it or some CachedFont has it referenced.
+#endif
};
}
diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm
index d1e00d1..15e573d 100644
--- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm
+++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm
@@ -27,19 +27,24 @@
namespace WebCore {
-FontPlatformData::FontPlatformData(NSFont* f, bool b , bool o)
+FontPlatformData::FontPlatformData(NSFont *f, bool b , bool o)
: m_syntheticBold(b), m_syntheticOblique(o), m_font(f)
{
if (f)
CFRetain(f);
m_size = f ? [f pointSize] : 0.0f;
+#ifndef BUILDING_ON_TIGER
+ m_cgFont = CTFontCopyGraphicsFont(toCTFontRef(f), 0);
+ m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(f), 0);
+#else
m_cgFont = wkGetCGFontFromNSFont(f);
m_atsuFontID = wkGetNSFontATSUFontId(f);
+#endif
}
FontPlatformData::FontPlatformData(const FontPlatformData& f)
{
- m_font = (f.m_font && f.m_font != (NSFont*)-1) ? (NSFont*)CFRetain(f.m_font) : f.m_font;
+ m_font = f.m_font && f.m_font != reinterpret_cast<NSFont *>(-1) ? static_cast<const NSFont *>(CFRetain(f.m_font)) : f.m_font;
m_syntheticBold = f.m_syntheticBold;
m_syntheticOblique = f.m_syntheticOblique;
m_size = f.m_size;
@@ -49,22 +54,54 @@ FontPlatformData::FontPlatformData(const FontPlatformData& f)
FontPlatformData:: ~FontPlatformData()
{
- if (m_font && m_font != (NSFont*)-1)
+ if (m_font && m_font != reinterpret_cast<NSFont *>(-1))
CFRelease(m_font);
}
-void FontPlatformData::setFont(NSFont* font) {
+const FontPlatformData& FontPlatformData::operator=(const FontPlatformData& f)
+{
+ m_syntheticBold = f.m_syntheticBold;
+ m_syntheticOblique = f.m_syntheticOblique;
+ m_size = f.m_size;
+ m_cgFont = f.m_cgFont;
+ m_atsuFontID = f.m_atsuFontID;
+ if (m_font == f.m_font)
+ return *this;
+ if (f.m_font && f.m_font != reinterpret_cast<NSFont *>(-1))
+ CFRetain(f.m_font);
+ if (m_font && m_font != reinterpret_cast<NSFont *>(-1))
+ CFRelease(m_font);
+ m_font = f.m_font;
+ return *this;
+}
+
+void FontPlatformData::setFont(NSFont *font)
+{
if (m_font == font)
return;
if (font)
CFRetain(font);
- if (m_font && m_font != (NSFont*)-1)
+ if (m_font && m_font != reinterpret_cast<NSFont *>(-1))
CFRelease(m_font);
m_font = font;
m_size = font ? [font pointSize] : 0.0f;
+#ifndef BUILDING_ON_TIGER
+ m_cgFont = CTFontCopyGraphicsFont(toCTFontRef(font), 0);
+ m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(font), 0);
+#else
m_cgFont = wkGetCGFontFromNSFont(font);
m_atsuFontID = wkGetNSFontATSUFontId(font);
+#endif
}
+bool FontPlatformData::roundsGlyphAdvances() const
+{
+ return [m_font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
+}
+
+bool FontPlatformData::allowsLigatures() const
+{
+ return ![[m_font coveredCharacterSet] characterIsMember:'a'];
}
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp
index b9f2da3..143e665 100644
--- a/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp
+++ b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp
@@ -37,6 +37,21 @@ namespace WebCore {
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData)
{
+ bool haveGlyphs = false;
+
+#ifndef BUILDING_ON_TIGER
+ Vector<CGGlyph, 512> glyphs(bufferLength);
+ wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength);
+
+ for (unsigned i = 0; i < length; ++i) {
+ if (!glyphs[i])
+ setGlyphDataForIndex(offset + i, 0, 0);
+ else {
+ setGlyphDataForIndex(offset + i, glyphs[i], fontData);
+ haveGlyphs = true;
+ }
+ }
+#else
// Use an array of long so we get good enough alignment.
long glyphVector[(GLYPH_VECTOR_SIZE + sizeof(long) - 1) / sizeof(long)];
@@ -56,7 +71,6 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b
return false;
}
- bool haveGlyphs = false;
ATSLayoutRecord* glyphRecord = (ATSLayoutRecord*)wkGetGlyphVectorFirstRecord(glyphVector);
for (unsigned i = 0; i < length; i++) {
Glyph glyph = glyphRecord->glyphID;
@@ -69,6 +83,7 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b
glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector));
}
wkClearGlyphVector(&glyphVector);
+#endif
return haveGlyphs;
}
diff --git a/WebCore/platform/graphics/mac/IconMac.mm b/WebCore/platform/graphics/mac/IconMac.mm
index cda73a0..63abe59 100644
--- a/WebCore/platform/graphics/mac/IconMac.mm
+++ b/WebCore/platform/graphics/mac/IconMac.mm
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -28,10 +28,6 @@
namespace WebCore {
-Icon::Icon()
-{
-}
-
Icon::Icon(NSImage *image)
: m_nsImage(image)
{
@@ -43,7 +39,7 @@ Icon::~Icon()
{
}
-PassRefPtr<Icon> Icon::newIconForFile(const String& filename)
+PassRefPtr<Icon> Icon::createIconForFile(const String& filename)
{
// Don't pass relative filenames -- we don't want a result that depends on the current directory.
// Need 0U here to disambiguate String::operator[] from operator(NSString*, int)[]
@@ -54,7 +50,23 @@ PassRefPtr<Icon> Icon::newIconForFile(const String& filename)
if (!image)
return 0;
- return new Icon(image);
+ return adoptRef(new Icon(image));
+}
+
+PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames)
+{
+ if (filenames.isEmpty())
+ return 0;
+#ifdef BUILDING_ON_TIGER
+ // FIXME: find a better image to use on Tiger.
+ return createIconForFile(filenames[0]);
+#else
+ NSImage* image = [NSImage imageNamed:NSImageNameMultipleDocuments];
+ if (!image)
+ return 0;
+
+ return adoptRef(new Icon(image));
+#endif
}
void Icon::paint(GraphicsContext* context, const IntRect& rect)
diff --git a/WebCore/platform/graphics/mac/ImageMac.mm b/WebCore/platform/graphics/mac/ImageMac.mm
index 0b14d71..a0d257b 100644
--- a/WebCore/platform/graphics/mac/ImageMac.mm
+++ b/WebCore/platform/graphics/mac/ImageMac.mm
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,8 +30,12 @@
#import "FoundationExtras.h"
#import "GraphicsContext.h"
#import "PlatformString.h"
-#import "WebCoreFrameBridge.h"
-#import "WebCoreSystemInterface.h"
+
+@interface WebCoreBundleFinder : NSObject
+@end
+
+@implementation WebCoreBundleFinder
+@end
namespace WebCore {
@@ -48,24 +52,22 @@ void BitmapImage::invalidatePlatformData()
m_tiffRep = 0;
}
-Image* Image::loadPlatformResource(const char *name)
+PassRefPtr<Image> Image::loadPlatformResource(const char *name)
{
- static BitmapImage nullImage;
-
- NSBundle *bundle = [NSBundle bundleForClass:[WebCoreFrameBridge class]];
+ NSBundle *bundle = [NSBundle bundleForClass:[WebCoreBundleFinder class]];
NSString *imagePath = [bundle pathForResource:[NSString stringWithUTF8String:name] ofType:@"tiff"];
NSData *namedImageData = [NSData dataWithContentsOfFile:imagePath];
if (namedImageData) {
- Image* image = new BitmapImage;
+ RefPtr<Image> image = BitmapImage::create();
image->setData(SharedBuffer::wrapNSData(namedImageData), true);
- return image;
+ return image.release();
}
-
+
// We have reports indicating resource loads are failing, but we don't yet know the root cause(s).
// Two theories are bad installs (image files are missing), and too-many-open-files.
// See rdar://5607381
ASSERT_NOT_REACHED();
- return &nullImage;
+ return Image::nullImage();
}
CFDataRef BitmapImage::getTIFFRepresentation()
diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h
index 8975d9b..3f18ab4 100644
--- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h
+++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h
@@ -36,14 +36,20 @@
#import <QTKit/QTTime.h>
@class QTMovie;
@class QTMovieView;
+@class QTVideoRendererWebKitOnly;
@class WebCoreMovieObserver;
#else
class QTMovie;
class QTMovieView;
class QTTime;
+class QTVideoRendererWebKitOnly;
class WebCoreMovieObserver;
#endif
+#ifndef DRAW_FRAME_RATE
+#define DRAW_FRAME_RATE 0
+#endif
+
namespace WebCore {
class MediaPlayerPrivate : Noncopyable {
@@ -99,8 +105,12 @@ public:
private:
void createQTMovie(const String& url);
+ void setUpVideoRendering();
+ void tearDownVideoRendering();
void createQTMovieView();
void detachQTMovieView();
+ void createQTVideoRenderer();
+ void destroyQTVideoRenderer();
QTTime createQTTime(float time) const;
void updateStates();
@@ -115,6 +125,7 @@ private:
MediaPlayer* m_player;
RetainPtr<QTMovie> m_qtMovie;
RetainPtr<QTMovieView> m_qtMovieView;
+ RetainPtr<QTVideoRendererWebKitOnly> m_qtVideoRenderer;
RetainPtr<WebCoreMovieObserver> m_objcObserver;
float m_seekTo;
float m_endTime;
@@ -124,6 +135,12 @@ private:
MediaPlayer::ReadyState m_readyState;
bool m_startedPlaying;
bool m_isStreaming;
+ bool m_visible;
+#if DRAW_FRAME_RATE
+ int m_frameCountWhilePlaying;
+ double m_timeStartedPlaying;
+ double m_timeStoppedPlaying;
+#endif
};
}
diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm
index 85c7a9e..0ec56d6 100644
--- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm
+++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,12 +32,20 @@
#import "BlockExceptions.h"
#import "GraphicsContext.h"
#import "KURL.h"
-#import "ScrollView.h"
+#import "FrameView.h"
#import "SoftLinking.h"
#import "WebCoreSystemInterface.h"
#import <QTKit/QTKit.h>
#import <objc/objc-runtime.h>
+#if DRAW_FRAME_RATE
+#import "Font.h"
+#import "Frame.h"
+#import "Document.h"
+#import "RenderObject.h"
+#import "RenderStyle.h"
+#endif
+
#ifdef BUILDING_ON_TIGER
static IMP method_setImplementation(Method m, IMP imp)
{
@@ -59,6 +67,7 @@ SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *)
SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *)
SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *)
SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *)
SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *)
SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *)
SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *)
@@ -74,6 +83,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *)
SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *)
SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *)
SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *)
+SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *)
#define QTMovie getQTMovieClass()
#define QTMovieView getQTMovieViewClass()
@@ -83,6 +93,7 @@ SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *)
#define QTMediaTypeSound getQTMediaTypeSound()
#define QTMediaTypeText getQTMediaTypeText()
#define QTMediaTypeVideo getQTMediaTypeVideo()
+#define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute()
#define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute()
#define QTMovieDidEndNotification getQTMovieDidEndNotification()
#define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute()
@@ -98,6 +109,7 @@ SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *)
#define QTMovieURLAttribute getQTMovieURLAttribute()
#define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification()
#define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute()
+#define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification()
// Older versions of the QTKit header don't have these constants.
#if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0
@@ -116,10 +128,12 @@ using namespace std;
@interface WebCoreMovieObserver : NSObject
{
MediaPlayerPrivate* m_callback;
+ NSView* m_view;
BOOL m_delayCallbacks;
}
-(id)initWithCallback:(MediaPlayerPrivate*)callback;
-(void)disconnect;
+-(void)setView:(NSView*)view;
-(void)repaint;
-(void)setDelayCallbacks:(BOOL)shouldDelay;
-(void)loadStateChanged:(NSNotification *)notification;
@@ -129,11 +143,19 @@ using namespace std;
-(void)didEnd:(NSNotification *)notification;
@end
+@protocol WebKitVideoRenderingDetails
+-(void)setMovie:(id)movie;
+-(void)drawInRect:(NSRect)rect;
+@end
+
namespace WebCore {
static const float endPointTimerInterval = 0.020f;
+
+#ifdef BUILDING_ON_TIGER
static const long minimumQuickTimeVersion = 0x07300000; // 7.3
-
+#endif
+
MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
: m_player(player)
, m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this])
@@ -145,12 +167,18 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
, m_readyState(MediaPlayer::DataUnavailable)
, m_startedPlaying(false)
, m_isStreaming(false)
+ , m_visible(false)
+#if DRAW_FRAME_RATE
+ , m_frameCountWhilePlaying(0)
+ , m_timeStartedPlaying(0)
+ , m_timeStoppedPlaying(0)
+#endif
{
}
MediaPlayerPrivate::~MediaPlayerPrivate()
{
- detachQTMovieView();
+ tearDownVideoRendering();
[[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
[m_objcObserver.get() disconnect];
@@ -160,23 +188,29 @@ void MediaPlayerPrivate::createQTMovie(const String& url)
{
[[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
- m_qtMovie = 0;
+ if (m_qtMovie) {
+ destroyQTVideoRenderer();
+ m_qtMovie = 0;
+ }
// Disable streaming support for now, <rdar://problem/5693967>
- if (url.startsWith("rtsp:"))
+ if (protocolIs(url, "rtsp"))
return;
-
- NSDictionary* movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
- KURL(url.deprecatedString()).getNSURL(), QTMovieURLAttribute,
+
+ NSURL *cocoaURL = KURL(url);
+ NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
+ cocoaURL, QTMovieURLAttribute,
[NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute,
[NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute,
+ [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute,
+ [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", // FIXME: Use defined attribute when required version of QT supports this attribute
nil];
NSError* error = nil;
m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]);
// FIXME: Find a proper way to detect streaming content.
- m_isStreaming = url.startsWith("rtsp:");
+ m_isStreaming = protocolIs(url, "rtsp");
if (!m_qtMovie)
return;
@@ -220,13 +254,16 @@ static void mainThreadSetNeedsDisplay(id self, SEL _cmd)
[delegate repaint];
}
+static Class QTVideoRendererClass()
+{
+ static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly");
+ return QTVideoRendererWebKitOnlyClass;
+}
+
void MediaPlayerPrivate::createQTMovieView()
{
detachQTMovieView();
- if (!m_player->m_parentWidget || !m_qtMovie)
- return;
-
static bool addedCustomMethods = false;
if (!addedCustomMethods) {
Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView");
@@ -241,7 +278,7 @@ void MediaPlayerPrivate::createQTMovieView()
m_qtMovieView.adoptNS([[QTMovieView alloc] init]);
setRect(m_player->rect());
- NSView* parentView = static_cast<ScrollView*>(m_player->m_parentWidget)->getDocumentView();
+ NSView* parentView = m_player->m_frameView->documentView();
[parentView addSubview:m_qtMovieView.get()];
#ifdef BUILDING_ON_TIGER
// setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy
@@ -249,17 +286,24 @@ void MediaPlayerPrivate::createQTMovieView()
#else
[m_qtMovieView.get() setDelegate:m_objcObserver.get()];
#endif
+ [m_objcObserver.get() setView:m_qtMovieView.get()];
[m_qtMovieView.get() setMovie:m_qtMovie.get()];
[m_qtMovieView.get() setControllerVisible:NO];
[m_qtMovieView.get() setPreservesAspectRatio:NO];
// the area not covered by video should be transparent
[m_qtMovieView.get() setFillColor:[NSColor clearColor]];
- wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES);
+
+ // If we're in a media document, allow QTMovieView to render in its default mode;
+ // otherwise tell it to draw synchronously.
+ // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested.
+ if (!m_player->inMediaDocument())
+ wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES);
}
void MediaPlayerPrivate::detachQTMovieView()
{
if (m_qtMovieView) {
+ [m_objcObserver.get() setView:nil];
#ifdef BUILDING_ON_TIGER
// setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy
[m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:nil];
@@ -271,6 +315,59 @@ void MediaPlayerPrivate::detachQTMovieView()
}
}
+void MediaPlayerPrivate::createQTVideoRenderer()
+{
+ destroyQTVideoRenderer();
+
+ m_qtVideoRenderer.adoptNS([[QTVideoRendererClass() alloc] init]);
+ if (!m_qtVideoRenderer)
+ return;
+
+ // associate our movie with our instance of QTVideoRendererWebKitOnly
+ [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()];
+
+ // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification
+ [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
+ selector:@selector(newImageAvailable:)
+ name:QTVideoRendererWebKitOnlyNewImageAvailableNotification
+ object:m_qtVideoRenderer.get()];
+}
+
+void MediaPlayerPrivate::destroyQTVideoRenderer()
+{
+ if (!m_qtVideoRenderer)
+ return;
+
+ // stop observing the renderer's notifications before we toss it
+ [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()
+ name:QTVideoRendererWebKitOnlyNewImageAvailableNotification
+ object:m_qtVideoRenderer.get()];
+
+ // disassociate our movie from our instance of QTVideoRendererWebKitOnly
+ [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:nil];
+
+ m_qtVideoRenderer = nil;
+}
+
+void MediaPlayerPrivate::setUpVideoRendering()
+{
+ if (!m_player->m_frameView || !m_qtMovie)
+ return;
+
+ if (m_player->inMediaDocument() || !QTVideoRendererClass() )
+ createQTMovieView();
+ else
+ createQTVideoRenderer();
+}
+
+void MediaPlayerPrivate::tearDownVideoRendering()
+{
+ if (m_qtMovieView)
+ detachQTMovieView();
+ else
+ destroyQTVideoRenderer();
+}
+
QTTime MediaPlayerPrivate::createQTTime(float time) const
{
if (!m_qtMovie)
@@ -295,8 +392,6 @@ void MediaPlayerPrivate::load(const String& url)
[m_objcObserver.get() setDelayCallbacks:YES];
createQTMovie(url);
- if (m_player->visible())
- createQTMovieView();
[m_objcObserver.get() loadStateChanged:nil];
[m_objcObserver.get() setDelayCallbacks:NO];
@@ -307,6 +402,9 @@ void MediaPlayerPrivate::play()
if (!m_qtMovie)
return;
m_startedPlaying = true;
+#if DRAW_FRAME_RATE
+ m_frameCountWhilePlaying = 0;
+#endif
[m_objcObserver.get() setDelayCallbacks:YES];
[m_qtMovie.get() setRate:m_player->rate()];
[m_objcObserver.get() setDelayCallbacks:NO];
@@ -318,6 +416,9 @@ void MediaPlayerPrivate::pause()
if (!m_qtMovie)
return;
m_startedPlaying = false;
+#if DRAW_FRAME_RATE
+ m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate];
+#endif
[m_objcObserver.get() setDelayCallbacks:YES];
[m_qtMovie.get() stop];
[m_objcObserver.get() setDelayCallbacks:NO];
@@ -521,7 +622,7 @@ void MediaPlayerPrivate::cancelLoad()
if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
return;
- detachQTMovieView();
+ tearDownVideoRendering();
m_qtMovie = nil;
updateStates();
@@ -534,14 +635,14 @@ void MediaPlayerPrivate::updateStates()
long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError);
- if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData) {
+ if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) {
unsigned enabledTrackCount;
disableUnsupportedTracks(enabledTrackCount);
// FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692>
if (!enabledTrackCount)
loadState = QTMovieLoadStateError;
}
-
+
// "Loaded" is reserved for fully buffered movies, never the case when streaming
if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
if (m_networkState < MediaPlayer::Loaded)
@@ -576,6 +677,9 @@ void MediaPlayerPrivate::updateStates()
m_player->networkStateChanged();
if (m_readyState != oldReadyState)
m_player->readyStateChanged();
+
+ if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::LoadedMetaData && m_player->visible())
+ setUpVideoRendering();
}
void MediaPlayerPrivate::loadStateChanged()
@@ -602,6 +706,9 @@ void MediaPlayerPrivate::didEnd()
{
m_endPointTimer.stop();
m_startedPlaying = false;
+#if DRAW_FRAME_RATE
+ m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate];
+#endif
updateStates();
m_player->timeChanged();
}
@@ -610,23 +717,42 @@ void MediaPlayerPrivate::setRect(const IntRect& r)
{
if (!m_qtMovieView)
return;
- // We don't really need the QTMovieView in any specific location so let's just get it out of the way
- // where it won't intercept events or try to bring up the context menu.
- IntRect farAwayButCorrectSize(r);
- farAwayButCorrectSize.move(-1000000, -1000000);
- [m_qtMovieView.get() setFrame:farAwayButCorrectSize];
+
+ if (m_player->inMediaDocument())
+ // We need the QTMovieView to be placed in the proper location for document mode.
+ [m_qtMovieView.get() setFrame:r];
+ else {
+ // We don't really need the QTMovieView in any specific location so let's just get it out of the way
+ // where it won't intercept events or try to bring up the context menu.
+ IntRect farAwayButCorrectSize(r);
+ farAwayButCorrectSize.move(-1000000, -1000000);
+ [m_qtMovieView.get() setFrame:farAwayButCorrectSize];
+ }
}
void MediaPlayerPrivate::setVisible(bool b)
{
- if (b)
- createQTMovieView();
- else
- detachQTMovieView();
+ if (m_visible != b) {
+ m_visible = b;
+ if (b) {
+ if (m_networkState >= MediaPlayer::LoadedMetaData)
+ setUpVideoRendering();
+ } else
+ tearDownVideoRendering();
+ }
}
void MediaPlayerPrivate::repaint()
{
+#if DRAW_FRAME_RATE
+ if (m_startedPlaying) {
+ m_frameCountWhilePlaying++;
+ // to eliminate preroll costs from our calculation,
+ // our frame rate calculation excludes the first frame drawn after playback starts
+ if (1==m_frameCountWhilePlaying)
+ m_timeStartedPlaying = [NSDate timeIntervalSinceReferenceDate];
+ }
+#endif
m_player->repaint();
}
@@ -635,16 +761,52 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r)
if (context->paintingDisabled())
return;
NSView *view = m_qtMovieView.get();
- if (view == nil)
+ id qtVideoRenderer = m_qtVideoRenderer.get();
+ if (!view && !qtVideoRenderer)
return;
+
[m_objcObserver.get() setDelayCallbacks:YES];
BEGIN_BLOCK_OBJC_EXCEPTIONS;
context->save();
context->translate(r.x(), r.y() + r.height());
context->scale(FloatSize(1.0f, -1.0f));
+ context->setImageInterpolationQuality(InterpolationLow);
IntRect paintRect(IntPoint(0, 0), IntSize(r.width(), r.height()));
NSGraphicsContext* newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context->platformContext() flipped:NO];
- [view displayRectIgnoringOpacity:paintRect inContext:newContext];
+
+ // draw the current video frame
+ if (qtVideoRenderer) {
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:newContext];
+ [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect];
+ [NSGraphicsContext restoreGraphicsState];
+ } else
+ [view displayRectIgnoringOpacity:paintRect inContext:newContext];
+
+#if DRAW_FRAME_RATE
+ // Draw the frame rate only after having played more than 10 frames.
+ if (m_frameCountWhilePlaying > 10) {
+ Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL;
+ Document* document = frame ? frame->document() : NULL;
+ RenderObject* renderer = document ? document->renderer() : NULL;
+ RenderStyle* styleToUse = renderer ? renderer->style() : NULL;
+ if (styleToUse) {
+ double frameRate = (m_frameCountWhilePlaying - 1) / ( m_startedPlaying ? ([NSDate timeIntervalSinceReferenceDate] - m_timeStartedPlaying) :
+ (m_timeStoppedPlaying - m_timeStartedPlaying) );
+ String text = String::format("%1.2f", frameRate);
+ TextRun textRun(text.characters(), text.length());
+ const Color color(255, 0, 0);
+ context->scale(FloatSize(1.0f, -1.0f));
+ context->setFont(styleToUse->font());
+ context->setStrokeColor(color);
+ context->setStrokeStyle(SolidStroke);
+ context->setStrokeThickness(1.0f);
+ context->setFillColor(color);
+ context->drawText(textRun, IntPoint(2, -3));
+ }
+ }
+#endif
+
context->restore();
END_BLOCK_OBJC_EXCEPTIONS;
[m_objcObserver.get() setDelayCallbacks:NO];
@@ -668,9 +830,9 @@ void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
bool MediaPlayerPrivate::isAvailable()
{
+#ifdef BUILDING_ON_TIGER
SInt32 version;
OSErr result;
- // This Carbon API is available in 64 bit too
result = Gestalt(gestaltQuickTime, &version);
if (result != noErr) {
LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
@@ -681,6 +843,10 @@ bool MediaPlayerPrivate::isAvailable()
return false;
}
return true;
+#else
+ // On 10.5 and higher, QuickTime will always be new enough for <video> and <audio> support, so we just check that the framework can be loaded.
+ return QTKitLibrary();
+#endif
}
void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount)
@@ -784,6 +950,17 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount)
m_callback = 0;
}
+-(NSMenu*)menuForEventDelegate:(NSEvent*)theEvent
+{
+ // Get the contextual menu from the QTMovieView's superview, the frame view
+ return [[m_view superview] menuForEvent:theEvent];
+}
+
+-(void)setView:(NSView*)view
+{
+ m_view = view;
+}
+
-(void)repaint
{
if (m_delayCallbacks)
@@ -832,6 +1009,11 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount)
m_callback->didEnd();
}
+- (void)newImageAvailable:(NSNotification *)notification
+{
+ [self repaint];
+}
+
- (void)setDelayCallbacks:(BOOL)shouldDelay
{
m_delayCallbacks = shouldDelay;
diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm
index 1f45c94..4ee5933 100644
--- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm
+++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm
@@ -56,13 +56,14 @@ static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x * (c
bool initFontData(SimpleFontData* fontData)
{
- if (!fontData->m_font.m_cgFont)
+ if (!fontData->m_font.cgFont())
return false;
+#ifdef BUILDING_ON_TIGER
ATSUStyle fontStyle;
if (ATSUCreateStyle(&fontStyle) != noErr)
return false;
-
+
ATSUFontID fontId = fontData->m_font.m_atsuFontID;
if (!fontId) {
ATSUDisposeStyle(fontStyle);
@@ -84,6 +85,7 @@ bool initFontData(SimpleFontData* fontData)
}
ATSUDisposeStyle(fontStyle);
+#endif
return true;
}
@@ -96,16 +98,63 @@ static NSString *webFallbackFontFamily(void)
return webFallbackFontFamily.get();
}
+#if !ERROR_DISABLED
+#ifdef __LP64__
+static NSString* pathFromFont(NSFont*)
+{
+ // FMGetATSFontRefFromFont is not available in 64-bit. As pathFromFont is only used for debugging
+ // purposes, returning nil is acceptable.
+ return nil;
+}
+#else
+static NSString* pathFromFont(NSFont *font)
+{
+#ifndef BUILDING_ON_TIGER
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(CTFontGetPlatformFont(toCTFontRef(font), 0));
+#else
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(wkGetNSFontATSUFontId(font));
+#endif
+ FSRef fileRef;
+
+#ifndef BUILDING_ON_TIGER
+ OSStatus status = ATSFontGetFileReference(atsFont, &fileRef);
+ if (status != noErr)
+ return nil;
+#else
+ FSSpec oFile;
+ OSStatus status = ATSFontGetFileSpecification(atsFont, &oFile);
+ if (status != noErr)
+ return nil;
+
+ status = FSpMakeFSRef(&oFile, &fileRef);
+ if (status != noErr)
+ return nil;
+#endif
+
+ UInt8 filePathBuffer[PATH_MAX];
+ status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX);
+ if (status == noErr)
+ return [NSString stringWithUTF8String:(const char*)filePathBuffer];
+
+ return nil;
+}
+#endif // __LP64__
+#endif // !ERROR_DISABLED
+
void SimpleFontData::platformInit()
{
+#ifdef BUILDING_ON_TIGER
m_styleGroup = 0;
+#endif
+#if USE(ATSUI)
m_ATSUStyleInitialized = false;
m_ATSUMirrors = false;
m_checkedShapesArabic = false;
m_shapesArabic = false;
+#endif
m_syntheticBoldOffset = m_font.m_syntheticBold ? 1.0f : 0.f;
-
+
bool failedSetup = false;
if (!initFontData(this)) {
// Ack! Something very bad happened, like a corrupt font.
@@ -126,9 +175,12 @@ void SimpleFontData::platformInit()
#if !ERROR_DISABLED
RetainPtr<NSFont> initialFont = m_font.font();
#endif
- m_font.setFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toFamily:fallbackFontFamily]);
+ if (m_font.font())
+ m_font.setFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toFamily:fallbackFontFamily]);
+ else
+ m_font.setFont([NSFont fontWithName:fallbackFontFamily size:m_font.size()]);
#if !ERROR_DISABLED
- NSString *filePath = wkPathFromFont(initialFont.get());
+ NSString *filePath = pathFromFont(initialFont.get());
if (!filePath)
filePath = @"not known";
#endif
@@ -165,7 +217,15 @@ void SimpleFontData::platformInit()
int iAscent;
int iDescent;
int iLineGap;
- wkGetFontMetrics(m_font.m_cgFont, &iAscent, &iDescent, &iLineGap, &m_unitsPerEm);
+#ifdef BUILDING_ON_TIGER
+ wkGetFontMetrics(m_font.cgFont(), &iAscent, &iDescent, &iLineGap, &m_unitsPerEm);
+#else
+ iAscent = CGFontGetAscent(m_font.cgFont());
+ iDescent = CGFontGetDescent(m_font.cgFont());
+ iLineGap = CGFontGetLeading(m_font.cgFont());
+ m_unitsPerEm = CGFontGetUnitsPerEm(m_font.cgFont());
+#endif
+
float pointSize = m_font.m_size;
float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize;
float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize;
@@ -179,6 +239,13 @@ void SimpleFontData::platformInit()
NSString *familyName = [m_font.font() familyName];
if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"])
fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f);
+ else if ([familyName isEqualToString:@"Geeza Pro"]) {
+ // Geeza Pro has glyphs that draw slightly above the ascent or far below the descent. Adjust
+ // those vertical metrics to better match reality, so that diacritics at the bottom of one line
+ // do not overlap diacritics at the top of the next line.
+ fAscent *= 1.08f;
+ fDescent *= 2.f;
+ }
m_ascent = lroundf(fAscent);
m_descent = lroundf(fDescent);
@@ -209,11 +276,14 @@ void SimpleFontData::platformInit()
void SimpleFontData::platformDestroy()
{
+#ifdef BUILDING_ON_TIGER
if (m_styleGroup)
wkReleaseStyleGroup(m_styleGroup);
-
+#endif
+#if USE(ATSUI)
if (m_ATSUStyleInitialized)
ATSUDisposeStyle(m_ATSUStyle);
+#endif
}
SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
@@ -290,13 +360,14 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
float pointSize = m_font.m_size;
CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
CGSize advance;
- if (!wkGetGlyphTransformedAdvances(m_font.m_cgFont, font, &m, &glyph, &advance)) {
+ if (!wkGetGlyphTransformedAdvances(m_font.cgFont(), font, &m, &glyph, &advance)) {
LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
advance.width = 0;
}
return advance.width + m_syntheticBoldOffset;
}
+#if USE(ATSUI)
void SimpleFontData::checkShapesArabic() const
{
ASSERT(!m_checkedShapesArabic);
@@ -325,5 +396,40 @@ void SimpleFontData::checkShapesArabic() const
LOG_ERROR("ATSFontGetTable failed (%d)", status);
}
}
+#endif
+#if USE(CORE_TEXT)
+CTFontRef SimpleFontData::getCTFont() const
+{
+ if (getNSFont())
+ return toCTFontRef(getNSFont());
+ if (!m_CTFont)
+ m_CTFont.adoptCF(CTFontCreateWithGraphicsFont(m_font.cgFont(), m_font.size(), NULL, NULL));
+ return m_CTFont.get();
}
+
+CFDictionaryRef SimpleFontData::getCFStringAttributes() const
+{
+ if (m_CFStringAttributes)
+ return m_CFStringAttributes.get();
+
+ static const float kerningAdjustmentValue = 0;
+ static CFNumberRef kerningAdjustment = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &kerningAdjustmentValue);
+
+ static const int ligaturesNotAllowedValue = 0;
+ static CFNumberRef ligaturesNotAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesNotAllowedValue);
+
+ static const int ligaturesAllowedValue = 1;
+ static CFNumberRef ligaturesAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesAllowedValue);
+
+ static const void* attributeKeys[] = { kCTFontAttributeName, kCTKernAttributeName, kCTLigatureAttributeName };
+ const void* attributeValues[] = { getCTFont(), kerningAdjustment, platformData().allowsLigatures() ? ligaturesAllowed : ligaturesNotAllowed };
+
+ m_CFStringAttributes.adoptCF(CFDictionaryCreate(NULL, attributeKeys, attributeValues, sizeof(attributeKeys) / sizeof(*attributeKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+
+ return m_CFStringAttributes.get();
+}
+
+#endif
+
+} // namespace WebCore