summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/mac
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/mac')
-rw-r--r--WebCore/platform/graphics/mac/ColorMac.h50
-rw-r--r--WebCore/platform/graphics/mac/ColorMac.mm169
-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/FloatPointMac.mm45
-rw-r--r--WebCore/platform/graphics/mac/FloatRectMac.mm45
-rw-r--r--WebCore/platform/graphics/mac/FloatSizeMac.mm45
-rw-r--r--WebCore/platform/graphics/mac/FontCacheMac.mm202
-rw-r--r--WebCore/platform/graphics/mac/FontCustomPlatformData.cpp84
-rw-r--r--WebCore/platform/graphics/mac/FontCustomPlatformData.h53
-rw-r--r--WebCore/platform/graphics/mac/FontMac.mm122
-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.h126
-rw-r--r--WebCore/platform/graphics/mac/FontPlatformDataMac.mm107
-rw-r--r--WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp91
-rw-r--r--WebCore/platform/graphics/mac/GraphicsContextMac.mm165
-rw-r--r--WebCore/platform/graphics/mac/IconMac.mm84
-rw-r--r--WebCore/platform/graphics/mac/ImageMac.mm125
-rw-r--r--WebCore/platform/graphics/mac/IntPointMac.mm44
-rw-r--r--WebCore/platform/graphics/mac/IntRectMac.mm49
-rw-r--r--WebCore/platform/graphics/mac/IntSizeMac.mm44
-rw-r--r--WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h149
-rw-r--r--WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm1024
-rw-r--r--WebCore/platform/graphics/mac/SimpleFontDataMac.mm435
25 files changed, 4630 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/mac/ColorMac.h b/WebCore/platform/graphics/mac/ColorMac.h
new file mode 100644
index 0000000..3be9094
--- /dev/null
+++ b/WebCore/platform/graphics/mac/ColorMac.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 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.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY 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.
+ */
+
+#ifndef ColorMac_h
+#define ColorMac_h
+
+#include "Color.h"
+
+#ifdef __OBJC__
+@class NSColor;
+#else
+class NSColor;
+#endif
+
+namespace WebCore {
+
+ Color colorFromNSColor(NSColor *);
+ NSColor* nsColor(const Color&);
+
+ bool usesTestModeFocusRingColor();
+ void setUsesTestModeFocusRingColor(bool);
+
+}
+
+#endif
diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm
new file mode 100644
index 0000000..96fdc39
--- /dev/null
+++ b/WebCore/platform/graphics/mac/ColorMac.mm
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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.
+ */
+
+#import "config.h"
+#import "Color.h"
+#import "ColorMac.h"
+
+#import <wtf/Assertions.h>
+#import <wtf/RetainPtr.h>
+
+@interface WebCoreControlTintObserver : NSObject
++ (void)controlTintDidChange;
+@end
+
+namespace WebCore {
+
+// NSColor calls don't throw, so no need to block Cocoa exceptions in this file
+
+static RGBA32 oldAquaFocusRingColor = 0xFF7DADD9;
+static RGBA32 systemFocusRingColor;
+static bool useOldAquaFocusRingColor;
+
+
+static RGBA32 makeRGBAFromNSColor(NSColor *c)
+{
+ return makeRGBA((int)(255 * [c redComponent]), (int)(255 * [c greenComponent]), (int)(255 * [c blueComponent]), (int)(255 * [c alphaComponent]));
+}
+
+Color colorFromNSColor(NSColor *c)
+{
+ return Color(makeRGBAFromNSColor(c));
+}
+
+NSColor* nsColor(const Color& color)
+{
+ unsigned c = color.rgb();
+ switch (c) {
+ case 0: {
+ // Need this to avoid returning nil because cachedRGBAValues will default to 0.
+ static RetainPtr<NSColor> clearColor = [NSColor clearColor];
+ return clearColor.get();
+ }
+ case Color::black: {
+ static RetainPtr<NSColor> blackColor = [NSColor blackColor];
+ return blackColor.get();
+ }
+ case Color::white: {
+ static RetainPtr<NSColor> whiteColor = [NSColor whiteColor];
+ return whiteColor.get();
+ }
+ default: {
+ const int cacheSize = 32;
+ static unsigned cachedRGBAValues[cacheSize];
+ static RetainPtr<NSColor> cachedColors[cacheSize];
+
+ for (int i = 0; i != cacheSize; ++i)
+ if (cachedRGBAValues[i] == c)
+ return cachedColors[i].get();
+
+#ifdef COLORMATCH_EVERYTHING
+ NSColor* result = [NSColor colorWithCalibratedRed:color.red() / 255.0f
+ green:color.green() / 255.0f
+ blue:color.blue() / 255.0f
+ alpha:color.alpha() /255.0f];
+#else
+ NSColor* result = [NSColor colorWithDeviceRed:color.red() / 255.0f
+ green:color.green() / 255.0f
+ blue:color.blue() / 255.0f
+ alpha:color.alpha() /255.0f];
+#endif
+
+ static int cursor;
+ cachedRGBAValues[cursor] = c;
+ cachedColors[cursor] = result;
+ if (++cursor == cacheSize)
+ cursor = 0;
+ return result;
+ }
+ }
+}
+
+static CGColorRef CGColorFromNSColor(NSColor* color)
+{
+ // This needs to always use device colorspace so it can de-calibrate the color for
+ // CGColor to possibly recalibrate it.
+ NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ CGFloat red = [deviceColor redComponent];
+ CGFloat green = [deviceColor greenComponent];
+ CGFloat blue = [deviceColor blueComponent];
+ CGFloat alpha = [deviceColor alphaComponent];
+ const CGFloat components[4] = { red, green, blue, alpha };
+ static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB();
+ CGColorRef cgColor = CGColorCreate(deviceRGBColorSpace, components);
+ return cgColor;
+}
+
+CGColorRef cgColor(const Color& c)
+{
+ // We could directly create a CGColor here, but that would
+ // skip any RGB caching the nsColor method does. A direct
+ // creation could be investigated for a possible performance win.
+ return CGColorFromNSColor(nsColor(c));
+}
+
+Color focusRingColor()
+{
+ 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;
+
+ return systemFocusRingColor;
+}
+
+bool usesTestModeFocusRingColor()
+{
+ return useOldAquaFocusRingColor;
+}
+
+void setUsesTestModeFocusRingColor(bool newValue)
+{
+ useOldAquaFocusRingColor = newValue;
+}
+
+}
+
+@implementation WebCoreControlTintObserver
+
++ (void)controlTintDidChange
+{
+#ifdef COLORMATCH_EVERYTHING
+#error Not yet implemented.
+#else
+ NSColor* color = [[NSColor keyboardFocusIndicatorColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ WebCore::systemFocusRingColor = WebCore::makeRGBAFromNSColor(color);
+#endif
+}
+
+@end
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/FloatPointMac.mm b/WebCore/platform/graphics/mac/FloatPointMac.mm
new file mode 100644
index 0000000..2f73314
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FloatPointMac.mm
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. 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 "FloatPoint.h"
+
+namespace WebCore {
+
+#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
+
+FloatPoint::FloatPoint(const NSPoint& p) : m_x(p.x), m_y(p.y)
+{
+}
+
+FloatPoint::operator NSPoint() const
+{
+ return NSMakePoint(m_x, m_y);
+}
+
+#endif
+
+}
diff --git a/WebCore/platform/graphics/mac/FloatRectMac.mm b/WebCore/platform/graphics/mac/FloatRectMac.mm
new file mode 100644
index 0000000..1d6b045
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FloatRectMac.mm
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. 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 "FloatRect.h"
+
+namespace WebCore {
+
+#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
+
+FloatRect::FloatRect(const NSRect& r) : m_location(r.origin), m_size(r.size)
+{
+}
+
+FloatRect::operator NSRect() const
+{
+ return NSMakeRect(x(), y(), width(), height());
+}
+
+#endif
+
+}
diff --git a/WebCore/platform/graphics/mac/FloatSizeMac.mm b/WebCore/platform/graphics/mac/FloatSizeMac.mm
new file mode 100644
index 0000000..01efbe9
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FloatSizeMac.mm
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. 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 "FloatSize.h"
+
+namespace WebCore {
+
+#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
+
+FloatSize::FloatSize(const NSSize& s) : m_width(s.width), m_height(s.height)
+{
+}
+
+FloatSize::operator NSSize() const
+{
+ return NSMakeSize(m_width, m_height);
+}
+
+#endif
+
+}
diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm
new file mode 100644
index 0000000..e7cda66
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontCacheMac.mm
@@ -0,0 +1,202 @@
+/*
+ * 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
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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.
+ */
+
+#import "config.h"
+#import "FontCache.h"
+
+#import "Font.h"
+#import "SimpleFontData.h"
+#import "FontPlatformData.h"
+#import "WebCoreSystemInterface.h"
+#import "WebFontCache.h"
+
+#ifdef BUILDING_ON_TIGER
+typedef int NSInteger;
+#endif
+
+namespace WebCore {
+
+static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*)
+{
+ FontCache::invalidate();
+}
+
+void FontCache::platformInit()
+{
+ wkSetUpFontCache();
+ // FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway.
+ ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0);
+}
+
+static int toAppKitFontWeight(FontWeight fontWeight)
+{
+ static int appKitFontWeights[] = {
+ 2, // FontWeight100
+ 3, // FontWeight200
+ 4, // FontWeight300
+ 5, // FontWeight400
+ 6, // FontWeight500
+ 8, // FontWeight600
+ 9, // FontWeight700
+ 10, // FontWeight800
+ 12, // FontWeight900
+ };
+ return appKitFontWeights[fontWeight];
+}
+
+static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight)
+{
+ return appKitFontWeight >= 7;
+}
+
+const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
+{
+ 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];
+ NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length));
+ [string release];
+
+ if (!substituteFont && length == 1)
+ 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 *fontManager = [NSFontManager sharedFontManager];
+
+ NSFontTraitMask traits;
+ NSInteger weight;
+ CGFloat size;
+
+ if (nsFont) {
+ traits = [fontManager traitsOfFont:nsFont];
+ if (platformData.m_syntheticBold)
+ traits |= NSBoldFontMask;
+ if (platformData.m_syntheticOblique)
+ traits |= NSFontItalicTrait;
+ weight = [fontManager weightOfFont:nsFont];
+ size = [nsFont pointSize];
+ } else {
+ // For custom fonts nsFont is nil.
+ traits = font.italic() ? NSFontItalicTrait : 0;
+ weight = toAppKitFontWeight(font.weight());
+ size = font.pixelSize();
+ }
+
+ if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size])
+ substituteFont = bestVariation;
+
+ substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont];
+
+ NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont];
+ NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont];
+
+ FontPlatformData alternateFont(substituteFont,
+ !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight),
+ !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait));
+ return getCachedFontData(&alternateFont);
+}
+
+FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font)
+{
+ // Attempt to find an appropriate font using a match based on
+ // the presence of keywords in the the requested names. For example, we'll
+ // match any name that contains "Arabic" to Geeza Pro.
+ FontPlatformData* platformData = 0;
+ const FontFamily* currFamily = &font.fontDescription().family();
+ while (currFamily && !platformData) {
+ if (currFamily->family().length()) {
+ static String matchWords[3] = { String("Arabic"), String("Pashto"), String("Urdu") };
+ static AtomicString geezaStr("Geeza Pro");
+ for (int j = 0; j < 3 && !platformData; ++j)
+ if (currFamily->family().contains(matchWords[j], false))
+ platformData = getCachedFontPlatformData(font.fontDescription(), geezaStr);
+ }
+ currFamily = currFamily->next();
+ }
+
+ return platformData;
+}
+
+FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription)
+{
+ static AtomicString timesStr("Times");
+ static AtomicString lucidaGrandeStr("Lucida Grande");
+
+ // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick
+ // the default that the user would get without changing any prefs.
+ FontPlatformData* platformFont = getCachedFontPlatformData(fontDescription, timesStr);
+ if (!platformFont)
+ // The Times fallback will almost always work, but in the highly unusual case where
+ // the user doesn't have it, we fall back on Lucida Grande because that's
+ // guaranteed to be there, according to Nathan Taylor. This is good enough
+ // to avoid a crash at least.
+ platformFont = getCachedFontPlatformData(fontDescription, lucidaGrandeStr);
+
+ return platformFont;
+}
+
+void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
+{
+ [WebFontCache getTraits:traitsMasks inFamily:familyName];
+}
+
+FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
+{
+ NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0;
+ NSInteger weight = toAppKitFontWeight(fontDescription.weight());
+ float size = fontDescription.computedPixelSize();
+
+ NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size];
+ if (!nsFont)
+ return 0;
+
+ NSFontManager *fontManager = [NSFontManager sharedFontManager];
+ NSFontTraitMask actualTraits = 0;
+ 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 = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight);
+ result->m_syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait);
+ return result;
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp
new file mode 100644
index 0000000..1fb144c
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 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.
+ *
+ */
+
+#include "config.h"
+#include "FontCustomPlatformData.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+#include "SharedBuffer.h"
+#include "FontPlatformData.h"
+
+namespace WebCore {
+
+FontCustomPlatformData::~FontCustomPlatformData()
+{
+ ATSFontDeactivate(m_atsContainer, NULL, kATSOptionFlagsDefault);
+ CGFontRelease(m_cgFont);
+}
+
+FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode)
+{
+ return FontPlatformData(m_cgFont, (ATSUFontID)m_atsFont, size, bold, italic);
+}
+
+FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer)
+{
+ ASSERT_ARG(buffer, buffer);
+
+ // Use ATS to activate the font.
+ ATSFontContainerRef containerRef = 0;
+
+ // The value "3" means that the font is private and can't be seen by anyone else.
+ ATSFontActivateFromMemory((void*)buffer->data(), buffer->size(), 3, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &containerRef);
+ if (!containerRef)
+ return 0;
+ ItemCount fontCount;
+ ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 0, NULL, &fontCount);
+
+ // We just support the first font in the list.
+ if (fontCount == 0) {
+ ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
+ return 0;
+ }
+
+ ATSFontRef fontRef = 0;
+ ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, &fontRef, NULL);
+ if (!fontRef) {
+ ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
+ return 0;
+ }
+
+ CGFontRef cgFontRef = CGFontCreateWithPlatformFont(&fontRef);
+#ifndef BUILDING_ON_TIGER
+ // Workaround for <rdar://problem/5675504>.
+ if (!CGFontGetNumberOfGlyphs(cgFontRef)) {
+ CFRelease(cgFontRef);
+ cgFontRef = 0;
+ }
+#endif
+ if (!cgFontRef) {
+ ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
+ return 0;
+ }
+
+ return new FontCustomPlatformData(containerRef, fontRef, cgFontRef);
+}
+
+}
diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/WebCore/platform/graphics/mac/FontCustomPlatformData.h
new file mode 100644
index 0000000..1e73ae0
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 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.
+ *
+ */
+
+#ifndef FontCustomPlatformData_h
+#define FontCustomPlatformData_h
+
+#include "FontRenderingMode.h"
+#include <wtf/Noncopyable.h>
+
+typedef struct CGFont* CGFontRef;
+typedef UInt32 ATSFontContainerRef;
+typedef UInt32 ATSFontRef;
+
+namespace WebCore {
+
+class FontPlatformData;
+class SharedBuffer;
+
+struct FontCustomPlatformData : Noncopyable {
+ FontCustomPlatformData(ATSFontContainerRef container, ATSFontRef atsFont, CGFontRef cgFont)
+ : m_atsContainer(container), m_atsFont(atsFont), m_cgFont(cgFont)
+ {}
+ ~FontCustomPlatformData();
+
+ FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode);
+
+ ATSFontContainerRef m_atsContainer;
+ ATSFontRef m_atsFont;
+ CGFontRef m_cgFont;
+};
+
+FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer);
+
+}
+
+#endif
diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm
new file mode 100644
index 0000000..bef18d0
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontMac.mm
@@ -0,0 +1,122 @@
+/*
+ * 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"
+
+#import "GlyphBuffer.h"
+#import "GraphicsContext.h"
+#import "Logging.h"
+#import "SimpleFontData.h"
+#import "WebCoreSystemInterface.h"
+#import "WebCoreTextRenderer.h"
+
+#define SYNTHETIC_OBLIQUE_ANGLE 14
+
+#ifdef __LP64__
+#define URefCon void*
+#else
+#define URefCon UInt32
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
+{
+ CGContextRef cgContext = context->platformContext();
+
+ bool originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
+ bool newShouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
+
+ if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing)
+ CGContextSetShouldSmoothFonts(cgContext, newShouldUseFontSmoothing);
+
+ const FontPlatformData& platformData = font->platformData();
+ NSFont* drawFont;
+ if (!isPrinterFont()) {
+ drawFont = [platformData.font() screenFont];
+ if (drawFont != platformData.font())
+ // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually).
+ LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen. Using screen font anyway, may result in incorrect metrics.",
+ [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
+ } else {
+ drawFont = [platformData.font() printerFont];
+ if (drawFont != platformData.font())
+ NSLog(@"Attempting to set non-printer font (%@) when printing. Using printer font anyway, may result in incorrect metrics.",
+ [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
+ }
+
+ CGContextSetFont(cgContext, platformData.cgFont());
+
+ CGAffineTransform matrix = CGAffineTransformIdentity;
+ if (drawFont)
+ memcpy(&matrix, [drawFont matrix], sizeof(matrix));
+ matrix.b = -matrix.b;
+ matrix.d = -matrix.d;
+ if (platformData.m_syntheticOblique)
+ matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0));
+ CGContextSetTextMatrix(cgContext, matrix);
+
+ if (drawFont) {
+ wkSetCGFontRenderingMode(cgContext, drawFont);
+ 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) {
+ CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y());
+ 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
new file mode 100644
index 0000000..40a2dbd
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontPlatformData.h
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the internal font implementation.
+ * It should not be included by source files outside it.
+ *
+ * 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
+ * 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.
+ *
+ */
+
+#ifndef FontPlatformData_h
+#define FontPlatformData_h
+
+#include "StringImpl.h"
+
+#ifdef __OBJC__
+@class NSFont;
+#else
+class NSFont;
+#endif
+
+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 {
+
+#ifndef BUILDING_ON_TIGER
+inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); }
+#endif
+
+struct FontPlatformData {
+ FontPlatformData(float size, bool syntheticBold, bool syntheticOblique)
+ : m_syntheticBold(syntheticBold)
+ , m_syntheticOblique(syntheticOblique)
+ , m_atsuFontID(0)
+ , m_size(size)
+ , m_font(0)
+#ifdef BUILDING_ON_TIGER
+ , m_cgFont(0)
+#endif
+ {
+ }
+
+ 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_atsuFontID(fontID), m_size(s), m_font(0), m_cgFont(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;
+
+ ATSUFontID m_atsuFontID;
+ float m_size;
+
+ unsigned hash() const
+ {
+ ASSERT(m_font != 0 || m_cgFont == 0);
+ uintptr_t hashCodes[2] = { (uintptr_t)m_font, m_syntheticBold << 1 | m_syntheticOblique };
+ 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 &&
+ m_cgFont == other.m_cgFont && m_size == other.m_size && m_atsuFontID == other.m_atsuFontID;
+ }
+
+ NSFont *font() const { return m_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
+};
+
+}
+
+#endif
diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm
new file mode 100644
index 0000000..15e573d
--- /dev/null
+++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the internal font implementation.
+ *
+ * Copyright (C) 2006-7 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 "FontPlatformData.h"
+
+#import "WebCoreSystemInterface.h"
+
+namespace WebCore {
+
+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 != 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;
+ m_cgFont = f.m_cgFont;
+ m_atsuFontID = f.m_atsuFontID;
+}
+
+FontPlatformData:: ~FontPlatformData()
+{
+ if (m_font && m_font != reinterpret_cast<NSFont *>(-1))
+ CFRelease(m_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 != 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
new file mode 100644
index 0000000..143e665
--- /dev/null
+++ b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2006, 2007 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 "GlyphPageTreeNode.h"
+
+#include "SimpleFontData.h"
+#include "WebCoreSystemInterface.h"
+#include <ApplicationServices/ApplicationServices.h>
+
+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)];
+
+ OSStatus status = wkInitializeGlyphVector(GlyphPage::size, &glyphVector);
+ if (status != noErr)
+ // This should never happen, perhaps indicates a bad font! If it does the
+ // font substitution code will find an alternate font.
+ return false;
+
+ wkConvertCharToGlyphs(fontData->m_styleGroup, buffer, bufferLength, &glyphVector);
+
+ unsigned numGlyphs = wkGetGlyphVectorNumGlyphs(&glyphVector);
+ if (numGlyphs != length) {
+ // This should never happen, perhaps indicates a bad font?
+ // If it does happen, the font substitution code will find an alternate font.
+ wkClearGlyphVector(&glyphVector);
+ return false;
+ }
+
+ ATSLayoutRecord* glyphRecord = (ATSLayoutRecord*)wkGetGlyphVectorFirstRecord(glyphVector);
+ for (unsigned i = 0; i < length; i++) {
+ Glyph glyph = glyphRecord->glyphID;
+ if (!glyph)
+ setGlyphDataForIndex(offset + i, 0, 0);
+ else {
+ setGlyphDataForIndex(offset + i, glyph, fontData);
+ haveGlyphs = true;
+ }
+ glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector));
+ }
+ wkClearGlyphVector(&glyphVector);
+#endif
+
+ return haveGlyphs;
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/WebCore/platform/graphics/mac/GraphicsContextMac.mm
new file mode 100644
index 0000000..3f9176c
--- /dev/null
+++ b/WebCore/platform/graphics/mac/GraphicsContextMac.mm
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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.
+ */
+
+#import "config.h"
+#import "GraphicsContext.h"
+
+#import "../cg/GraphicsContextPlatformPrivateCG.h"
+
+#import "WebCoreSystemInterface.h"
+
+// FIXME: More of this should use CoreGraphics instead of AppKit.
+// FIXME: More of this should move into GraphicsContextCG.cpp.
+
+namespace WebCore {
+
+// NSColor, NSBezierPath, and NSGraphicsContext
+// calls in this file are all exception-safe, so we don't block
+// exceptions for those.
+
+void GraphicsContext::drawFocusRing(const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ int radius = (focusRingWidth() - 1) / 2;
+ int offset = radius + focusRingOffset();
+ CGColorRef colorRef = color.isValid() ? cgColor(color) : 0;
+
+ CGMutablePathRef focusRingPath = CGPathCreateMutable();
+ const Vector<IntRect>& rects = focusRingRects();
+ unsigned rectCount = rects.size();
+ for (unsigned i = 0; i < rectCount; i++)
+ CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset));
+
+ CGContextRef context = platformContext();
+#ifdef BUILDING_ON_TIGER
+ CGContextBeginTransparencyLayer(context, NULL);
+#endif
+ CGContextBeginPath(context);
+ CGContextAddPath(context, focusRingPath);
+ wkDrawFocusRing(context, colorRef, radius);
+#ifdef BUILDING_ON_TIGER
+ CGContextEndTransparencyLayer(context);
+#endif
+ CGColorRelease(colorRef);
+
+ CGPathRelease(focusRingPath);
+}
+
+#ifdef BUILDING_ON_TIGER // Post-Tiger's setCompositeOperation() is defined in GraphicsContextCG.cpp.
+void GraphicsContext::setCompositeOperation(CompositeOperator op)
+{
+ if (paintingDisabled())
+ return;
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ [[NSGraphicsContext graphicsContextWithGraphicsPort:platformContext() flipped:YES]
+ setCompositingOperation:(NSCompositingOperation)op];
+ [pool drain];
+}
+#endif
+
+void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, int width, bool grammar)
+{
+ if (paintingDisabled())
+ return;
+
+ // Constants for spelling pattern color
+ static RetainPtr<NSColor> spellingPatternColor = nil;
+ static bool usingDotForSpelling = false;
+
+ // Constants for grammar pattern color
+ static RetainPtr<NSColor> grammarPatternColor = nil;
+ static bool usingDotForGrammar = false;
+
+ // These are the same for misspelling or bad grammar
+ int patternHeight = cMisspellingLineThickness;
+ int patternWidth = cMisspellingLinePatternWidth;
+
+ // Initialize pattern color if needed
+ if (!grammar && !spellingPatternColor) {
+ NSImage *image = [NSImage imageNamed:@"SpellingDot"];
+ ASSERT(image); // if image is not available, we want to know
+ NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
+ if (color)
+ usingDotForSpelling = true;
+ else
+ color = [NSColor redColor];
+ spellingPatternColor = color;
+ }
+
+ if (grammar && !grammarPatternColor) {
+ NSImage *image = [NSImage imageNamed:@"GrammarDot"];
+ ASSERT(image); // if image is not available, we want to know
+ NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
+ if (color)
+ usingDotForGrammar = true;
+ else
+ color = [NSColor greenColor];
+ grammarPatternColor = color;
+ }
+
+ bool usingDot;
+ NSColor *patternColor;
+ if (grammar) {
+ usingDot = usingDotForGrammar;
+ patternColor = grammarPatternColor.get();
+ } else {
+ usingDot = usingDotForSpelling;
+ patternColor = spellingPatternColor.get();
+ }
+
+ // Make sure to draw only complete dots.
+ // NOTE: Code here used to shift the underline to the left and increase the width
+ // to make sure everything gets underlined, but that results in drawing out of
+ // bounds (e.g. when at the edge of a view) and could make it appear that the
+ // space between adjacent misspelled words was underlined.
+ if (usingDot) {
+ // allow slightly more considering that the pattern ends with a transparent pixel
+ int widthMod = width % patternWidth;
+ if (patternWidth - widthMod > cMisspellingLinePatternGapWidth)
+ width -= widthMod;
+ }
+
+ // FIXME: This code should not use NSGraphicsContext currentContext
+ // In order to remove this requirement we will need to use CGPattern instead of NSColor
+ // FIXME: This code should not be using wkSetPatternPhaseInUserSpace, as this approach is wrong
+ // for transforms.
+
+ // Draw underline
+ NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+ CGContextRef context = (CGContextRef)[currentContext graphicsPort];
+ CGContextSaveGState(context);
+
+ [patternColor set];
+
+ wkSetPatternPhaseInUserSpace(context, point);
+
+ NSRectFillUsingOperation(NSMakeRect(point.x(), point.y(), width, patternHeight), NSCompositeSourceOver);
+
+ CGContextRestoreGState(context);
+}
+
+}
diff --git a/WebCore/platform/graphics/mac/IconMac.mm b/WebCore/platform/graphics/mac/IconMac.mm
new file mode 100644
index 0000000..63abe59
--- /dev/null
+++ b/WebCore/platform/graphics/mac/IconMac.mm
@@ -0,0 +1,84 @@
+/*
+ * 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
+ * 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 "Icon.h"
+
+#import "GraphicsContext.h"
+#import "LocalCurrentGraphicsContext.h"
+#import "PlatformString.h"
+#import <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+Icon::Icon(NSImage *image)
+ : m_nsImage(image)
+{
+ // Need this because WebCore uses AppKit's flipped coordinate system exclusively.
+ [image setFlipped:YES];
+}
+
+Icon::~Icon()
+{
+}
+
+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)[]
+ if (filename.isEmpty() || filename[0U] != '/')
+ return 0;
+
+ NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile:filename];
+ if (!image)
+ return 0;
+
+ 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)
+{
+ if (context->paintingDisabled())
+ return;
+
+ LocalCurrentGraphicsContext localCurrentGC(context);
+
+ [m_nsImage.get() drawInRect:rect
+ fromRect:NSMakeRect(0, 0, [m_nsImage.get() size].width, [m_nsImage.get() size].height)
+ operation:NSCompositeSourceOver fraction:1.0f];
+}
+
+}
diff --git a/WebCore/platform/graphics/mac/ImageMac.mm b/WebCore/platform/graphics/mac/ImageMac.mm
new file mode 100644
index 0000000..a0d257b
--- /dev/null
+++ b/WebCore/platform/graphics/mac/ImageMac.mm
@@ -0,0 +1,125 @@
+/*
+ * 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
+ * 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.
+ */
+
+#import "config.h"
+#import "BitmapImage.h"
+
+#import "FloatRect.h"
+#import "FoundationExtras.h"
+#import "GraphicsContext.h"
+#import "PlatformString.h"
+
+@interface WebCoreBundleFinder : NSObject
+@end
+
+@implementation WebCoreBundleFinder
+@end
+
+namespace WebCore {
+
+void BitmapImage::initPlatformData()
+{
+}
+
+void BitmapImage::invalidatePlatformData()
+{
+ if (m_frames.size() != 1)
+ return;
+
+ m_nsImage = 0;
+ m_tiffRep = 0;
+}
+
+PassRefPtr<Image> Image::loadPlatformResource(const char *name)
+{
+ NSBundle *bundle = [NSBundle bundleForClass:[WebCoreBundleFinder class]];
+ NSString *imagePath = [bundle pathForResource:[NSString stringWithUTF8String:name] ofType:@"tiff"];
+ NSData *namedImageData = [NSData dataWithContentsOfFile:imagePath];
+ if (namedImageData) {
+ RefPtr<Image> image = BitmapImage::create();
+ image->setData(SharedBuffer::wrapNSData(namedImageData), true);
+ 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 Image::nullImage();
+}
+
+CFDataRef BitmapImage::getTIFFRepresentation()
+{
+ if (m_tiffRep)
+ return m_tiffRep.get();
+
+ unsigned numFrames = frameCount();
+
+ // If numFrames is zero, we know for certain this image doesn't have valid data
+ // Even though the call to CGImageDestinationCreateWithData will fail and we'll handle it gracefully,
+ // in certain circumstances that call will spam the console with an error message
+ if (!numFrames)
+ return 0;
+
+ Vector<CGImageRef> images;
+ for (unsigned i = 0; i < numFrames; ++i ) {
+ CGImageRef cgImage = frameAtIndex(i);
+ if (cgImage)
+ images.append(cgImage);
+ }
+
+ unsigned numValidFrames = images.size();
+
+ RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(0, 0));
+ // FIXME: Use type kCGImageTypeIdentifierTIFF constant once is becomes available in the API
+ CGImageDestinationRef destination = CGImageDestinationCreateWithData(data.get(), CFSTR("public.tiff"), numValidFrames, 0);
+
+ if (!destination)
+ return 0;
+
+ for (unsigned i = 0; i < numValidFrames; ++i)
+ CGImageDestinationAddImage(destination, images[i], 0);
+
+ CGImageDestinationFinalize(destination);
+ CFRelease(destination);
+
+ m_tiffRep = data;
+ return m_tiffRep.get();
+}
+
+NSImage* BitmapImage::getNSImage()
+{
+ if (m_nsImage)
+ return m_nsImage.get();
+
+ CFDataRef data = getTIFFRepresentation();
+ if (!data)
+ return 0;
+
+ m_nsImage.adoptNS([[NSImage alloc] initWithData:(NSData*)data]);
+ return m_nsImage.get();
+}
+
+}
diff --git a/WebCore/platform/graphics/mac/IntPointMac.mm b/WebCore/platform/graphics/mac/IntPointMac.mm
new file mode 100644
index 0000000..7a2e730
--- /dev/null
+++ b/WebCore/platform/graphics/mac/IntPointMac.mm
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 "IntPoint.h"
+
+namespace WebCore {
+
+#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
+
+IntPoint::IntPoint(const NSPoint& p) : m_x(static_cast<int>(p.x)), m_y(static_cast<int>(p.y))
+{
+}
+
+IntPoint::operator NSPoint() const
+{
+ return NSMakePoint(m_x, m_y);
+}
+
+#endif
+
+}
diff --git a/WebCore/platform/graphics/mac/IntRectMac.mm b/WebCore/platform/graphics/mac/IntRectMac.mm
new file mode 100644
index 0000000..738618a
--- /dev/null
+++ b/WebCore/platform/graphics/mac/IntRectMac.mm
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, 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 "IntRect.h"
+
+namespace WebCore {
+
+#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
+
+IntRect::operator NSRect() const
+{
+ return NSMakeRect(x(), y(), width(), height());
+}
+
+IntRect enclosingIntRect(const NSRect& rect)
+{
+ int l = static_cast<int>(floorf(rect.origin.x));
+ int t = static_cast<int>(floorf(rect.origin.y));
+ int r = static_cast<int>(ceilf(NSMaxX(rect)));
+ int b = static_cast<int>(ceilf(NSMaxY(rect)));
+ return IntRect(l, t, r - l, b - t);
+}
+
+#endif
+
+}
diff --git a/WebCore/platform/graphics/mac/IntSizeMac.mm b/WebCore/platform/graphics/mac/IntSizeMac.mm
new file mode 100644
index 0000000..c7dcd88
--- /dev/null
+++ b/WebCore/platform/graphics/mac/IntSizeMac.mm
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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 "IntSize.h"
+
+namespace WebCore {
+
+#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
+
+IntSize::IntSize(const NSSize& s) : m_width(static_cast<int>(s.width)), m_height(static_cast<int>(s.height))
+{
+}
+
+IntSize::operator NSSize() const
+{
+ return NSMakeSize(m_width, m_height);
+}
+
+#endif
+
+}
diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h
new file mode 100644
index 0000000..3f18ab4
--- /dev/null
+++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#ifndef MediaPlayerPrivateQTKit_h
+#define MediaPlayerPrivateQTKit_h
+
+#if ENABLE(VIDEO)
+
+#include "MediaPlayer.h"
+#include "Timer.h"
+#include <wtf/RetainPtr.h>
+
+#ifdef __OBJC__
+#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 {
+public:
+ MediaPlayerPrivate(MediaPlayer*);
+ ~MediaPlayerPrivate();
+
+ IntSize naturalSize() const;
+ bool hasVideo() const;
+
+ void load(const String& url);
+ void cancelLoad();
+
+ void play();
+ void pause();
+
+ bool paused() const;
+ bool seeking() const;
+
+ float duration() const;
+ float currentTime() const;
+ void seek(float time);
+ void setEndTime(float time);
+
+ void setRate(float);
+ void setVolume(float);
+
+ int dataRate() const;
+
+ MediaPlayer::NetworkState networkState() const { return m_networkState; }
+ MediaPlayer::ReadyState readyState() const { return m_readyState; }
+
+ float maxTimeBuffered() const;
+ float maxTimeSeekable() const;
+ unsigned bytesLoaded() const;
+ bool totalBytesKnown() const;
+ unsigned totalBytes() const;
+
+ void setVisible(bool);
+ void setRect(const IntRect& r);
+
+ void loadStateChanged();
+ void rateChanged();
+ void sizeChanged();
+ void timeChanged();
+ void didEnd();
+
+ void repaint();
+ void paint(GraphicsContext*, const IntRect&);
+
+ static void getSupportedTypes(HashSet<String>& types);
+ static bool isAvailable();
+
+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();
+ void doSeek();
+ void cancelSeek();
+ void seekTimerFired(Timer<MediaPlayerPrivate>*);
+ void endPointTimerFired(Timer<MediaPlayerPrivate>*);
+ float maxTimeLoaded() const;
+ void startEndPointTimerIfNeeded();
+ void disableUnsupportedTracks(unsigned& enabledTrackCount);
+
+ 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;
+ Timer<MediaPlayerPrivate> m_seekTimer;
+ Timer<MediaPlayerPrivate> m_endPointTimer;
+ MediaPlayer::NetworkState m_networkState;
+ 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
+};
+
+}
+
+#endif
+#endif
diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm
new file mode 100644
index 0000000..0ec56d6
--- /dev/null
+++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm
@@ -0,0 +1,1024 @@
+/*
+ * 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 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.
+ */
+
+#import "config.h"
+
+#if ENABLE(VIDEO)
+
+#import "MediaPlayerPrivateQTKit.h"
+
+#import "BlockExceptions.h"
+#import "GraphicsContext.h"
+#import "KURL.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)
+{
+ IMP result = m->method_imp;
+ m->method_imp = imp;
+ return result;
+}
+#endif
+
+SOFT_LINK_FRAMEWORK(QTKit)
+
+SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale))
+
+SOFT_LINK_CLASS(QTKit, QTMovie)
+SOFT_LINK_CLASS(QTKit, QTMovieView)
+
+SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *)
+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 *)
+SOFT_LINK_POINTER(QTKit, QTMovieIsActiveAttribute, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *)
+SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *)
+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()
+
+#define QTMediaTypeAttribute getQTMediaTypeAttribute()
+#define QTMediaTypeBase getQTMediaTypeBase()
+#define QTMediaTypeSound getQTMediaTypeSound()
+#define QTMediaTypeText getQTMediaTypeText()
+#define QTMediaTypeVideo getQTMediaTypeVideo()
+#define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute()
+#define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute()
+#define QTMovieDidEndNotification getQTMovieDidEndNotification()
+#define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute()
+#define QTMovieIsActiveAttribute getQTMovieIsActiveAttribute()
+#define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute()
+#define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification()
+#define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute()
+#define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute()
+#define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification()
+#define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification()
+#define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification()
+#define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute()
+#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
+enum {
+ QTMovieLoadStateError = -1L,
+ QTMovieLoadStateLoaded = 2000L,
+ QTMovieLoadStatePlayable = 10000L,
+ QTMovieLoadStatePlaythroughOK = 20000L,
+ QTMovieLoadStateComplete = 100000L
+};
+#endif
+
+using namespace WebCore;
+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;
+-(void)rateChanged:(NSNotification *)notification;
+-(void)sizeChanged:(NSNotification *)notification;
+-(void)timeChanged:(NSNotification *)notification;
+-(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])
+ , m_seekTo(-1)
+ , m_endTime(numeric_limits<float>::infinity())
+ , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired)
+ , m_endPointTimer(this, &MediaPlayerPrivate::endPointTimerFired)
+ , m_networkState(MediaPlayer::Empty)
+ , 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()
+{
+ tearDownVideoRendering();
+
+ [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
+ [m_objcObserver.get() disconnect];
+}
+
+void MediaPlayerPrivate::createQTMovie(const String& url)
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
+
+ if (m_qtMovie) {
+ destroyQTVideoRenderer();
+ m_qtMovie = 0;
+ }
+
+ // Disable streaming support for now, <rdar://problem/5693967>
+ if (protocolIs(url, "rtsp"))
+ return;
+
+ 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 = protocolIs(url, "rtsp");
+
+ if (!m_qtMovie)
+ return;
+
+ [m_qtMovie.get() setVolume:m_player->volume()];
+
+ [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
+ selector:@selector(loadStateChanged:)
+ name:QTMovieLoadStateDidChangeNotification
+ object:m_qtMovie.get()];
+ [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
+ selector:@selector(rateChanged:)
+ name:QTMovieRateDidChangeNotification
+ object:m_qtMovie.get()];
+ [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
+ selector:@selector(sizeChanged:)
+ name:QTMovieSizeDidChangeNotification
+ object:m_qtMovie.get()];
+ [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
+ selector:@selector(timeChanged:)
+ name:QTMovieTimeDidChangeNotification
+ object:m_qtMovie.get()];
+ [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
+ selector:@selector(didEnd:)
+ name:QTMovieDidEndNotification
+ object:m_qtMovie.get()];
+}
+
+static void mainThreadSetNeedsDisplay(id self, SEL _cmd)
+{
+ id movieView = [self superview];
+ ASSERT(!movieView || [movieView isKindOfClass:[QTMovieView class]]);
+ if (!movieView || ![movieView isKindOfClass:[QTMovieView class]])
+ return;
+
+ WebCoreMovieObserver* delegate = [movieView delegate];
+ ASSERT(!delegate || [delegate isKindOfClass:[WebCoreMovieObserver class]]);
+ if (!delegate || ![delegate isKindOfClass:[WebCoreMovieObserver class]])
+ return;
+
+ [delegate repaint];
+}
+
+static Class QTVideoRendererClass()
+{
+ static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly");
+ return QTVideoRendererWebKitOnlyClass;
+}
+
+void MediaPlayerPrivate::createQTMovieView()
+{
+ detachQTMovieView();
+
+ static bool addedCustomMethods = false;
+ if (!addedCustomMethods) {
+ Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView");
+ ASSERT(QTMovieContentViewClass);
+
+ Method mainThreadSetNeedsDisplayMethod = class_getInstanceMethod(QTMovieContentViewClass, @selector(_mainThreadSetNeedsDisplay));
+ ASSERT(mainThreadSetNeedsDisplayMethod);
+
+ method_setImplementation(mainThreadSetNeedsDisplayMethod, reinterpret_cast<IMP>(mainThreadSetNeedsDisplay));
+ addedCustomMethods = true;
+ }
+
+ m_qtMovieView.adoptNS([[QTMovieView alloc] init]);
+ setRect(m_player->rect());
+ 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
+ [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:m_objcObserver.get()];
+#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]];
+
+ // 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];
+#else
+ [m_qtMovieView.get() setDelegate:nil];
+#endif
+ [m_qtMovieView.get() removeFromSuperview];
+ m_qtMovieView = nil;
+ }
+}
+
+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)
+ return QTMakeTime(0, 600);
+ long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue];
+ return QTMakeTime(time * timeScale, timeScale);
+}
+
+void MediaPlayerPrivate::load(const String& url)
+{
+ if (m_networkState != MediaPlayer::Loading) {
+ m_networkState = MediaPlayer::Loading;
+ m_player->networkStateChanged();
+ }
+ if (m_readyState != MediaPlayer::DataUnavailable) {
+ m_readyState = MediaPlayer::DataUnavailable;
+ m_player->readyStateChanged();
+ }
+ cancelSeek();
+ m_endPointTimer.stop();
+
+ [m_objcObserver.get() setDelayCallbacks:YES];
+
+ createQTMovie(url);
+
+ [m_objcObserver.get() loadStateChanged:nil];
+ [m_objcObserver.get() setDelayCallbacks:NO];
+}
+
+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];
+ startEndPointTimerIfNeeded();
+}
+
+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];
+ m_endPointTimer.stop();
+}
+
+float MediaPlayerPrivate::duration() const
+{
+ if (!m_qtMovie)
+ return 0;
+ QTTime time = [m_qtMovie.get() duration];
+ if (time.flags == kQTTimeIsIndefinite)
+ return numeric_limits<float>::infinity();
+ return static_cast<float>(time.timeValue) / time.timeScale;
+}
+
+float MediaPlayerPrivate::currentTime() const
+{
+ if (!m_qtMovie)
+ return 0;
+ QTTime time = [m_qtMovie.get() currentTime];
+ return min(static_cast<float>(time.timeValue) / time.timeScale, m_endTime);
+}
+
+void MediaPlayerPrivate::seek(float time)
+{
+ cancelSeek();
+
+ if (!m_qtMovie)
+ return;
+
+ if (time > duration())
+ time = duration();
+
+ m_seekTo = time;
+ if (maxTimeLoaded() >= m_seekTo)
+ doSeek();
+ else
+ m_seekTimer.start(0, 0.5f);
+}
+
+void MediaPlayerPrivate::doSeek()
+{
+ QTTime qttime = createQTTime(m_seekTo);
+ // setCurrentTime generates several event callbacks, update afterwards
+ [m_objcObserver.get() setDelayCallbacks:YES];
+ float oldRate = [m_qtMovie.get() rate];
+ [m_qtMovie.get() setRate:0];
+ [m_qtMovie.get() setCurrentTime:qttime];
+ float timeAfterSeek = currentTime();
+ // restore playback only if not at end, othewise QTMovie will loop
+ if (timeAfterSeek < duration() && timeAfterSeek < m_endTime)
+ [m_qtMovie.get() setRate:oldRate];
+ cancelSeek();
+ [m_objcObserver.get() setDelayCallbacks:NO];
+}
+
+void MediaPlayerPrivate::cancelSeek()
+{
+ m_seekTo = -1;
+ m_seekTimer.stop();
+}
+
+void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*)
+{
+ if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) {
+ cancelSeek();
+ updateStates();
+ m_player->timeChanged();
+ return;
+ }
+
+ if (maxTimeLoaded() >= m_seekTo)
+ doSeek();
+ else {
+ MediaPlayer::NetworkState state = networkState();
+ if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
+ cancelSeek();
+ updateStates();
+ m_player->timeChanged();
+ }
+ }
+}
+
+void MediaPlayerPrivate::setEndTime(float time)
+{
+ m_endTime = time;
+ startEndPointTimerIfNeeded();
+}
+
+void MediaPlayerPrivate::startEndPointTimerIfNeeded()
+{
+ if (m_endTime < duration() && m_startedPlaying && !m_endPointTimer.isActive())
+ m_endPointTimer.startRepeating(endPointTimerInterval);
+}
+
+void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*)
+{
+ float time = currentTime();
+
+ // just do end for now
+ if (time >= m_endTime) {
+ pause();
+ didEnd();
+ }
+}
+
+bool MediaPlayerPrivate::paused() const
+{
+ if (!m_qtMovie)
+ return true;
+ return [m_qtMovie.get() rate] == 0;
+}
+
+bool MediaPlayerPrivate::seeking() const
+{
+ if (!m_qtMovie)
+ return false;
+ return m_seekTo >= 0;
+}
+
+IntSize MediaPlayerPrivate::naturalSize() const
+{
+ if (!m_qtMovie)
+ return IntSize();
+ return IntSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]);
+}
+
+bool MediaPlayerPrivate::hasVideo() const
+{
+ if (!m_qtMovie)
+ return false;
+ return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue];
+}
+
+void MediaPlayerPrivate::setVolume(float volume)
+{
+ if (!m_qtMovie)
+ return;
+ [m_qtMovie.get() setVolume:volume];
+}
+
+void MediaPlayerPrivate::setRate(float rate)
+{
+ if (!m_qtMovie)
+ return;
+ if (!paused())
+ [m_qtMovie.get() setRate:rate];
+}
+
+int MediaPlayerPrivate::dataRate() const
+{
+ if (!m_qtMovie)
+ return 0;
+ return wkQTMovieDataRate(m_qtMovie.get());
+}
+
+
+float MediaPlayerPrivate::maxTimeBuffered() const
+{
+ // rtsp streams are not buffered
+ return m_isStreaming ? 0 : maxTimeLoaded();
+}
+
+float MediaPlayerPrivate::maxTimeSeekable() const
+{
+ // infinite duration means live stream
+ return isinf(duration()) ? 0 : maxTimeLoaded();
+}
+
+float MediaPlayerPrivate::maxTimeLoaded() const
+{
+ if (!m_qtMovie)
+ return 0;
+ return wkQTMovieMaxTimeLoaded(m_qtMovie.get());
+}
+
+unsigned MediaPlayerPrivate::bytesLoaded() const
+{
+ float dur = duration();
+ if (!dur)
+ return 0;
+ return totalBytes() * maxTimeLoaded() / dur;
+}
+
+bool MediaPlayerPrivate::totalBytesKnown() const
+{
+ return totalBytes() > 0;
+}
+
+unsigned MediaPlayerPrivate::totalBytes() const
+{
+ if (!m_qtMovie)
+ return 0;
+ return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue];
+}
+
+void MediaPlayerPrivate::cancelLoad()
+{
+ // FIXME: Is there a better way to check for this?
+ if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
+ return;
+
+ tearDownVideoRendering();
+ m_qtMovie = nil;
+
+ updateStates();
+}
+
+void MediaPlayerPrivate::updateStates()
+{
+ MediaPlayer::NetworkState oldNetworkState = m_networkState;
+ MediaPlayer::ReadyState oldReadyState = m_readyState;
+
+ long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError);
+
+ 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)
+ m_networkState = MediaPlayer::Loaded;
+ m_readyState = MediaPlayer::CanPlayThrough;
+ } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
+ if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking())
+ m_networkState = MediaPlayer::LoadedFirstFrame;
+ m_readyState = MediaPlayer::CanPlayThrough;
+ } else if (loadState >= QTMovieLoadStatePlayable) {
+ if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking())
+ m_networkState = MediaPlayer::LoadedFirstFrame;
+ // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
+ m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::CanPlay : MediaPlayer::DataUnavailable;
+ } else if (loadState >= QTMovieLoadStateLoaded) {
+ if (m_networkState < MediaPlayer::LoadedMetaData)
+ m_networkState = MediaPlayer::LoadedMetaData;
+ m_readyState = MediaPlayer::DataUnavailable;
+ } else if (loadState > QTMovieLoadStateError) {
+ if (m_networkState < MediaPlayer::Loading)
+ m_networkState = MediaPlayer::Loading;
+ m_readyState = MediaPlayer::DataUnavailable;
+ } else {
+ m_networkState = MediaPlayer::LoadFailed;
+ m_readyState = MediaPlayer::DataUnavailable;
+ }
+
+ if (seeking())
+ m_readyState = MediaPlayer::DataUnavailable;
+
+ if (m_networkState != oldNetworkState)
+ m_player->networkStateChanged();
+ if (m_readyState != oldReadyState)
+ m_player->readyStateChanged();
+
+ if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::LoadedMetaData && m_player->visible())
+ setUpVideoRendering();
+}
+
+void MediaPlayerPrivate::loadStateChanged()
+{
+ updateStates();
+}
+
+void MediaPlayerPrivate::rateChanged()
+{
+ updateStates();
+}
+
+void MediaPlayerPrivate::sizeChanged()
+{
+}
+
+void MediaPlayerPrivate::timeChanged()
+{
+ updateStates();
+ m_player->timeChanged();
+}
+
+void MediaPlayerPrivate::didEnd()
+{
+ m_endPointTimer.stop();
+ m_startedPlaying = false;
+#if DRAW_FRAME_RATE
+ m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate];
+#endif
+ updateStates();
+ m_player->timeChanged();
+}
+
+void MediaPlayerPrivate::setRect(const IntRect& r)
+{
+ if (!m_qtMovieView)
+ return;
+
+ 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 (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();
+}
+
+void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r)
+{
+ if (context->paintingDisabled())
+ return;
+ NSView *view = m_qtMovieView.get();
+ 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];
+
+ // 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];
+}
+
+void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
+{
+ NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes];
+ int count = [fileTypes count];
+ for (int n = 0; n < count; n++) {
+ CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]);
+ RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL));
+ if (!uti)
+ continue;
+ RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType));
+ if (!mime)
+ continue;
+ types.add(mime.get());
+ }
+}
+
+bool MediaPlayerPrivate::isAvailable()
+{
+#ifdef BUILDING_ON_TIGER
+ SInt32 version;
+ OSErr result;
+ result = Gestalt(gestaltQuickTime, &version);
+ if (result != noErr) {
+ LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
+ return false;
+ }
+ if (version < minimumQuickTimeVersion) {
+ LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", version, minimumQuickTimeVersion);
+ 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)
+{
+ if (!m_qtMovie) {
+ enabledTrackCount = 0;
+ return;
+ }
+
+ static HashSet<String>* allowedTrackTypes = 0;
+ if (!allowedTrackTypes) {
+ allowedTrackTypes = new HashSet<String>;
+ allowedTrackTypes->add(QTMediaTypeVideo);
+ allowedTrackTypes->add(QTMediaTypeSound);
+ allowedTrackTypes->add(QTMediaTypeText);
+ allowedTrackTypes->add(QTMediaTypeBase);
+ allowedTrackTypes->add("clcp");
+ allowedTrackTypes->add("sbtl");
+ }
+
+ NSArray *tracks = [m_qtMovie.get() tracks];
+
+ unsigned trackCount = [tracks count];
+ enabledTrackCount = trackCount;
+ for (unsigned trackIndex = 0; trackIndex < trackCount; trackIndex++) {
+ // Grab the track at the current index. If there isn't one there, then
+ // we can move onto the next one.
+ QTTrack *track = [tracks objectAtIndex:trackIndex];
+ if (!track)
+ continue;
+
+ // Check to see if the track is disabled already, we should move along.
+ // We don't need to re-disable it.
+ if (![track isEnabled])
+ continue;
+
+ // Grab the track's media. We're going to check to see if we need to
+ // disable the tracks. They could be unsupported.
+ QTMedia *trackMedia = [track media];
+ if (!trackMedia)
+ continue;
+
+ // Grab the media type for this track.
+ NSString *mediaType = [trackMedia attributeForKey:QTMediaTypeAttribute];
+ if (!mediaType)
+ continue;
+
+ // Test whether the media type is in our white list.
+ if (!allowedTrackTypes->contains(mediaType)) {
+ // If this track type is not allowed, then we need to disable it.
+ [track setEnabled:NO];
+ --enabledTrackCount;
+ }
+
+ // Disable chapter tracks. These are most likely to lead to trouble, as
+ // they will be composited under the video tracks, forcing QT to do extra
+ // work.
+ QTTrack *chapterTrack = [track performSelector:@selector(chapterlist)];
+ if (!chapterTrack)
+ continue;
+
+ // Try to grab the media for the track.
+ QTMedia *chapterMedia = [chapterTrack media];
+ if (!chapterMedia)
+ continue;
+
+ // Grab the media type for this track.
+ id chapterMediaType = [chapterMedia attributeForKey:QTMediaTypeAttribute];
+ if (!chapterMediaType)
+ continue;
+
+ // Check to see if the track is a video track. We don't care about
+ // other non-video tracks.
+ if (![chapterMediaType isEqual:QTMediaTypeVideo])
+ continue;
+
+ // Check to see if the track is already disabled. If it is, we
+ // should move along.
+ if (![chapterTrack isEnabled])
+ continue;
+
+ // Disable the evil, evil track.
+ [chapterTrack setEnabled:NO];
+ --enabledTrackCount;
+ }
+}
+
+}
+
+@implementation WebCoreMovieObserver
+
+- (id)initWithCallback:(MediaPlayerPrivate *)callback
+{
+ m_callback = callback;
+ return [super init];
+}
+
+- (void)disconnect
+{
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ 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)
+ [self performSelector:_cmd withObject:nil afterDelay:0.];
+ else if (m_callback)
+ m_callback->repaint();
+}
+
+- (void)loadStateChanged:(NSNotification *)notification
+{
+ if (m_delayCallbacks)
+ [self performSelector:_cmd withObject:nil afterDelay:0];
+ else
+ m_callback->loadStateChanged();
+}
+
+- (void)rateChanged:(NSNotification *)notification
+{
+ if (m_delayCallbacks)
+ [self performSelector:_cmd withObject:nil afterDelay:0];
+ else
+ m_callback->rateChanged();
+}
+
+- (void)sizeChanged:(NSNotification *)notification
+{
+ if (m_delayCallbacks)
+ [self performSelector:_cmd withObject:nil afterDelay:0];
+ else
+ m_callback->sizeChanged();
+}
+
+- (void)timeChanged:(NSNotification *)notification
+{
+ if (m_delayCallbacks)
+ [self performSelector:_cmd withObject:nil afterDelay:0];
+ else
+ m_callback->timeChanged();
+}
+
+- (void)didEnd:(NSNotification *)notification
+{
+ if (m_delayCallbacks)
+ [self performSelector:_cmd withObject:nil afterDelay:0];
+ else
+ m_callback->didEnd();
+}
+
+- (void)newImageAvailable:(NSNotification *)notification
+{
+ [self repaint];
+}
+
+- (void)setDelayCallbacks:(BOOL)shouldDelay
+{
+ m_delayCallbacks = shouldDelay;
+}
+
+@end
+
+#endif
diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm
new file mode 100644
index 0000000..4ee5933
--- /dev/null
+++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov
+ *
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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.
+ */
+
+#import "config.h"
+#import "SimpleFontData.h"
+
+#import "BlockExceptions.h"
+#import "Color.h"
+#import "FloatRect.h"
+#import "Font.h"
+#import "FontCache.h"
+#import "FontDescription.h"
+#import "SharedBuffer.h"
+#import "WebCoreSystemInterface.h"
+#import <ApplicationServices/ApplicationServices.h>
+#import <float.h>
+#import <unicode/uchar.h>
+#import <wtf/Assertions.h>
+#import <wtf/RetainPtr.h>
+
+@interface NSFont (WebAppKitSecretAPI)
+- (BOOL)_isFakeFixedPitch;
+@end
+
+namespace WebCore {
+
+const float smallCapsFontSizeMultiplier = 0.7f;
+const float contextDPI = 72.0f;
+static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x * (contextDPI / (contextDPI * unitsPerEm)); }
+
+bool initFontData(SimpleFontData* fontData)
+{
+ 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);
+ return false;
+ }
+
+ ATSUAttributeTag tag = kATSUFontTag;
+ ByteCount size = sizeof(ATSUFontID);
+ ATSUFontID *valueArray[1] = {&fontId};
+ OSStatus status = ATSUSetAttributes(fontStyle, 1, &tag, &size, (void* const*)valueArray);
+ if (status != noErr) {
+ ATSUDisposeStyle(fontStyle);
+ return false;
+ }
+
+ if (wkGetATSStyleGroup(fontStyle, &fontData->m_styleGroup) != noErr) {
+ ATSUDisposeStyle(fontStyle);
+ return false;
+ }
+
+ ATSUDisposeStyle(fontStyle);
+#endif
+
+ return true;
+}
+
+static NSString *webFallbackFontFamily(void)
+{
+ static RetainPtr<NSString> webFallbackFontFamily = nil;
+ if (!webFallbackFontFamily)
+ webFallbackFontFamily = [[NSFont systemFontOfSize:16.0f] familyName];
+ 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.
+ // Try looking for an alternate 'base' font for this renderer.
+
+ // Special case hack to use "Times New Roman" in place of "Times".
+ // "Times RO" is a common font whose family name is "Times".
+ // It overrides the normal "Times" family font.
+ // It also appears to have a corrupt regular variant.
+ NSString *fallbackFontFamily;
+ if ([[m_font.font() familyName] isEqual:@"Times"])
+ fallbackFontFamily = @"Times New Roman";
+ else
+ fallbackFontFamily = webFallbackFontFamily();
+
+ // Try setting up the alternate font.
+ // This is a last ditch effort to use a substitute font when something has gone wrong.
+#if !ERROR_DISABLED
+ RetainPtr<NSFont> initialFont = m_font.font();
+#endif
+ 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 = pathFromFont(initialFont.get());
+ if (!filePath)
+ filePath = @"not known";
+#endif
+ if (!initFontData(this)) {
+ if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
+ // OK, couldn't setup Times New Roman as an alternate to Times, fallback
+ // on the system font. If this fails we have no alternative left.
+ m_font.setFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toFamily:webFallbackFontFamily()]);
+ if (!initFontData(this)) {
+ // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
+ LOG_ERROR("unable to initialize with font %@ at %@", initialFont.get(), filePath);
+ failedSetup = true;
+ }
+ } else {
+ // We tried the requested font and the system font. No joy. We have to give up.
+ LOG_ERROR("unable to initialize with font %@ at %@", initialFont.get(), filePath);
+ failedSetup = true;
+ }
+ }
+
+ // Report the problem.
+ LOG_ERROR("Corrupt font detected, using %@ in place of %@ located at \"%@\".",
+ [m_font.font() familyName], [initialFont.get() familyName], filePath);
+ }
+
+ // If all else fails, try to set up using the system font.
+ // This is probably because Times and Times New Roman are both unavailable.
+ if (failedSetup) {
+ m_font.setFont([NSFont systemFontOfSize:[m_font.font() pointSize]]);
+ LOG_ERROR("failed to set up font, using system font %s", m_font.font());
+ initFontData(this);
+ }
+
+ int iAscent;
+ int iDescent;
+ int iLineGap;
+#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;
+ float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize;
+
+ // We need to adjust Times, Helvetica, and Courier to closely match the
+ // vertical metrics of their Microsoft counterparts that are the de facto
+ // web standard. The AppKit adjustment of 20% is too big and is
+ // incorrectly added to line spacing, so we use a 15% adjustment instead
+ // and add it to the ascent.
+ 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);
+ m_lineGap = lroundf(fLineGap);
+ m_lineSpacing = m_ascent + m_descent + m_lineGap;
+
+ // Hack Hiragino line metrics to allow room for marked text underlines.
+ // <rdar://problem/5386183>
+ if (m_descent < 3 && m_lineGap >= 3 && [familyName hasPrefix:@"Hiragino"]) {
+ m_lineGap -= 3 - m_descent;
+ m_descent = 3;
+ }
+
+ // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font.
+ // Unfortunately, NSFont will round this for us so we don't quite get the right value.
+ GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
+ NSGlyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0;
+ if (xGlyph) {
+ NSRect xBox = [m_font.font() boundingRectForGlyph:xGlyph];
+ // Use the maximum of either width or height because "x" is nearly square
+ // and web pages that foolishly use this metric for width will be laid out
+ // poorly if we return an accurate height. Classic case is Times 13 point,
+ // which has an "x" that is 7x6 pixels.
+ m_xHeight = MAX(NSMaxX(xBox), NSMaxY(xBox));
+ } else
+ m_xHeight = [m_font.font() xHeight];
+}
+
+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
+{
+ if (!m_smallCapsFontData) {
+ if (isCustomFont()) {
+ FontPlatformData smallCapsFontData(m_font);
+ smallCapsFontData.m_size = smallCapsFontData.m_size * smallCapsFontSizeMultiplier;
+ m_smallCapsFontData = new SimpleFontData(smallCapsFontData, true, false);
+ } else {
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ float size = [m_font.font() pointSize] * smallCapsFontSizeMultiplier;
+ FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toSize:size]);
+
+ // AppKit resets the type information (screen/printer) when you convert a font to a different size.
+ // We have to fix up the font that we're handed back.
+ smallCapsFont.setFont(fontDescription.usePrinterFont() ? [smallCapsFont.font() printerFont] : [smallCapsFont.font() screenFont]);
+
+ if (smallCapsFont.font()) {
+ NSFontManager *fontManager = [NSFontManager sharedFontManager];
+ NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_font.font()];
+
+ if (m_font.m_syntheticBold)
+ fontTraits |= NSBoldFontMask;
+ if (m_font.m_syntheticOblique)
+ fontTraits |= NSItalicFontMask;
+
+ NSFontTraitMask smallCapsFontTraits = [fontManager traitsOfFont:smallCapsFont.font()];
+ smallCapsFont.m_syntheticBold = (fontTraits & NSBoldFontMask) && !(smallCapsFontTraits & NSBoldFontMask);
+ smallCapsFont.m_syntheticOblique = (fontTraits & NSItalicFontMask) && !(smallCapsFontTraits & NSItalicFontMask);
+
+ m_smallCapsFontData = FontCache::getCachedFontData(&smallCapsFont);
+ }
+ END_BLOCK_OBJC_EXCEPTIONS;
+ }
+ }
+ return m_smallCapsFontData;
+}
+
+bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
+{
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:(UniChar*)characters length:length freeWhenDone:NO];
+ NSCharacterSet *set = [[m_font.font() coveredCharacterSet] invertedSet];
+ bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
+ [string release];
+ return result;
+}
+
+void SimpleFontData::determinePitch()
+{
+ NSFont* f = m_font.font();
+ // Special case Osaka-Mono.
+ // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixed pitch.
+ // Note that the AppKit does not report Osaka-Mono as fixed pitch.
+
+ // Special case MS-PGothic.
+ // According to <rdar://problem/4032938>, we should not treat MS-PGothic as fixed pitch.
+ // Note that AppKit does report MS-PGothic as fixed pitch.
+
+ // Special case MonotypeCorsiva
+ // According to <rdar://problem/5454704>, we should not treat MonotypeCorsiva as fixed pitch.
+ // Note that AppKit does report MonotypeCorsiva as fixed pitch.
+
+ NSString *name = [f fontName];
+ m_treatAsFixedPitch = ([f isFixedPitch] || [f _isFakeFixedPitch] ||
+ [name caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame) &&
+ [name caseInsensitiveCompare:@"MS-PGothic"] != NSOrderedSame &&
+ [name caseInsensitiveCompare:@"MonotypeCorsiva"] != NSOrderedSame;
+}
+
+float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
+{
+ NSFont* font = m_font.font();
+ float pointSize = m_font.m_size;
+ CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
+ CGSize 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);
+
+ m_checkedShapesArabic = true;
+
+ ATSUFontID fontID = m_font.m_atsuFontID;
+ if (!fontID) {
+ LOG_ERROR("unable to get ATSUFontID for %@", m_font.font());
+ return;
+ }
+
+ // This function is called only on fonts that contain Arabic glyphs. Our
+ // heuristic is that if such a font has a glyph metamorphosis table, then
+ // it includes shaping information for Arabic.
+ FourCharCode tables[] = { 'morx', 'mort' };
+ for (unsigned i = 0; i < sizeof(tables) / sizeof(tables[0]); ++i) {
+ ByteCount tableSize;
+ OSStatus status = ATSFontGetTable(fontID, tables[i], 0, 0, 0, &tableSize);
+ if (status == noErr) {
+ m_shapesArabic = true;
+ return;
+ }
+
+ if (status != kATSInvalidFontTableAccess)
+ 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