summaryrefslogtreecommitdiff
path: root/gfx/thebes
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes')
-rw-r--r--gfx/thebes/PrintTargetCG.cpp120
-rw-r--r--gfx/thebes/PrintTargetCG.h42
-rw-r--r--gfx/thebes/gfxCoreTextShaper.cpp800
-rw-r--r--gfx/thebes/gfxCoreTextShaper.h71
-rw-r--r--gfx/thebes/gfxFontUtils.cpp41
-rw-r--r--gfx/thebes/gfxMacFont.cpp475
-rw-r--r--gfx/thebes/gfxMacFont.h102
-rw-r--r--gfx/thebes/gfxMacPlatformFontList.h182
-rw-r--r--gfx/thebes/gfxMacPlatformFontList.mm1444
-rw-r--r--gfx/thebes/gfxPlatform.cpp7
-rw-r--r--gfx/thebes/gfxPlatformMac.cpp617
-rw-r--r--gfx/thebes/gfxPlatformMac.h93
-rw-r--r--gfx/thebes/gfxPrefs.h3
-rw-r--r--gfx/thebes/gfxQuartzNativeDrawing.cpp74
-rw-r--r--gfx/thebes/gfxQuartzNativeDrawing.h71
-rw-r--r--gfx/thebes/gfxQuartzSurface.cpp137
-rw-r--r--gfx/thebes/gfxQuartzSurface.h43
-rw-r--r--gfx/thebes/gfxTextRun.cpp5
-rw-r--r--gfx/thebes/moz.build25
19 files changed, 4344 insertions, 8 deletions
diff --git a/gfx/thebes/PrintTargetCG.cpp b/gfx/thebes/PrintTargetCG.cpp
new file mode 100644
index 0000000000..5fe838182a
--- /dev/null
+++ b/gfx/thebes/PrintTargetCG.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTargetCG.h"
+
+#include "cairo.h"
+#include "cairo-quartz.h"
+#include "mozilla/gfx/HelpersCairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+PrintTargetCG::PrintTargetCG(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize)
+ : PrintTarget(aCairoSurface, aSize)
+{
+ // TODO: Add memory reporting like gfxQuartzSurface.
+ //RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
+}
+
+/* static */ already_AddRefed<PrintTargetCG>
+PrintTargetCG::CreateOrNull(const IntSize& aSize, gfxImageFormat aFormat)
+{
+ if (!Factory::CheckSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ unsigned int width = static_cast<unsigned int>(aSize.width);
+ unsigned int height = static_cast<unsigned int>(aSize.height);
+
+ cairo_format_t cformat = GfxFormatToCairoFormat(aFormat);
+ cairo_surface_t* surface =
+ cairo_quartz_surface_create(cformat, width, height);
+
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+
+ // The new object takes ownership of our surface reference.
+ RefPtr<PrintTargetCG> target = new PrintTargetCG(surface, aSize);
+
+ return target.forget();
+}
+
+/* static */ already_AddRefed<PrintTargetCG>
+PrintTargetCG::CreateOrNull(CGContextRef aContext, const IntSize& aSize)
+{
+ if (!Factory::CheckSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ unsigned int width = static_cast<unsigned int>(aSize.width);
+ unsigned int height = static_cast<unsigned int>(aSize.height);
+
+ cairo_surface_t* surface =
+ cairo_quartz_surface_create_for_cg_context(aContext, width, height);
+
+ if (cairo_surface_status(surface)) {
+ return nullptr;
+ }
+
+ // The new object takes ownership of our surface reference.
+ RefPtr<PrintTargetCG> target = new PrintTargetCG(surface, aSize);
+
+ return target.forget();
+}
+
+static size_t
+PutBytesNull(void* info, const void* buffer, size_t count)
+{
+ return count;
+}
+
+already_AddRefed<DrawTarget>
+PrintTargetCG::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
+{
+ if (!mRefDT) {
+ const IntSize size(1, 1);
+
+ CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
+ CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
+ CGContextRef pdfContext = CGPDFContextCreate(consumer, nullptr, nullptr);
+ CGDataConsumerRelease(consumer);
+
+ cairo_surface_t* similar =
+ cairo_quartz_surface_create_for_cg_context(
+ pdfContext, size.width, size.height);
+
+ CGContextRelease(pdfContext);
+
+ if (cairo_surface_status(similar)) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForCairoSurface(similar, size);
+
+ // The DT addrefs the surface, so we need drop our own reference to it:
+ cairo_surface_destroy(similar);
+
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+
+ if (aRecorder) {
+ dt = CreateRecordingDrawTarget(aRecorder, dt);
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+ }
+
+ mRefDT = dt.forget();
+ }
+ return do_AddRef(mRefDT);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/thebes/PrintTargetCG.h b/gfx/thebes/PrintTargetCG.h
new file mode 100644
index 0000000000..87dbdbc2c7
--- /dev/null
+++ b/gfx/thebes/PrintTargetCG.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PRINTTARGETCG_H
+#define MOZILLA_GFX_PRINTTARGETCG_H
+
+#include <Carbon/Carbon.h>
+#include "PrintTarget.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * CoreGraphics printing target.
+ *
+ * Note that a CGContextRef obtained from PMSessionGetCGGraphicsContext is
+ * valid only for the current page. As a consequence instances of this class
+ * should only be used to print a single page.
+ */
+class PrintTargetCG final : public PrintTarget
+{
+public:
+ static already_AddRefed<PrintTargetCG>
+ CreateOrNull(const IntSize& aSize, gfxImageFormat aFormat);
+
+ static already_AddRefed<PrintTargetCG>
+ CreateOrNull(CGContextRef aContext, const IntSize& aSize);
+
+ virtual already_AddRefed<DrawTarget>
+ GetReferenceDrawTarget(DrawEventRecorder* aRecorder) final;
+
+private:
+ PrintTargetCG(cairo_surface_t* aCairoSurface,
+ const IntSize& aSize);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PRINTTARGETCG_H */
diff --git a/gfx/thebes/gfxCoreTextShaper.cpp b/gfx/thebes/gfxCoreTextShaper.cpp
new file mode 100644
index 0000000000..08217b82f9
--- /dev/null
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -0,0 +1,800 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "gfxCoreTextShaper.h"
+#include "gfxMacFont.h"
+#include "gfxFontUtils.h"
+#include "gfxTextRun.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+#include <algorithm>
+
+#include <dlfcn.h>
+
+using namespace mozilla;
+
+// standard font descriptors that we construct the first time they're needed
+CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
+CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
+CTFontDescriptorRef gfxCoreTextShaper::sIndicFeaturesDescriptor = nullptr;
+CTFontDescriptorRef gfxCoreTextShaper::sIndicDisableLigaturesDescriptor = nullptr;
+
+static CFStringRef sCTWritingDirectionAttributeName = nullptr;
+
+// See CTStringAttributes.h
+enum {
+ kMyCTWritingDirectionEmbedding = (0 << 1),
+ kMyCTWritingDirectionOverride = (1 << 1)
+};
+
+// Helper to create a CFDictionary with the right attributes for shaping our
+// text, including imposing the given directionality.
+// This will only be called if we're on 10.8 or later.
+CFDictionaryRef
+gfxCoreTextShaper::CreateAttrDict(bool aRightToLeft)
+{
+ // Because we always shape unidirectional runs, and may have applied
+ // directional overrides, we want to force a direction rather than
+ // allowing CoreText to do its own unicode-based bidi processing.
+ SInt16 dirOverride = kMyCTWritingDirectionOverride |
+ (aRightToLeft ? kCTWritingDirectionRightToLeft
+ : kCTWritingDirectionLeftToRight);
+ CFNumberRef dirNumber =
+ ::CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt16Type, &dirOverride);
+ CFArrayRef dirArray =
+ ::CFArrayCreate(kCFAllocatorDefault,
+ (const void **) &dirNumber, 1,
+ &kCFTypeArrayCallBacks);
+ ::CFRelease(dirNumber);
+ CFTypeRef attrs[] = { kCTFontAttributeName, sCTWritingDirectionAttributeName };
+ CFTypeRef values[] = { mCTFont, dirArray };
+ CFDictionaryRef attrDict =
+ ::CFDictionaryCreate(kCFAllocatorDefault,
+ attrs, values, ArrayLength(attrs),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ ::CFRelease(dirArray);
+ return attrDict;
+}
+
+CFDictionaryRef
+gfxCoreTextShaper::CreateAttrDictWithoutDirection()
+{
+ CFTypeRef attrs[] = { kCTFontAttributeName };
+ CFTypeRef values[] = { mCTFont };
+ CFDictionaryRef attrDict =
+ ::CFDictionaryCreate(kCFAllocatorDefault,
+ attrs, values, ArrayLength(attrs),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ return attrDict;
+}
+
+gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont)
+ : gfxFontShaper(aFont)
+ , mAttributesDictLTR(nullptr)
+ , mAttributesDictRTL(nullptr)
+{
+ static bool sInitialized = false;
+ if (!sInitialized) {
+ CFStringRef* pstr = (CFStringRef*)
+ dlsym(RTLD_DEFAULT, "kCTWritingDirectionAttributeName");
+ if (pstr) {
+ sCTWritingDirectionAttributeName = *pstr;
+ }
+ sInitialized = true;
+ }
+
+ // Create our CTFontRef
+ mCTFont = CreateCTFontWithFeatures(aFont->GetAdjustedSize(),
+ GetDefaultFeaturesDescriptor());
+}
+
+gfxCoreTextShaper::~gfxCoreTextShaper()
+{
+ if (mAttributesDictLTR) {
+ ::CFRelease(mAttributesDictLTR);
+ }
+ if (mAttributesDictRTL) {
+ ::CFRelease(mAttributesDictRTL);
+ }
+ if (mCTFont) {
+ ::CFRelease(mCTFont);
+ }
+}
+
+static bool
+IsBuggyIndicScript(unicode::Script aScript)
+{
+ return aScript == unicode::Script::BENGALI ||
+ aScript == unicode::Script::KANNADA;
+}
+
+bool
+gfxCoreTextShaper::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
+ bool isRightToLeft = aShapedText->IsRightToLeft();
+ const UniChar* text = reinterpret_cast<const UniChar*>(aText);
+ uint32_t length = aLength;
+
+ uint32_t startOffset;
+ CFStringRef stringObj;
+ CFDictionaryRef attrObj;
+
+ if (sCTWritingDirectionAttributeName) {
+ startOffset = 0;
+ stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
+ text, length,
+ kCFAllocatorNull);
+
+ // Get an attributes dictionary suitable for shaping text in the
+ // current direction, creating it if necessary.
+ attrObj = isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR;
+ if (!attrObj) {
+ attrObj = CreateAttrDict(isRightToLeft);
+ (isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR) = attrObj;
+ }
+ } else {
+ // OS is too old to support kCTWritingDirectionAttributeName:
+ // we need to bidi-wrap the text if the run is RTL,
+ // or if it is an LTR run but may contain (overridden) RTL chars
+ bool bidiWrap = isRightToLeft;
+ if (!bidiWrap && !aShapedText->TextIs8Bit()) {
+ uint32_t i;
+ for (i = 0; i < length; ++i) {
+ if (gfxFontUtils::PotentialRTLChar(aText[i])) {
+ bidiWrap = true;
+ break;
+ }
+ }
+ }
+
+ // If there's a possibility of any bidi, we wrap the text with
+ // direction overrides to ensure neutrals or characters that were
+ // bidi-overridden in HTML behave properly.
+ static const UniChar beginLTR[] = { 0x202d, 0x20 };
+ static const UniChar beginRTL[] = { 0x202e, 0x20 };
+ static const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c };
+
+ if (bidiWrap) {
+ startOffset = isRightToLeft ? ArrayLength(beginRTL)
+ : ArrayLength(beginLTR);
+ CFMutableStringRef mutableString =
+ ::CFStringCreateMutable(kCFAllocatorDefault,
+ length + startOffset +
+ ArrayLength(endBidiWrap));
+ ::CFStringAppendCharacters(mutableString,
+ isRightToLeft ? beginRTL : beginLTR,
+ startOffset);
+ ::CFStringAppendCharacters(mutableString, text, length);
+ ::CFStringAppendCharacters(mutableString, endBidiWrap,
+ ArrayLength(endBidiWrap));
+ stringObj = mutableString;
+ } else {
+ startOffset = 0;
+ stringObj =
+ ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
+ text, length,
+ kCFAllocatorNull);
+ }
+
+ // Get an attributes dictionary suitable for shaping text,
+ // creating it if necessary. (This dict is not LTR-specific,
+ // but we use that field to store it anyway.)
+ if (!mAttributesDictLTR) {
+ mAttributesDictLTR = CreateAttrDictWithoutDirection();
+ }
+ attrObj = mAttributesDictLTR;
+ }
+
+ CTFontRef tempCTFont = nullptr;
+ if (IsBuggyIndicScript(aScript)) {
+ // To work around buggy Indic AAT fonts shipped with OS X,
+ // we re-enable the Line Initial Smart Swashes feature that is needed
+ // for "split vowels" to work in at least Bengali and Kannada fonts.
+ // Affected fonts include Bangla MN, Bangla Sangam MN, Kannada MN,
+ // Kannada Sangam MN. See bugs 686225, 728557, 953231, 1145515.
+ tempCTFont =
+ CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
+ aShapedText->DisableLigatures()
+ ? GetIndicDisableLigaturesDescriptor()
+ : GetIndicFeaturesDescriptor());
+ } else if (aShapedText->DisableLigatures()) {
+ // For letterspacing (or maybe other situations) we need to make
+ // a copy of the CTFont with the ligature feature disabled.
+ tempCTFont =
+ CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
+ GetDisableLigaturesDescriptor());
+ }
+
+ // For the disabled-ligature or buggy-indic-font case, we need to replace
+ // the standard CTFont in the attribute dictionary with a tweaked version.
+ CFMutableDictionaryRef mutableAttr = nullptr;
+ if (tempCTFont) {
+ mutableAttr = ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 2,
+ attrObj);
+ ::CFDictionaryReplaceValue(mutableAttr,
+ kCTFontAttributeName, tempCTFont);
+ // Having created the dict, we're finished with our temporary
+ // Indic and/or ligature-disabled CTFontRef.
+ ::CFRelease(tempCTFont);
+ attrObj = mutableAttr;
+ }
+
+ // Now we can create an attributed string
+ CFAttributedStringRef attrStringObj =
+ ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
+ ::CFRelease(stringObj);
+
+ // Create the CoreText line from our string, then we're done with it
+ CTLineRef line = ::CTLineCreateWithAttributedString(attrStringObj);
+ ::CFRelease(attrStringObj);
+
+ // and finally retrieve the glyph data and store into the gfxTextRun
+ CFArrayRef glyphRuns = ::CTLineGetGlyphRuns(line);
+ uint32_t numRuns = ::CFArrayGetCount(glyphRuns);
+
+ // Iterate through the glyph runs.
+ // Note that this includes the bidi wrapper, so we have to be careful
+ // not to include the extra glyphs from there
+ bool success = true;
+ for (uint32_t runIndex = 0; runIndex < numRuns; runIndex++) {
+ CTRunRef aCTRun =
+ (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
+ // If the range is purely within bidi-wrapping text, ignore it.
+ CFRange range = ::CTRunGetStringRange(aCTRun);
+ if (uint32_t(range.location + range.length) <= startOffset ||
+ range.location - startOffset >= aLength) {
+ continue;
+ }
+ CFDictionaryRef runAttr = ::CTRunGetAttributes(aCTRun);
+ if (runAttr != attrObj) {
+ // If Core Text manufactured a new dictionary, this may indicate
+ // unexpected font substitution. In that case, we fail (and fall
+ // back to harfbuzz shaping)...
+ const void* font1 =
+ ::CFDictionaryGetValue(attrObj, kCTFontAttributeName);
+ const void* font2 =
+ ::CFDictionaryGetValue(runAttr, kCTFontAttributeName);
+ if (font1 != font2) {
+ // ...except that if the fallback was only for a variation
+ // selector or join control that is otherwise unsupported,
+ // we just ignore it.
+ if (range.length == 1) {
+ char16_t ch = aText[range.location - startOffset];
+ if (gfxFontUtils::IsJoinControl(ch) ||
+ gfxFontUtils::IsVarSelector(ch)) {
+ continue;
+ }
+ }
+ NS_WARNING("unexpected font fallback in Core Text");
+ success = false;
+ break;
+ }
+ }
+ if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun,
+ startOffset) != NS_OK) {
+ success = false;
+ break;
+ }
+ }
+
+ if (mutableAttr) {
+ ::CFRelease(mutableAttr);
+ }
+ ::CFRelease(line);
+
+ return success;
+}
+
+#define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;
+ // some testing indicates that 90%+ of glyph runs will fit
+ // without requiring a separate allocation
+
+nsresult
+gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ CTRunRef aCTRun,
+ int32_t aStringOffset)
+{
+ // The word has been bidi-wrapped; aStringOffset is the number
+ // of chars at the beginning of the CTLine that we should skip.
+ // aCTRun is a glyph run from the CoreText layout process.
+
+ int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
+
+ int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
+ if (numGlyphs == 0) {
+ return NS_OK;
+ }
+
+ int32_t wordLength = aLength;
+
+ // character offsets get really confusing here, as we have to keep track of
+ // (a) the text in the actual textRun we're constructing
+ // (c) the string that was handed to CoreText, which contains the text of the font run
+ // plus directional-override padding
+ // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line
+ // (but may extend beyond the actual font run into the bidi wrapping text).
+ // aStringOffset tells us how many initial characters of the line to ignore.
+
+ // get the source string range within the CTLine's text
+ CFRange stringRange = ::CTRunGetStringRange(aCTRun);
+ // skip the run if it is entirely outside the actual range of the font run
+ if (stringRange.location - aStringOffset + stringRange.length <= 0 ||
+ stringRange.location - aStringOffset >= wordLength) {
+ return NS_OK;
+ }
+
+ // retrieve the laid-out glyph data from the CTRun
+ UniquePtr<CGGlyph[]> glyphsArray;
+ UniquePtr<CGPoint[]> positionsArray;
+ UniquePtr<CFIndex[]> glyphToCharArray;
+ const CGGlyph* glyphs = nullptr;
+ const CGPoint* positions = nullptr;
+ const CFIndex* glyphToChar = nullptr;
+
+ // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
+ // and so allocating a new array and copying data with CTRunGetGlyphs
+ // will be extremely rare.
+ // If this were not the case, we could use an AutoTArray<> to
+ // try and avoid the heap allocation for small runs.
+ // It's possible that some future change to CoreText will mean that
+ // CTRunGetGlyphsPtr fails more often; if this happens, AutoTArray<>
+ // may become an attractive option.
+ glyphs = ::CTRunGetGlyphsPtr(aCTRun);
+ if (!glyphs) {
+ glyphsArray = MakeUniqueFallible<CGGlyph[]>(numGlyphs);
+ if (!glyphsArray) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get());
+ glyphs = glyphsArray.get();
+ }
+
+ positions = ::CTRunGetPositionsPtr(aCTRun);
+ if (!positions) {
+ positionsArray = MakeUniqueFallible<CGPoint[]>(numGlyphs);
+ if (!positionsArray) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get());
+ positions = positionsArray.get();
+ }
+
+ // Remember that the glyphToChar indices relate to the CoreText line,
+ // not to the beginning of the textRun, the font run,
+ // or the stringRange of the glyph run
+ glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun);
+ if (!glyphToChar) {
+ glyphToCharArray = MakeUniqueFallible<CFIndex[]>(numGlyphs);
+ if (!glyphToCharArray) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
+ glyphToChar = glyphToCharArray.get();
+ }
+
+ double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
+ nullptr, nullptr, nullptr);
+
+ AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
+ gfxShapedText::CompressedGlyph *charGlyphs =
+ aShapedText->GetCharacterGlyphs() + aOffset;
+
+ // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
+ // to a source text character; we also need the charindex-to-glyphindex mapping to
+ // find the glyph for a given char. Note that some chars may not map to any glyph
+ // (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
+ // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
+ // record the last glyph index for cases where the char maps to several glyphs,
+ // so that our clumping will include all the glyph fragments for the character.
+
+ // The charToGlyph array is indexed by char position within the stringRange of the glyph run.
+
+ static const int32_t NO_GLYPH = -1;
+ AutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
+ if (!charToGlyphArray.SetLength(stringRange.length, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ int32_t *charToGlyph = charToGlyphArray.Elements();
+ for (int32_t offset = 0; offset < stringRange.length; ++offset) {
+ charToGlyph[offset] = NO_GLYPH;
+ }
+ for (int32_t i = 0; i < numGlyphs; ++i) {
+ int32_t loc = glyphToChar[i] - stringRange.location;
+ if (loc >= 0 && loc < stringRange.length) {
+ charToGlyph[loc] = i;
+ }
+ }
+
+ // Find character and glyph clumps that correspond, allowing for ligatures,
+ // indic reordering, split glyphs, etc.
+ //
+ // The idea is that we'll find a character sequence starting at the first char of stringRange,
+ // and extend it until it includes the character associated with the first glyph;
+ // we also extend it as long as there are "holes" in the range of glyphs. So we
+ // will eventually have a contiguous sequence of characters, starting at the beginning
+ // of the range, that map to a contiguous sequence of glyphs, starting at the beginning
+ // of the glyph array. That's a clump; then we update the starting positions and repeat.
+ //
+ // NB: In the case of RTL layouts, we iterate over the stringRange in reverse.
+ //
+
+ // This may find characters that fall outside the range 0:wordLength,
+ // so we won't necessarily use everything we find here.
+
+ bool isRightToLeft = aShapedText->IsRightToLeft();
+ int32_t glyphStart = 0; // looking for a clump that starts at this glyph index
+ int32_t charStart = isRightToLeft ?
+ stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)
+
+ while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
+ bool inOrder = true;
+ int32_t charEnd = glyphToChar[glyphStart] - stringRange.location;
+ NS_WARNING_ASSERTION(
+ charEnd >= 0 && charEnd < stringRange.length,
+ "glyph-to-char mapping points outside string range");
+ // clamp charEnd to the valid range of the string
+ charEnd = std::max(charEnd, 0);
+ charEnd = std::min(charEnd, int32_t(stringRange.length));
+
+ int32_t glyphEnd = glyphStart;
+ int32_t charLimit = isRightToLeft ? -1 : stringRange.length;
+ do {
+ // This is normally executed once for each iteration of the outer loop,
+ // but in unusual cases where the character/glyph association is complex,
+ // the initial character range might correspond to a non-contiguous
+ // glyph range with "holes" in it. If so, we will repeat this loop to
+ // extend the character range until we have a contiguous glyph sequence.
+ NS_ASSERTION((direction > 0 && charEnd < charLimit) ||
+ (direction < 0 && charEnd > charLimit),
+ "no characters left in range?");
+ charEnd += direction;
+ while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
+ charEnd += direction;
+ }
+
+ // find the maximum glyph index covered by the clump so far
+ if (isRightToLeft) {
+ for (int32_t i = charStart; i > charEnd; --i) {
+ if (charToGlyph[i] != NO_GLYPH) {
+ // update extent of glyph range
+ glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
+ }
+ }
+ } else {
+ for (int32_t i = charStart; i < charEnd; ++i) {
+ if (charToGlyph[i] != NO_GLYPH) {
+ // update extent of glyph range
+ glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
+ }
+ }
+ }
+
+ if (glyphEnd == glyphStart + 1) {
+ // for the common case of a single-glyph clump, we can skip the following checks
+ break;
+ }
+
+ if (glyphEnd == glyphStart) {
+ // no glyphs, try to extend the clump
+ continue;
+ }
+
+ // check whether all glyphs in the range are associated with the characters
+ // in our clump; if not, we have a discontinuous range, and should extend it
+ // unless we've reached the end of the text
+ bool allGlyphsAreWithinCluster = true;
+ int32_t prevGlyphCharIndex = charStart;
+ for (int32_t i = glyphStart; i < glyphEnd; ++i) {
+ int32_t glyphCharIndex = glyphToChar[i] - stringRange.location;
+ if (isRightToLeft) {
+ if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) {
+ allGlyphsAreWithinCluster = false;
+ break;
+ }
+ if (glyphCharIndex > prevGlyphCharIndex) {
+ inOrder = false;
+ }
+ prevGlyphCharIndex = glyphCharIndex;
+ } else {
+ if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
+ allGlyphsAreWithinCluster = false;
+ break;
+ }
+ if (glyphCharIndex < prevGlyphCharIndex) {
+ inOrder = false;
+ }
+ prevGlyphCharIndex = glyphCharIndex;
+ }
+ }
+ if (allGlyphsAreWithinCluster) {
+ break;
+ }
+ } while (charEnd != charLimit);
+
+ NS_WARNING_ASSERTION(glyphStart < glyphEnd,
+ "character/glyph clump contains no glyphs!");
+ if (glyphStart == glyphEnd) {
+ ++glyphStart; // make progress - avoid potential infinite loop
+ charStart = charEnd;
+ continue;
+ }
+
+ NS_WARNING_ASSERTION(charStart != charEnd,
+ "character/glyph clump contains no characters!");
+ if (charStart == charEnd) {
+ glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s),
+ // as there's nowhere to attach them
+ continue;
+ }
+
+ // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
+ // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
+ // and endCharIndex to the limit (position beyond the last char),
+ // adjusting for the offset of the stringRange relative to the textRun.
+ int32_t baseCharIndex, endCharIndex;
+ if (isRightToLeft) {
+ while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) {
+ charEnd--;
+ }
+ baseCharIndex = charEnd + stringRange.location - aStringOffset + 1;
+ endCharIndex = charStart + stringRange.location - aStringOffset + 1;
+ } else {
+ while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) {
+ charEnd++;
+ }
+ baseCharIndex = charStart + stringRange.location - aStringOffset;
+ endCharIndex = charEnd + stringRange.location - aStringOffset;
+ }
+
+ // Then we check if the clump falls outside our actual string range; if so, just go to the next.
+ if (endCharIndex <= 0 || baseCharIndex >= wordLength) {
+ glyphStart = glyphEnd;
+ charStart = charEnd;
+ continue;
+ }
+ // Ensure we won't try to go beyond the valid length of the word's text
+ baseCharIndex = std::max(baseCharIndex, 0);
+ endCharIndex = std::min(endCharIndex, wordLength);
+
+ // Now we're ready to set the glyph info in the textRun; measure the glyph width
+ // of the first (perhaps only) glyph, to see if it is "Simple"
+ int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
+ double toNextGlyph;
+ if (glyphStart < numGlyphs-1) {
+ toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
+ } else {
+ toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
+ }
+ int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
+
+ // Check if it's a simple one-to-one mapping
+ int32_t glyphsInClump = glyphEnd - glyphStart;
+ if (glyphsInClump == 1 &&
+ gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
+ gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
+ charGlyphs[baseCharIndex].IsClusterStart() &&
+ positions[glyphStart].y == 0.0)
+ {
+ charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
+ glyphs[glyphStart]);
+ } else {
+ // collect all glyphs in a list to be assigned to the first char;
+ // there must be at least one in the clump, and we already measured its advance,
+ // hence the placement of the loop-exit test and the measurement of the next glyph
+ while (1) {
+ gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
+ details->mGlyphID = glyphs[glyphStart];
+ details->mXOffset = 0;
+ details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit;
+ details->mAdvance = advance;
+ if (++glyphStart >= glyphEnd) {
+ break;
+ }
+ if (glyphStart < numGlyphs-1) {
+ toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
+ } else {
+ toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
+ }
+ advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
+ }
+
+ gfxTextRun::CompressedGlyph textRunGlyph;
+ textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
+ true, detailedGlyphs.Length());
+ aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph,
+ detailedGlyphs.Elements());
+
+ detailedGlyphs.Clear();
+ }
+
+ // the rest of the chars in the group are ligature continuations, no associated glyphs
+ while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
+ gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
+ NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph");
+ shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0);
+ }
+
+ glyphStart = glyphEnd;
+ charStart = charEnd;
+ }
+
+ return NS_OK;
+}
+
+#undef SMALL_GLYPH_RUN
+
+// Construct the font attribute descriptor that we'll apply by default when
+// creating a CTFontRef. This will turn off line-edge swashes by default,
+// because we don't know the actual line breaks when doing glyph shaping.
+
+// We also cache feature descriptors for shaping with disabled ligatures, and
+// for buggy Indic AAT font workarounds, created on an as-needed basis.
+
+#define MAX_FEATURES 3 // max used by any of our Get*Descriptor functions
+
+CTFontDescriptorRef
+gfxCoreTextShaper::CreateFontFeaturesDescriptor(
+ const std::pair<SInt16,SInt16> aFeatures[],
+ size_t aCount)
+{
+ MOZ_ASSERT(aCount <= MAX_FEATURES);
+
+ CFDictionaryRef featureSettings[MAX_FEATURES];
+
+ for (size_t i = 0; i < aCount; i++) {
+ CFNumberRef type = ::CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt16Type,
+ &aFeatures[i].first);
+ CFNumberRef selector = ::CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt16Type,
+ &aFeatures[i].second);
+
+ CFTypeRef keys[] = { kCTFontFeatureTypeIdentifierKey,
+ kCTFontFeatureSelectorIdentifierKey };
+ CFTypeRef values[] = { type, selector };
+ featureSettings[i] =
+ ::CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **) keys,
+ (const void **) values,
+ ArrayLength(keys),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ ::CFRelease(selector);
+ ::CFRelease(type);
+ }
+
+ CFArrayRef featuresArray =
+ ::CFArrayCreate(kCFAllocatorDefault,
+ (const void **) featureSettings,
+ aCount, // not ArrayLength(featureSettings), as we
+ // may not have used all the allocated slots
+ &kCFTypeArrayCallBacks);
+
+ for (size_t i = 0; i < aCount; i++) {
+ ::CFRelease(featureSettings[i]);
+ }
+
+ const CFTypeRef attrKeys[] = { kCTFontFeatureSettingsAttribute };
+ const CFTypeRef attrValues[] = { featuresArray };
+ CFDictionaryRef attributesDict =
+ ::CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **) attrKeys,
+ (const void **) attrValues,
+ ArrayLength(attrKeys),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ ::CFRelease(featuresArray);
+
+ CTFontDescriptorRef descriptor =
+ ::CTFontDescriptorCreateWithAttributes(attributesDict);
+ ::CFRelease(attributesDict);
+
+ return descriptor;
+}
+
+CTFontDescriptorRef
+gfxCoreTextShaper::GetDefaultFeaturesDescriptor()
+{
+ if (sDefaultFeaturesDescriptor == nullptr) {
+ const std::pair<SInt16,SInt16> kDefaultFeatures[] = {
+ { kSmartSwashType, kLineInitialSwashesOffSelector },
+ { kSmartSwashType, kLineFinalSwashesOffSelector }
+ };
+ sDefaultFeaturesDescriptor =
+ CreateFontFeaturesDescriptor(kDefaultFeatures,
+ ArrayLength(kDefaultFeatures));
+ }
+ return sDefaultFeaturesDescriptor;
+}
+
+CTFontDescriptorRef
+gfxCoreTextShaper::GetDisableLigaturesDescriptor()
+{
+ if (sDisableLigaturesDescriptor == nullptr) {
+ const std::pair<SInt16,SInt16> kDisableLigatures[] = {
+ { kSmartSwashType, kLineInitialSwashesOffSelector },
+ { kSmartSwashType, kLineFinalSwashesOffSelector },
+ { kLigaturesType, kCommonLigaturesOffSelector }
+ };
+ sDisableLigaturesDescriptor =
+ CreateFontFeaturesDescriptor(kDisableLigatures,
+ ArrayLength(kDisableLigatures));
+ }
+ return sDisableLigaturesDescriptor;
+}
+
+CTFontDescriptorRef
+gfxCoreTextShaper::GetIndicFeaturesDescriptor()
+{
+ if (sIndicFeaturesDescriptor == nullptr) {
+ const std::pair<SInt16,SInt16> kIndicFeatures[] = {
+ { kSmartSwashType, kLineFinalSwashesOffSelector }
+ };
+ sIndicFeaturesDescriptor =
+ CreateFontFeaturesDescriptor(kIndicFeatures,
+ ArrayLength(kIndicFeatures));
+ }
+ return sIndicFeaturesDescriptor;
+}
+
+CTFontDescriptorRef
+gfxCoreTextShaper::GetIndicDisableLigaturesDescriptor()
+{
+ if (sIndicDisableLigaturesDescriptor == nullptr) {
+ const std::pair<SInt16,SInt16> kIndicDisableLigatures[] = {
+ { kSmartSwashType, kLineFinalSwashesOffSelector },
+ { kLigaturesType, kCommonLigaturesOffSelector }
+ };
+ sIndicDisableLigaturesDescriptor =
+ CreateFontFeaturesDescriptor(kIndicDisableLigatures,
+ ArrayLength(kIndicDisableLigatures));
+ }
+ return sIndicDisableLigaturesDescriptor;
+}
+
+CTFontRef
+gfxCoreTextShaper::CreateCTFontWithFeatures(CGFloat aSize,
+ CTFontDescriptorRef aDescriptor)
+{
+ gfxMacFont *f = static_cast<gfxMacFont*>(mFont);
+ return ::CTFontCreateWithGraphicsFont(f->GetCGFontRef(), aSize, nullptr,
+ aDescriptor);
+}
+
+void
+gfxCoreTextShaper::Shutdown() // [static]
+{
+ if (sIndicDisableLigaturesDescriptor != nullptr) {
+ ::CFRelease(sIndicDisableLigaturesDescriptor);
+ sIndicDisableLigaturesDescriptor = nullptr;
+ }
+ if (sIndicFeaturesDescriptor != nullptr) {
+ ::CFRelease(sIndicFeaturesDescriptor);
+ sIndicFeaturesDescriptor = nullptr;
+ }
+ if (sDisableLigaturesDescriptor != nullptr) {
+ ::CFRelease(sDisableLigaturesDescriptor);
+ sDisableLigaturesDescriptor = nullptr;
+ }
+ if (sDefaultFeaturesDescriptor != nullptr) {
+ ::CFRelease(sDefaultFeaturesDescriptor);
+ sDefaultFeaturesDescriptor = nullptr;
+ }
+}
diff --git a/gfx/thebes/gfxCoreTextShaper.h b/gfx/thebes/gfxCoreTextShaper.h
new file mode 100644
index 0000000000..8e5d24f91f
--- /dev/null
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CORETEXTSHAPER_H
+#define GFX_CORETEXTSHAPER_H
+
+#include "gfxFont.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+class gfxMacFont;
+
+class gfxCoreTextShaper : public gfxFontShaper {
+public:
+ explicit gfxCoreTextShaper(gfxMacFont *aFont);
+
+ virtual ~gfxCoreTextShaper();
+
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText);
+
+ // clean up static objects that may have been cached
+ static void Shutdown();
+
+protected:
+ CTFontRef mCTFont;
+
+ // attributes for shaping text with LTR or RTL directionality
+ CFDictionaryRef mAttributesDictLTR;
+ CFDictionaryRef mAttributesDictRTL;
+
+ nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ CTRunRef aCTRun,
+ int32_t aStringOffset);
+
+ CTFontRef CreateCTFontWithFeatures(CGFloat aSize,
+ CTFontDescriptorRef aDescriptor);
+
+ CFDictionaryRef CreateAttrDict(bool aRightToLeft);
+ CFDictionaryRef CreateAttrDictWithoutDirection();
+
+ static CTFontDescriptorRef
+ CreateFontFeaturesDescriptor(const std::pair<SInt16,SInt16> aFeatures[],
+ size_t aCount);
+
+ static CTFontDescriptorRef GetDefaultFeaturesDescriptor();
+ static CTFontDescriptorRef GetDisableLigaturesDescriptor();
+ static CTFontDescriptorRef GetIndicFeaturesDescriptor();
+ static CTFontDescriptorRef GetIndicDisableLigaturesDescriptor();
+
+ // cached font descriptor, created the first time it's needed
+ static CTFontDescriptorRef sDefaultFeaturesDescriptor;
+
+ // cached descriptor for adding disable-ligatures setting to a font
+ static CTFontDescriptorRef sDisableLigaturesDescriptor;
+
+ // feature descriptors for buggy Indic AAT font workaround
+ static CTFontDescriptorRef sIndicFeaturesDescriptor;
+ static CTFontDescriptorRef sIndicDisableLigaturesDescriptor;
+};
+
+#endif /* GFX_CORETEXTSHAPER_H */
diff --git a/gfx/thebes/gfxFontUtils.cpp b/gfx/thebes/gfxFontUtils.cpp
index 0d4b309f6c..54ca03ff6c 100644
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -379,14 +379,24 @@ gfxFontUtils::ReadCMAPTableFormat14(const uint8_t *aBuf, uint32_t aLength,
return NS_OK;
}
-// For fonts with two format-4 tables, we allow the first one (Unicode platform)
-// to be replaced by the Microsoft-platform subtable.
+// For fonts with two format-4 tables, the first one (Unicode platform) is preferred on the Mac;
+// on other platforms we allow the Microsoft-platform subtable to replace it.
-#define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft) || \
- ((p) == PLATFORM_ID_UNICODE))
+#if defined(XP_MACOSX)
+ #define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft && !(k)) || \
+ ((p) == PLATFORM_ID_UNICODE))
-#define acceptableUCS4Encoding(p, e, k) \
+ #define acceptableUCS4Encoding(p, e, k) \
+ (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform) && (k) != 12 || \
+ ((p) == PLATFORM_ID_UNICODE && \
+ ((e) != EncodingIDUVSForUnicodePlatform)))
+#else
+ #define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft) || \
+ ((p) == PLATFORM_ID_UNICODE))
+
+ #define acceptableUCS4Encoding(p, e, k) \
((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform)
+#endif
#define acceptablePlatform(p) ((p) == PLATFORM_ID_UNICODE || (p) == PLATFORM_ID_MICROSOFT)
#define isSymbol(p,e) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDSymbol)
@@ -1220,8 +1230,13 @@ gfxFontUtils::GetFamilyNameFromTable(hb_blob_t *aNameTable,
}
enum {
+#if defined(XP_MACOSX)
+ CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MAC_ENGLISH,
+ PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MAC
+#else
CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MICROSOFT_EN_US,
PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MICROSOFT
+#endif
};
nsresult
@@ -1262,6 +1277,22 @@ gfxFontUtils::ReadCanonicalName(const char *aNameData, uint32_t aDataLen,
NS_ENSURE_SUCCESS(rv, rv);
}
+#if defined(XP_MACOSX)
+ // may be dealing with font that only has Microsoft name entries
+ if (names.Length() == 0) {
+ rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ID_MICROSOFT_EN_US,
+ PLATFORM_ID_MICROSOFT, names);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // getting really desperate now, take anything!
+ if (names.Length() == 0) {
+ rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
+ PLATFORM_ID_MICROSOFT, names);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+#endif
+
// return the first name (99.9% of the time names will
// contain a single English name)
if (names.Length()) {
diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp
new file mode 100644
index 0000000000..f512c689f7
--- /dev/null
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -0,0 +1,475 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxMacFont.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Sprintf.h"
+
+#include "gfxCoreTextShaper.h"
+#include <algorithm>
+#include "gfxPlatformMac.h"
+#include "gfxContext.h"
+#include "gfxFontUtils.h"
+#include "gfxMacPlatformFontList.h"
+#include "gfxFontConstants.h"
+#include "gfxTextRun.h"
+
+#include "cairo-quartz.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+ bool aNeedsBold)
+ : gfxFont(aFontEntry, aFontStyle),
+ mCGFont(nullptr),
+ mCTFont(nullptr),
+ mFontFace(nullptr)
+{
+ mApplySyntheticBold = aNeedsBold;
+
+ mCGFont = aFontEntry->GetFontRef();
+ if (!mCGFont) {
+ mIsValid = false;
+ return;
+ }
+
+ // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
+ InitMetrics();
+ if (!mIsValid) {
+ return;
+ }
+
+ mFontFace = cairo_quartz_font_face_create_for_cgfont(mCGFont);
+
+ cairo_status_t cairoerr = cairo_font_face_status(mFontFace);
+ if (cairoerr != CAIRO_STATUS_SUCCESS) {
+ mIsValid = false;
+#ifdef DEBUG
+ char warnBuf[1024];
+ SprintfLiteral(warnBuf,
+ "Failed to create Cairo font face: %s status: %d",
+ NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
+ NS_WARNING(warnBuf);
+#endif
+ return;
+ }
+
+ cairo_matrix_t sizeMatrix, ctm;
+ cairo_matrix_init_identity(&ctm);
+ cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
+
+ // synthetic oblique by skewing via the font matrix
+ bool needsOblique = mFontEntry != nullptr &&
+ mFontEntry->IsUpright() &&
+ mStyle.style != NS_FONT_STYLE_NORMAL &&
+ mStyle.allowSyntheticStyle;
+
+ if (needsOblique) {
+ cairo_matrix_t style;
+ cairo_matrix_init(&style,
+ 1, //xx
+ 0, //yx
+ -1 * OBLIQUE_SKEW_FACTOR, //xy
+ 1, //yy
+ 0, //x0
+ 0); //y0
+ cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
+ }
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+ // turn off font anti-aliasing based on user pref setting
+ if (mAdjustedSize <=
+ (gfxFloat)gfxPlatformMac::GetPlatform()->GetAntiAliasingThreshold()) {
+ cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_NONE);
+ mAntialiasOption = kAntialiasNone;
+ } else if (mStyle.useGrayscaleAntialiasing) {
+ cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_GRAY);
+ mAntialiasOption = kAntialiasGrayscale;
+ }
+
+ mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix, &ctm,
+ fontOptions);
+ cairo_font_options_destroy(fontOptions);
+
+ cairoerr = cairo_scaled_font_status(mScaledFont);
+ if (cairoerr != CAIRO_STATUS_SUCCESS) {
+ mIsValid = false;
+#ifdef DEBUG
+ char warnBuf[1024];
+ SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d",
+ NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
+ NS_WARNING(warnBuf);
+#endif
+ }
+}
+
+gfxMacFont::~gfxMacFont()
+{
+ if (mCTFont) {
+ ::CFRelease(mCTFont);
+ }
+ if (mScaledFont) {
+ cairo_scaled_font_destroy(mScaledFont);
+ }
+ if (mFontFace) {
+ cairo_font_face_destroy(mFontFace);
+ }
+}
+
+bool
+gfxMacFont::ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText)
+{
+ if (!mIsValid) {
+ NS_WARNING("invalid font! expect incorrect text rendering");
+ return false;
+ }
+
+ // Currently, we don't support vertical shaping via CoreText,
+ // so we ignore RequiresAATLayout if vertical is requested.
+ if (static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout() &&
+ !aVertical) {
+ if (!mCoreTextShaper) {
+ mCoreTextShaper = MakeUnique<gfxCoreTextShaper>(this);
+ }
+ if (mCoreTextShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
+ aScript, aVertical, aShapedText)) {
+ PostShapingFixup(aDrawTarget, aText, aOffset,
+ aLength, aVertical, aShapedText);
+ return true;
+ }
+ }
+
+ return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
+ aVertical, aShapedText);
+}
+
+bool
+gfxMacFont::SetupCairoFont(DrawTarget* aDrawTarget)
+{
+ if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
+ // Don't cairo_set_scaled_font as that would propagate the error to
+ // the cairo_t, precluding any further drawing.
+ return false;
+ }
+ cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), mScaledFont);
+ return true;
+}
+
+gfxFont::RunMetrics
+gfxMacFont::Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget *aRefDrawTarget,
+ Spacing *aSpacing,
+ uint16_t aOrientation)
+{
+ gfxFont::RunMetrics metrics =
+ gfxFont::Measure(aTextRun, aStart, aEnd,
+ aBoundingBoxType, aRefDrawTarget, aSpacing,
+ aOrientation);
+
+ // if aBoundingBoxType is not TIGHT_HINTED_OUTLINE_EXTENTS then we need to add
+ // a pixel column each side of the bounding box in case of antialiasing "bleed"
+ if (aBoundingBoxType != TIGHT_HINTED_OUTLINE_EXTENTS &&
+ metrics.mBoundingBox.width > 0) {
+ metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
+ metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 2;
+ }
+
+ return metrics;
+}
+
+void
+gfxMacFont::InitMetrics()
+{
+ mIsValid = false;
+ ::memset(&mMetrics, 0, sizeof(mMetrics));
+
+ uint32_t upem = 0;
+
+ // try to get unitsPerEm from sfnt head table, to avoid calling CGFont
+ // if possible (bug 574368) and because CGFontGetUnitsPerEm does not
+ // return the true value for OpenType/CFF fonts (it normalizes to 1000,
+ // which then leads to metrics errors when we read the 'hmtx' table to
+ // get glyph advances for HarfBuzz, see bug 580863)
+ CFDataRef headData =
+ ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('h','e','a','d'));
+ if (headData) {
+ if (size_t(::CFDataGetLength(headData)) >= sizeof(HeadTable)) {
+ const HeadTable *head =
+ reinterpret_cast<const HeadTable*>(::CFDataGetBytePtr(headData));
+ upem = head->unitsPerEm;
+ }
+ ::CFRelease(headData);
+ }
+ if (!upem) {
+ upem = ::CGFontGetUnitsPerEm(mCGFont);
+ }
+
+ if (upem < 16 || upem > 16384) {
+ // See http://www.microsoft.com/typography/otspec/head.htm
+#ifdef DEBUG
+ char warnBuf[1024];
+ SprintfLiteral(warnBuf,
+ "Bad font metrics for: %s (invalid unitsPerEm value)",
+ NS_ConvertUTF16toUTF8(mFontEntry->Name()).get());
+ NS_WARNING(warnBuf);
+#endif
+ return;
+ }
+
+ mAdjustedSize = std::max(mStyle.size, 1.0);
+ mFUnitsConvFactor = mAdjustedSize / upem;
+
+ // For CFF fonts, when scaling values read from CGFont* APIs, we need to
+ // use CG's idea of unitsPerEm, which may differ from the "true" value in
+ // the head table of the font (see bug 580863)
+ gfxFloat cgConvFactor;
+ if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
+ cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
+ } else {
+ cgConvFactor = mFUnitsConvFactor;
+ }
+
+ // Try to read 'sfnt' metrics; for local, non-sfnt fonts ONLY, fall back to
+ // platform APIs. The InitMetrics...() functions will set mIsValid on success.
+ if (!InitMetricsFromSfntTables(mMetrics) &&
+ (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
+ InitMetricsFromPlatform();
+ }
+ if (!mIsValid) {
+ return;
+ }
+
+ if (mMetrics.xHeight == 0.0) {
+ mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
+ }
+
+ if (mMetrics.capHeight == 0.0) {
+ mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
+ }
+
+ if (mStyle.sizeAdjust > 0.0 && mStyle.size > 0.0 &&
+ mMetrics.xHeight > 0.0) {
+ // apply font-size-adjust, and recalculate metrics
+ gfxFloat aspect = mMetrics.xHeight / mStyle.size;
+ mAdjustedSize = mStyle.GetAdjustedSize(aspect);
+ mFUnitsConvFactor = mAdjustedSize / upem;
+ if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
+ cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
+ } else {
+ cgConvFactor = mFUnitsConvFactor;
+ }
+ mMetrics.xHeight = 0.0;
+ if (!InitMetricsFromSfntTables(mMetrics) &&
+ (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
+ InitMetricsFromPlatform();
+ }
+ if (!mIsValid) {
+ // this shouldn't happen, as we succeeded earlier before applying
+ // the size-adjust factor! But check anyway, for paranoia's sake.
+ return;
+ }
+ if (mMetrics.xHeight == 0.0) {
+ mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
+ }
+ }
+
+ // Once we reach here, we've got basic metrics and set mIsValid = TRUE;
+ // there should be no further points of actual failure in InitMetrics().
+ // (If one is introduced, be sure to reset mIsValid to FALSE!)
+
+ mMetrics.emHeight = mAdjustedSize;
+
+ // Measure/calculate additional metrics, independent of whether we used
+ // the tables directly or ATS metrics APIs
+
+ CFDataRef cmap =
+ ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c','m','a','p'));
+
+ uint32_t glyphID;
+ if (mMetrics.aveCharWidth <= 0) {
+ mMetrics.aveCharWidth = GetCharWidth(cmap, 'x', &glyphID,
+ cgConvFactor);
+ if (glyphID == 0) {
+ // we didn't find 'x', so use maxAdvance rather than zero
+ mMetrics.aveCharWidth = mMetrics.maxAdvance;
+ }
+ }
+ if (IsSyntheticBold()) {
+ mMetrics.aveCharWidth += GetSyntheticBoldOffset();
+ mMetrics.maxAdvance += GetSyntheticBoldOffset();
+ }
+
+ mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID, cgConvFactor);
+ if (glyphID == 0) {
+ // no space glyph?!
+ mMetrics.spaceWidth = mMetrics.aveCharWidth;
+ }
+ mSpaceGlyph = glyphID;
+
+ mMetrics.zeroOrAveCharWidth = GetCharWidth(cmap, '0', &glyphID,
+ cgConvFactor);
+ if (glyphID == 0) {
+ mMetrics.zeroOrAveCharWidth = mMetrics.aveCharWidth;
+ }
+
+ if (cmap) {
+ ::CFRelease(cmap);
+ }
+
+ CalculateDerivedMetrics(mMetrics);
+
+ SanitizeMetrics(&mMetrics, mFontEntry->mIsBadUnderlineFont);
+
+#if 0
+ fprintf (stderr, "Font: %p (%s) size: %f\n", this,
+ NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
+// fprintf (stderr, " fbounds.origin.x %f y %f size.width %f height %f\n", fbounds.origin.x, fbounds.origin.y, fbounds.size.width, fbounds.size.height);
+ fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
+ fprintf (stderr, " maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
+ fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
+ fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f capHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight, mMetrics.capHeight);
+ fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
+#endif
+}
+
+gfxFloat
+gfxMacFont::GetCharWidth(CFDataRef aCmap, char16_t aUniChar,
+ uint32_t *aGlyphID, gfxFloat aConvFactor)
+{
+ CGGlyph glyph = 0;
+
+ if (aCmap) {
+ glyph = gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap),
+ ::CFDataGetLength(aCmap),
+ aUniChar);
+ }
+
+ if (aGlyphID) {
+ *aGlyphID = glyph;
+ }
+
+ if (glyph) {
+ int advance;
+ if (::CGFontGetGlyphAdvances(mCGFont, &glyph, 1, &advance)) {
+ return advance * aConvFactor;
+ }
+ }
+
+ return 0;
+}
+
+int32_t
+gfxMacFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
+{
+ if (!mCTFont) {
+ mCTFont = ::CTFontCreateWithGraphicsFont(mCGFont, mAdjustedSize,
+ nullptr, nullptr);
+ if (!mCTFont) { // shouldn't happen, but let's be safe
+ NS_WARNING("failed to create CTFontRef to measure glyph width");
+ return 0;
+ }
+ }
+
+ CGSize advance;
+ ::CTFontGetAdvancesForGlyphs(mCTFont, kCTFontDefaultOrientation, &aGID,
+ &advance, 1);
+ return advance.width * 0x10000;
+}
+
+// Try to initialize font metrics via platform APIs (CG/CT),
+// and set mIsValid = TRUE on success.
+// We ONLY call this for local (platform) fonts that are not sfnt format;
+// for sfnts, including ALL downloadable fonts, we prefer to use
+// InitMetricsFromSfntTables and avoid platform APIs.
+void
+gfxMacFont::InitMetricsFromPlatform()
+{
+ CTFontRef ctFont = ::CTFontCreateWithGraphicsFont(mCGFont,
+ mAdjustedSize,
+ nullptr, nullptr);
+ if (!ctFont) {
+ return;
+ }
+
+ mMetrics.underlineOffset = ::CTFontGetUnderlinePosition(ctFont);
+ mMetrics.underlineSize = ::CTFontGetUnderlineThickness(ctFont);
+
+ mMetrics.externalLeading = ::CTFontGetLeading(ctFont);
+
+ mMetrics.maxAscent = ::CTFontGetAscent(ctFont);
+ mMetrics.maxDescent = ::CTFontGetDescent(ctFont);
+
+ // this is not strictly correct, but neither CTFont nor CGFont seems to
+ // provide maxAdvance, unless we were to iterate over all the glyphs
+ // (which isn't worth the cost here)
+ CGRect r = ::CTFontGetBoundingBox(ctFont);
+ mMetrics.maxAdvance = r.size.width;
+
+ // aveCharWidth is also not provided, so leave it at zero
+ // (fallback code in gfxMacFont::InitMetrics will then try measuring 'x');
+ // this could lead to less-than-"perfect" text field sizing when width is
+ // specified as a number of characters, and the font in use is a non-sfnt
+ // legacy font, but that's a sufficiently obscure edge case that we can
+ // ignore the potential discrepancy.
+ mMetrics.aveCharWidth = 0;
+
+ mMetrics.xHeight = ::CTFontGetXHeight(ctFont);
+ mMetrics.capHeight = ::CTFontGetCapHeight(ctFont);
+
+ ::CFRelease(ctFont);
+
+ mIsValid = true;
+}
+
+already_AddRefed<ScaledFont>
+gfxMacFont::GetScaledFont(DrawTarget *aTarget)
+{
+ if (!mAzureScaledFont) {
+ NativeFont nativeFont;
+ nativeFont.mType = NativeFontType::MAC_FONT_FACE;
+ nativeFont.mFont = GetCGFontRef();
+ mAzureScaledFont = mozilla::gfx::Factory::CreateScaledFontWithCairo(nativeFont, GetAdjustedSize(), mScaledFont);
+ }
+
+ RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
+ return scaledFont.forget();
+}
+
+already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
+gfxMacFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
+{
+ if (aRunParams) {
+ return mozilla::gfx::Factory::CreateCGGlyphRenderingOptions(aRunParams->fontSmoothingBGColor);
+ }
+ return nullptr;
+}
+
+void
+gfxMacFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+ // mCGFont is shared with the font entry, so not counted here;
+ // and we don't have APIs to measure the cairo mFontFace object
+}
+
+void
+gfxMacFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const
+{
+ aSizes->mFontInstances += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h
new file mode 100644
index 0000000000..d12cc717ba
--- /dev/null
+++ b/gfx/thebes/gfxMacFont.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MACFONT_H
+#define GFX_MACFONT_H
+
+#include "mozilla/MemoryReporting.h"
+#include "gfxFont.h"
+#include "cairo.h"
+#include <ApplicationServices/ApplicationServices.h>
+
+class MacOSFontEntry;
+
+class gfxMacFont : public gfxFont
+{
+public:
+ gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+ bool aNeedsBold);
+
+ virtual ~gfxMacFont();
+
+ CGFontRef GetCGFontRef() const { return mCGFont; }
+
+ /* overrides for the pure virtual methods in gfxFont */
+ virtual uint32_t GetSpaceGlyph() override {
+ return mSpaceGlyph;
+ }
+
+ virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
+
+ /* override Measure to add padding for antialiasing */
+ virtual RunMetrics Measure(const gfxTextRun *aTextRun,
+ uint32_t aStart, uint32_t aEnd,
+ BoundingBoxType aBoundingBoxType,
+ DrawTarget *aDrawTargetForTightBoundingBox,
+ Spacing *aSpacing,
+ uint16_t aOrientation) override;
+
+ // We need to provide hinted (non-linear) glyph widths if using a font
+ // with embedded color bitmaps (Apple Color Emoji), as Core Text renders
+ // the glyphs with non-linear scaling at small pixel sizes.
+ virtual bool ProvidesGlyphWidths() const override {
+ return mFontEntry->HasFontTable(TRUETYPE_TAG('s','b','i','x'));
+ }
+
+ virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
+ uint16_t aGID) override;
+
+ virtual already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFont(mozilla::gfx::DrawTarget *aTarget) override;
+
+ virtual already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
+ GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) override;
+
+ virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const override;
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontCacheSizes* aSizes) const override;
+
+ virtual FontType GetType() const override { return FONT_TYPE_MAC; }
+
+protected:
+ virtual const Metrics& GetHorizontalMetrics() override {
+ return mMetrics;
+ }
+
+ // override to prefer CoreText shaping with fonts that depend on AAT
+ virtual bool ShapeText(DrawTarget *aDrawTarget,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ Script aScript,
+ bool aVertical,
+ gfxShapedText *aShapedText) override;
+
+ void InitMetrics();
+ void InitMetricsFromPlatform();
+
+ // Get width and glyph ID for a character; uses aConvFactor
+ // to convert font units as returned by CG to actual dimensions
+ gfxFloat GetCharWidth(CFDataRef aCmap, char16_t aUniChar,
+ uint32_t *aGlyphID, gfxFloat aConvFactor);
+
+ // a weak reference to the CoreGraphics font: this is owned by the
+ // MacOSFontEntry, it is not retained or released by gfxMacFont
+ CGFontRef mCGFont;
+
+ // a Core Text font reference, created only if we're using CT to measure
+ // glyph widths; otherwise null.
+ CTFontRef mCTFont;
+
+ cairo_font_face_t *mFontFace;
+
+ mozilla::UniquePtr<gfxFontShaper> mCoreTextShaper;
+
+ Metrics mMetrics;
+ uint32_t mSpaceGlyph;
+};
+
+#endif /* GFX_MACFONT_H */
diff --git a/gfx/thebes/gfxMacPlatformFontList.h b/gfx/thebes/gfxMacPlatformFontList.h
new file mode 100644
index 0000000000..0ab062dca4
--- /dev/null
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfxMacPlatformFontList_H_
+#define gfxMacPlatformFontList_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "mozilla/MemoryReporting.h"
+#include "nsDataHashtable.h"
+#include "nsRefPtrHashtable.h"
+
+#include "gfxPlatformFontList.h"
+#include "gfxPlatform.h"
+#include "gfxPlatformMac.h"
+
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+#include "mozilla/LookAndFeel.h"
+
+class gfxMacPlatformFontList;
+
+// a single member of a font family (i.e. a single face, such as Times Italic)
+class MacOSFontEntry : public gfxFontEntry
+{
+public:
+ friend class gfxMacPlatformFontList;
+
+ MacOSFontEntry(const nsAString& aPostscriptName, int32_t aWeight,
+ bool aIsStandardFace = false,
+ double aSizeHint = 0.0);
+
+ // for use with data fonts
+ MacOSFontEntry(const nsAString& aPostscriptName, CGFontRef aFontRef,
+ uint16_t aWeight, uint16_t aStretch, uint8_t aStyle,
+ bool aIsDataUserFont, bool aIsLocal);
+
+ virtual ~MacOSFontEntry() {
+ ::CGFontRelease(mFontRef);
+ }
+
+ virtual CGFontRef GetFontRef();
+
+ // override gfxFontEntry table access function to bypass table cache,
+ // use CGFontRef API to get direct access to system font data
+ virtual hb_blob_t *GetFontTable(uint32_t aTag) override;
+
+ virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const override;
+
+ nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
+
+ bool RequiresAATLayout() const { return mRequiresAAT; }
+
+ bool IsCFF();
+
+protected:
+ virtual gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) override;
+
+ virtual bool HasFontTable(uint32_t aTableTag) override;
+
+ static void DestroyBlobFunc(void* aUserData);
+
+ CGFontRef mFontRef; // owning reference to the CGFont, released on destruction
+
+ double mSizeHint;
+
+ bool mFontRefInitialized;
+ bool mRequiresAAT;
+ bool mIsCFF;
+ bool mIsCFFInitialized;
+ nsTHashtable<nsUint32HashKey> mAvailableTables;
+};
+
+class gfxMacPlatformFontList : public gfxPlatformFontList {
+public:
+ static gfxMacPlatformFontList* PlatformFontList() {
+ return static_cast<gfxMacPlatformFontList*>(sPlatformFontList);
+ }
+
+ static int32_t AppleWeightToCSSWeight(int32_t aAppleWeight);
+
+ bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
+
+ gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle) override;
+
+ gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength) override;
+
+ bool FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle = nullptr,
+ gfxFloat aDevToCssSize = 1.0) override;
+
+ // lookup the system font for a particular system font type and set
+ // the name and style characteristics
+ void LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
+ nsAString& aSystemFontName,
+ gfxFontStyle &aFontStyle,
+ float aDevPixPerCSSPixel);
+
+protected:
+ virtual gfxFontFamily*
+ GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
+private:
+ friend class gfxPlatformMac;
+
+ gfxMacPlatformFontList();
+ virtual ~gfxMacPlatformFontList();
+
+ // initialize font lists
+ virtual nsresult InitFontListForPlatform() override;
+
+ // special case font faces treated as font families (set via prefs)
+ void InitSingleFaceList();
+
+ // initialize system fonts
+ void InitSystemFontNames();
+
+ // helper function to lookup in both hidden system fonts and normal fonts
+ gfxFontFamily* FindSystemFontFamily(const nsAString& aFamily);
+
+ static void RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
+ void *observer,
+ CFStringRef name,
+ const void *object,
+ CFDictionaryRef userInfo);
+
+ // attempt to use platform-specific fallback for the given character
+ // return null if no usable result found
+ gfxFontEntry*
+ PlatformGlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily) override;
+
+ bool UsesSystemFallback() override { return true; }
+
+ already_AddRefed<FontInfoData> CreateFontInfoData() override;
+
+ // Add the specified family to mSystemFontFamilies or mFontFamilies.
+ // Ideally we'd use NSString* instead of CFStringRef here, but this header
+ // file is included in .cpp files, so we can't use objective C classes here.
+ // But CFStringRef and NSString* are the same thing anyway (they're
+ // toll-free bridged).
+ void AddFamily(CFStringRef aFamily);
+
+#ifdef MOZ_BUNDLED_FONTS
+ void ActivateBundledFonts();
+#endif
+
+ enum {
+ kATSGenerationInitial = -1
+ };
+
+ // default font for use with system-wide font fallback
+ CTFontRef mDefaultFont;
+
+ // hidden system fonts used within UI elements, there may be a whole set
+ // for different locales (e.g. .Helvetica Neue UI, .SF NS Text)
+ FontFamilyTable mSystemFontFamilies;
+
+ // font families that -apple-system maps to
+ // Pre-10.11 this was always a single font family, such as Lucida Grande
+ // or Helvetica Neue. For OSX 10.11, Apple uses pair of families
+ // for the UI, one for text sizes and another for display sizes
+ bool mUseSizeSensitiveSystemFont;
+ nsString mSystemTextFontFamilyName;
+ nsString mSystemDisplayFontFamilyName; // only used on OSX 10.11
+};
+
+#endif /* gfxMacPlatformFontList_H_ */
diff --git a/gfx/thebes/gfxMacPlatformFontList.mm b/gfx/thebes/gfxMacPlatformFontList.mm
new file mode 100644
index 0000000000..4536ab527b
--- /dev/null
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -0,0 +1,1444 @@
+/* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: BSD
+ *
+ * Copyright (C) 2006-2009 Mozilla Corporation. All rights reserved.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ * Masayuki Nakano <masayuki@d-toybox.com>
+ * John Daggett <jdaggett@mozilla.com>
+ * Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Copyright (C) 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.
+ * 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.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/Logging.h"
+
+#include <algorithm>
+
+#import <AppKit/AppKit.h>
+
+#include "gfxPlatformMac.h"
+#include "gfxMacPlatformFontList.h"
+#include "gfxMacFont.h"
+#include "gfxUserFontSet.h"
+#include "harfbuzz/hb.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
+
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISimpleEnumerator.h"
+#include "nsCharTraits.h"
+#include "nsCocoaFeatures.h"
+#include "nsCocoaUtils.h"
+#include "gfxFontConstants.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/gfx/2D.h"
+
+#include <unistd.h>
+#include <time.h>
+#include <dlfcn.h>
+
+using namespace mozilla;
+
+// indexes into the NSArray objects that the Cocoa font manager returns
+// as the available members of a family
+#define INDEX_FONT_POSTSCRIPT_NAME 0
+#define INDEX_FONT_FACE_NAME 1
+#define INDEX_FONT_WEIGHT 2
+#define INDEX_FONT_TRAITS 3
+
+static const int kAppleMaxWeight = 14;
+static const int kAppleExtraLightWeight = 3;
+static const int kAppleUltraLightWeight = 2;
+
+static const int gAppleWeightToCSSWeight[] = {
+ 0,
+ 1, // 1.
+ 1, // 2. W1, ultralight
+ 2, // 3. W2, extralight
+ 3, // 4. W3, light
+ 4, // 5. W4, semilight
+ 5, // 6. W5, medium
+ 6, // 7.
+ 6, // 8. W6, semibold
+ 7, // 9. W7, bold
+ 8, // 10. W8, extrabold
+ 8, // 11.
+ 9, // 12. W9, ultrabold
+ 9, // 13
+ 9 // 14
+};
+
+// cache Cocoa's "shared font manager" for performance
+static NSFontManager *sFontManager;
+
+static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
+{
+ aDist.SetLength([aSrc length]);
+ [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())];
+}
+
+static NSString* GetNSStringForString(const nsAString& aSrc)
+{
+ return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
+ length:aSrc.Length()];
+}
+
+#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
+ mozilla::LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_fontlist), \
+ mozilla::LogLevel::Debug)
+#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
+ gfxPlatform::GetLog(eGfxLog_cmapdata), \
+ mozilla::LogLevel::Debug)
+
+#pragma mark-
+
+// Complex scripts will not render correctly unless appropriate AAT or OT
+// layout tables are present.
+// For OpenType, we also check that the GSUB table supports the relevant
+// script tag, to avoid using things like Arial Unicode MS for Lao (it has
+// the characters, but lacks OpenType support).
+
+// TODO: consider whether we should move this to gfxFontEntry and do similar
+// cmap-masking on other platforms to avoid using fonts that won't shape
+// properly.
+
+nsresult
+MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
+{
+ // attempt this once, if errors occur leave a blank cmap
+ if (mCharacterMap) {
+ return NS_OK;
+ }
+
+ RefPtr<gfxCharacterMap> charmap;
+ nsresult rv;
+ bool symbolFont = false; // currently ignored
+
+ if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+ mUVSOffset,
+ symbolFont))) {
+ rv = NS_OK;
+ } else {
+ uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+ charmap = new gfxCharacterMap();
+ AutoTable cmapTable(this, kCMAP);
+
+ if (cmapTable) {
+ bool unicodeFont = false; // currently ignored
+ uint32_t cmapLen;
+ const uint8_t* cmapData =
+ reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
+ &cmapLen));
+ rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
+ *charmap, mUVSOffset,
+ unicodeFont, symbolFont);
+ } else {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+
+ if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
+ // We assume a Graphite font knows what it's doing,
+ // and provides whatever shaping is needed for the
+ // characters it supports, so only check/clear the
+ // complex-script ranges for non-Graphite fonts
+
+ // for layout support, check for the presence of mort/morx and/or
+ // opentype layout tables
+ bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
+ HasFontTable(TRUETYPE_TAG('m','o','r','t'));
+ bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
+ bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
+ if (hasAATLayout && !(hasGSUB || hasGPOS)) {
+ mRequiresAAT = true; // prefer CoreText if font has no OTL tables
+ }
+
+ for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
+ sr->rangeStart; sr++) {
+ // check to see if the cmap includes complex script codepoints
+ if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
+ if (hasAATLayout) {
+ // prefer CoreText for Apple's complex-script fonts,
+ // even if they also have some OpenType tables
+ // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
+ mRequiresAAT = true;
+ // and don't mask off complex-script ranges, we assume
+ // the AAT tables will provide the necessary shaping
+ continue;
+ }
+
+ // We check for GSUB here, as GPOS alone would not be ok.
+ if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
+ continue;
+ }
+
+ charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
+ }
+ }
+
+ // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious
+ // blank glyphs for obscure Tibetan and Arabic-script codepoints.
+ // Blacklist these so that font fallback will not use them.
+ if (mRequiresAAT && (FamilyName().EqualsLiteral("Songti SC") ||
+ FamilyName().EqualsLiteral("Songti TC") ||
+ FamilyName().EqualsLiteral("STSong") ||
+ // Bug 1390980: on 10.11, the Kaiti fonts are also affected.
+ FamilyName().EqualsLiteral("Kaiti SC") ||
+ FamilyName().EqualsLiteral("Kaiti TC") ||
+ FamilyName().EqualsLiteral("STKaiti"))) {
+ charmap->ClearRange(0x0f6b, 0x0f70);
+ charmap->ClearRange(0x0f8c, 0x0f8f);
+ charmap->clear(0x0f98);
+ charmap->clear(0x0fbd);
+ charmap->ClearRange(0x0fcd, 0x0fff);
+ charmap->clear(0x0620);
+ charmap->clear(0x065f);
+ charmap->ClearRange(0x06ee, 0x06ef);
+ charmap->clear(0x06ff);
+ }
+ }
+
+ mHasCmapTable = NS_SUCCEEDED(rv);
+ if (mHasCmapTable) {
+ gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+ mCharacterMap = pfl->FindCharMap(charmap);
+ } else {
+ // if error occurred, initialize to null cmap
+ mCharacterMap = new gfxCharacterMap();
+ }
+
+ LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
+ NS_ConvertUTF16toUTF8(mName).get(),
+ charmap->SizeOfIncludingThis(moz_malloc_size_of),
+ charmap->mHash, mCharacterMap == charmap ? " new" : ""));
+ if (LOG_CMAPDATA_ENABLED()) {
+ char prefix[256];
+ SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+ NS_ConvertUTF16toUTF8(mName).get());
+ charmap->Dump(prefix, eGfxLog_cmapdata);
+ }
+
+ return rv;
+}
+
+gfxFont*
+MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
+{
+ return new gfxMacFont(this, aFontStyle, aNeedsBold);
+}
+
+bool
+MacOSFontEntry::IsCFF()
+{
+ if (!mIsCFFInitialized) {
+ mIsCFFInitialized = true;
+ mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
+ }
+
+ return mIsCFF;
+}
+
+MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
+ int32_t aWeight,
+ bool aIsStandardFace,
+ double aSizeHint)
+ : gfxFontEntry(aPostscriptName, aIsStandardFace),
+ mFontRef(NULL),
+ mSizeHint(aSizeHint),
+ mFontRefInitialized(false),
+ mRequiresAAT(false),
+ mIsCFF(false),
+ mIsCFFInitialized(false)
+{
+ mWeight = aWeight;
+}
+
+MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
+ CGFontRef aFontRef,
+ uint16_t aWeight, uint16_t aStretch,
+ uint8_t aStyle,
+ bool aIsDataUserFont,
+ bool aIsLocalUserFont)
+ : gfxFontEntry(aPostscriptName, false),
+ mFontRef(NULL),
+ mSizeHint(0.0),
+ mFontRefInitialized(false),
+ mRequiresAAT(false),
+ mIsCFF(false),
+ mIsCFFInitialized(false)
+{
+ mFontRef = aFontRef;
+ mFontRefInitialized = true;
+ ::CFRetain(mFontRef);
+
+ mWeight = aWeight;
+ mStretch = aStretch;
+ mFixedPitch = false; // xxx - do we need this for downloaded fonts?
+ mStyle = aStyle;
+
+ NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
+ "userfont is either a data font or a local font");
+ mIsDataUserFont = aIsDataUserFont;
+ mIsLocalUserFont = aIsLocalUserFont;
+}
+
+CGFontRef
+MacOSFontEntry::GetFontRef()
+{
+ if (!mFontRefInitialized) {
+ mFontRefInitialized = true;
+ NSString *psname = GetNSStringForString(mName);
+ mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
+ if (!mFontRef) {
+ // This happens on macOS 10.12 for font entry names that start with
+ // .AppleSystemUIFont. For those fonts, we need to go through NSFont
+ // to get the correct CGFontRef.
+ // Both the Text and the Display variant of the display font use
+ // .AppleSystemUIFontSomethingSomething as their member names.
+ // That's why we're carrying along mSizeHint to this place so that
+ // we get the variant that we want for this family.
+ NSFont* font = [NSFont fontWithName:psname size:mSizeHint];
+ if (font) {
+ mFontRef = CTFontCopyGraphicsFont((CTFontRef)font, nullptr);
+ }
+ }
+ }
+ return mFontRef;
+}
+
+// For a logging build, we wrap the CFDataRef in a FontTableRec so that we can
+// use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging
+// does not get this overhead.
+class FontTableRec {
+public:
+ explicit FontTableRec(CFDataRef aDataRef)
+ : mDataRef(aDataRef)
+ {
+ MOZ_COUNT_CTOR(FontTableRec);
+ }
+
+ ~FontTableRec() {
+ MOZ_COUNT_DTOR(FontTableRec);
+ ::CFRelease(mDataRef);
+ }
+
+private:
+ CFDataRef mDataRef;
+};
+
+/*static*/ void
+MacOSFontEntry::DestroyBlobFunc(void* aUserData)
+{
+#ifdef NS_BUILD_REFCNT_LOGGING
+ FontTableRec *ftr = static_cast<FontTableRec*>(aUserData);
+ delete ftr;
+#else
+ ::CFRelease((CFDataRef)aUserData);
+#endif
+}
+
+hb_blob_t *
+MacOSFontEntry::GetFontTable(uint32_t aTag)
+{
+ CGFontRef fontRef = GetFontRef();
+ if (!fontRef) {
+ return nullptr;
+ }
+
+ CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
+ if (dataRef) {
+ return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
+ ::CFDataGetLength(dataRef),
+ HB_MEMORY_MODE_READONLY,
+#ifdef NS_BUILD_REFCNT_LOGGING
+ new FontTableRec(dataRef),
+#else
+ (void*)dataRef,
+#endif
+ DestroyBlobFunc);
+ }
+
+ return nullptr;
+}
+
+bool
+MacOSFontEntry::HasFontTable(uint32_t aTableTag)
+{
+ if (mAvailableTables.Count() == 0) {
+ nsAutoreleasePool localPool;
+
+ CGFontRef fontRef = GetFontRef();
+ if (!fontRef) {
+ return false;
+ }
+ CFArrayRef tags = ::CGFontCopyTableTags(fontRef);
+ if (!tags) {
+ return false;
+ }
+ int numTags = (int) ::CFArrayGetCount(tags);
+ for (int t = 0; t < numTags; t++) {
+ uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t);
+ mAvailableTables.PutEntry(tag);
+ }
+ ::CFRelease(tags);
+ }
+
+ return mAvailableTables.GetEntry(aTableTag);
+}
+
+void
+MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+/* gfxMacFontFamily */
+#pragma mark-
+
+class gfxMacFontFamily : public gfxFontFamily
+{
+public:
+ explicit gfxMacFontFamily(nsAString& aName, double aSizeHint) :
+ gfxFontFamily(aName),
+ mSizeHint(aSizeHint)
+ {}
+
+ virtual ~gfxMacFontFamily() {}
+
+ virtual void LocalizedName(nsAString& aLocalizedName);
+
+ virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
+
+protected:
+ double mSizeHint;
+};
+
+void
+gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
+{
+ nsAutoreleasePool localPool;
+
+ if (!HasOtherFamilyNames()) {
+ aLocalizedName = mName;
+ return;
+ }
+
+ NSString *family = GetNSStringForString(mName);
+ NSString *localized = [sFontManager
+ localizedNameForFamily:family
+ face:nil];
+
+ if (localized) {
+ GetStringForNSString(localized, aLocalizedName);
+ return;
+ }
+
+ // failed to get localized name, just use the canonical one
+ aLocalizedName = mName;
+}
+
+// Return the CSS weight value to use for the given face, overriding what
+// AppKit gives us (used to adjust families with bad weight values, see
+// bug 931426).
+// A return value of 0 indicates no override - use the existing weight.
+static inline int
+GetWeightOverride(const nsAString& aPSName)
+{
+ nsAutoCString prefName("font.weight-override.");
+ // The PostScript name is required to be ASCII; if it's not, the font is
+ // broken anyway, so we really don't care that this is lossy.
+ LossyAppendUTF16toASCII(aPSName, prefName);
+ return Preferences::GetInt(prefName.get(), 0);
+}
+
+void
+gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
+{
+ if (mHasStyles)
+ return;
+
+ nsAutoreleasePool localPool;
+
+ NSString *family = GetNSStringForString(mName);
+
+ // create a font entry for each face
+ NSArray *fontfaces = [sFontManager
+ availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api
+ int faceCount = [fontfaces count];
+ int faceIndex;
+
+ for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
+ NSArray *face = [fontfaces objectAtIndex:faceIndex];
+ NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
+ int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
+ uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
+ NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
+ bool isStandardFace = false;
+
+ if (appKitWeight == kAppleExtraLightWeight) {
+ // if the facename contains UltraLight, set the weight to the ultralight weight value
+ NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
+ if (range.location != NSNotFound) {
+ appKitWeight = kAppleUltraLightWeight;
+ }
+ }
+
+ // make a nsString
+ nsAutoString postscriptFontName;
+ GetStringForNSString(psname, postscriptFontName);
+
+ int32_t cssWeight = GetWeightOverride(postscriptFontName);
+ if (cssWeight) {
+ // scale down and clamp, to get a value from 1..9
+ cssWeight = ((cssWeight + 50) / 100);
+ cssWeight = std::max(1, std::min(cssWeight, 9));
+ } else {
+ cssWeight =
+ gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
+ }
+ cssWeight *= 100; // scale up to CSS values
+
+ if ([facename isEqualToString:@"Regular"] ||
+ [facename isEqualToString:@"Bold"] ||
+ [facename isEqualToString:@"Italic"] ||
+ [facename isEqualToString:@"Oblique"] ||
+ [facename isEqualToString:@"Bold Italic"] ||
+ [facename isEqualToString:@"Bold Oblique"])
+ {
+ isStandardFace = true;
+ }
+
+ // create a font entry
+ MacOSFontEntry *fontEntry =
+ new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace, mSizeHint);
+ if (!fontEntry) {
+ break;
+ }
+
+ // set additional properties based on the traits reported by Cocoa
+ if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
+ fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
+ } else if (macTraits & NSExpandedFontMask) {
+ fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
+ }
+ // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
+ // at least (see bug 611855), so check for style name endings as well
+ if ((macTraits & NSItalicFontMask) ||
+ [facename hasSuffix:@"Italic"] ||
+ [facename hasSuffix:@"Oblique"])
+ {
+ fontEntry->mStyle = NS_FONT_STYLE_ITALIC;
+ }
+ if (macTraits & NSFixedPitchFontMask) {
+ fontEntry->mFixedPitch = true;
+ }
+
+ if (LOG_FONTLIST_ENABLED()) {
+ LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
+ " with style: %s weight: %d stretch: %d"
+ " (apple-weight: %d macTraits: %8.8x)",
+ NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(Name()).get(),
+ fontEntry->IsItalic() ? "italic" : "normal",
+ cssWeight, fontEntry->Stretch(),
+ appKitWeight, macTraits));
+ }
+
+ // insert into font entry array of family
+ AddFontEntry(fontEntry);
+ }
+
+ SortAvailableFonts();
+ SetHasStyles(true);
+
+ if (mIsBadUnderlineFamily) {
+ SetBadUnderlineFonts();
+ }
+}
+
+/* gfxSingleFaceMacFontFamily */
+#pragma mark-
+
+class gfxSingleFaceMacFontFamily : public gfxFontFamily
+{
+public:
+ explicit gfxSingleFaceMacFontFamily(nsAString& aName) :
+ gfxFontFamily(aName)
+ {
+ mFaceNamesInitialized = true; // omit from face name lists
+ }
+
+ virtual ~gfxSingleFaceMacFontFamily() {}
+
+ virtual void LocalizedName(nsAString& aLocalizedName);
+
+ virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
+};
+
+void
+gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
+{
+ nsAutoreleasePool localPool;
+
+ if (!HasOtherFamilyNames()) {
+ aLocalizedName = mName;
+ return;
+ }
+
+ gfxFontEntry *fe = mAvailableFonts[0];
+ NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
+ size:0.0];
+ if (font) {
+ NSString *localized = [font displayName];
+ if (localized) {
+ GetStringForNSString(localized, aLocalizedName);
+ return;
+ }
+ }
+
+ // failed to get localized name, just use the canonical one
+ aLocalizedName = mName;
+}
+
+void
+gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
+{
+ if (mOtherFamilyNamesInitialized) {
+ return;
+ }
+
+ gfxFontEntry *fe = mAvailableFonts[0];
+ if (!fe) {
+ return;
+ }
+
+ const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
+
+ gfxFontEntry::AutoTable nameTable(fe, kNAME);
+ if (!nameTable) {
+ return;
+ }
+
+ mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
+ nameTable,
+ true);
+
+ mOtherFamilyNamesInitialized = true;
+}
+
+/* gfxMacPlatformFontList */
+#pragma mark-
+
+gfxMacPlatformFontList::gfxMacPlatformFontList() :
+ gfxPlatformFontList(false),
+ mDefaultFont(nullptr),
+ mUseSizeSensitiveSystemFont(false)
+{
+#ifdef MOZ_BUNDLED_FONTS
+ ActivateBundledFonts();
+#endif
+
+ ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
+ this,
+ RegisteredFontsChangedNotificationCallback,
+ kCTFontManagerRegisteredFontsChangedNotification,
+ 0,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+
+ // cache this in a static variable so that MacOSFontFamily objects
+ // don't have to repeatedly look it up
+ sFontManager = [NSFontManager sharedFontManager];
+}
+
+gfxMacPlatformFontList::~gfxMacPlatformFontList()
+{
+ if (mDefaultFont) {
+ ::CFRelease(mDefaultFont);
+ }
+}
+
+void
+gfxMacPlatformFontList::AddFamily(CFStringRef aFamily)
+{
+ NSString* family = (NSString*)aFamily;
+
+ // CTFontManager includes weird internal family names and
+ // LastResort, skip over those
+ if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame) {
+ return;
+ }
+
+ bool hiddenSystemFont = [family hasPrefix:@"."];
+
+ FontFamilyTable& table =
+ hiddenSystemFont ? mSystemFontFamilies : mFontFamilies;
+
+ nsAutoString familyName;
+ nsCocoaUtils::GetStringForNSString(family, familyName);
+
+ double sizeHint = 0.0;
+ if (hiddenSystemFont && mUseSizeSensitiveSystemFont &&
+ mSystemDisplayFontFamilyName.Equals(familyName)) {
+ sizeHint = 128.0;
+ }
+
+ nsAutoString key;
+ ToLowerCase(familyName, key);
+
+ RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(familyName, sizeHint);
+ table.Put(key, familyEntry);
+
+ // check the bad underline blacklist
+ if (mBadUnderlineFamilyNames.Contains(key)) {
+ familyEntry->SetBadUnderlineFamily();
+ }
+}
+
+nsresult
+gfxMacPlatformFontList::InitFontListForPlatform()
+{
+ nsAutoreleasePool localPool;
+
+ // reset system font list
+ mSystemFontFamilies.Clear();
+
+ // iterate over available families
+
+ InitSystemFontNames();
+
+ CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
+
+ for (NSString* familyName in (NSArray*)familyNames) {
+ AddFamily((CFStringRef)familyName);
+ }
+
+ CFRelease(familyNames);
+
+ InitSingleFaceList();
+
+ // to avoid full search of font name tables, seed the other names table with localized names from
+ // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause
+ // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
+ PreloadNamesList();
+
+ // start the delayed cmap loader
+ GetPrefsAndStartLoader();
+
+ return NS_OK;
+}
+
+void
+gfxMacPlatformFontList::InitSingleFaceList()
+{
+ AutoTArray<nsString, 10> singleFaceFonts;
+ gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
+
+ for (const auto& singleFaceFamily : singleFaceFonts) {
+ LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
+ NS_ConvertUTF16toUTF8(singleFaceFamily).get()));
+ // Each entry in the "single face families" list is expected to be a
+ // colon-separated pair of FaceName:Family,
+ // where FaceName is the individual face name (psname) of a font
+ // that should be exposed as a separate family name,
+ // and Family is the standard family to which that face belongs.
+ // The only such face listed by default is
+ // Osaka-Mono:Osaka
+ nsAutoString familyName(singleFaceFamily);
+ auto colon = familyName.FindChar(':');
+ if (colon == kNotFound) {
+ continue;
+ }
+
+ // Look for the parent family in the main font family list,
+ // and ensure we have loaded its list of available faces.
+ nsAutoString key(Substring(familyName, colon + 1));
+ ToLowerCase(key);
+ gfxFontFamily* family = mFontFamilies.GetWeak(key);
+ if (!family) {
+ continue;
+ }
+ family->FindStyleVariations();
+
+ // Truncate the entry from prefs at the colon, so now it is just the
+ // desired single-face-family name.
+ familyName.Truncate(colon);
+
+ // Look through the family's faces to see if this one is present.
+ const gfxFontEntry* fe = nullptr;
+ for (const auto& face : family->GetFontList()) {
+ if (face->Name().Equals(familyName)) {
+ fe = face;
+ break;
+ }
+ }
+ if (!fe) {
+ continue;
+ }
+
+ // We found the correct face, so create the single-face family record.
+ GenerateFontListKey(familyName, key);
+ LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
+ NS_ConvertUTF16toUTF8(familyName).get(),
+ NS_ConvertUTF16toUTF8(key).get()));
+
+ // add only if doesn't exist already
+ if (!mFontFamilies.GetWeak(key)) {
+ RefPtr<gfxFontFamily> familyEntry =
+ new gfxSingleFaceMacFontFamily(familyName);
+ // We need a separate font entry, because its family name will
+ // differ from the one we found in the main list.
+ MacOSFontEntry* fontEntry =
+ new MacOSFontEntry(fe->Name(), fe->mWeight, true,
+ static_cast<const MacOSFontEntry*>(fe)->
+ mSizeHint);
+ familyEntry->AddFontEntry(fontEntry);
+ familyEntry->SetHasStyles(true);
+ mFontFamilies.Put(key, familyEntry);
+ LOG_FONTLIST(("(fontlist-singleface) added new family\n",
+ NS_ConvertUTF16toUTF8(familyName).get(),
+ NS_ConvertUTF16toUTF8(key).get()));
+ }
+ }
+}
+
+// System fonts under OSX may contain weird "meta" names but if we create
+// a new font using just the Postscript name, the NSFont api returns an object
+// with the actual real family name. For example, under OSX 10.11:
+//
+// [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont
+// [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName]
+// size:8.0] familyName] ==> .SF NS Text
+
+static NSString* GetRealFamilyName(NSFont* aFont)
+{
+ NSFont* f = [NSFont fontWithName: [[aFont fontDescriptor] postscriptName]
+ size: 0.0];
+ return [f familyName];
+}
+
+// System fonts under OSX 10.11 use a combination of two families, one
+// for text sizes and another for larger, display sizes. Each has a
+// different number of weights. There aren't efficient API's for looking
+// this information up, so hard code the logic here but confirm via
+// debug assertions that the logic is correct.
+
+const CGFloat kTextDisplayCrossover = 20.0; // use text family below this size
+
+void
+gfxMacPlatformFontList::InitSystemFontNames()
+{
+ // system font under 10.11 are two distinct families for text/display sizes
+ if (nsCocoaFeatures::OnElCapitanOrLater()) {
+ mUseSizeSensitiveSystemFont = true;
+ }
+
+ // text font family
+ NSFont* sys = [NSFont systemFontOfSize: 0.0];
+ NSString* textFamilyName = GetRealFamilyName(sys);
+ nsAutoString familyName;
+ nsCocoaUtils::GetStringForNSString(textFamilyName, familyName);
+ mSystemTextFontFamilyName = familyName;
+
+ // display font family, if on OSX 10.11
+ if (mUseSizeSensitiveSystemFont) {
+ NSFont* displaySys = [NSFont systemFontOfSize: 128.0];
+ NSString* displayFamilyName = GetRealFamilyName(displaySys);
+ nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName);
+ mSystemDisplayFontFamilyName = familyName;
+
+#if DEBUG
+ // confirm that the optical size switch is at 20.0
+ NS_ASSERTION([textFamilyName compare:displayFamilyName] != NSOrderedSame,
+ "system text/display fonts are the same!");
+ NSString* fam19 = GetRealFamilyName([NSFont systemFontOfSize:
+ (kTextDisplayCrossover - 1.0)]);
+ NSString* fam20 = GetRealFamilyName([NSFont systemFontOfSize:
+ kTextDisplayCrossover]);
+ NS_ASSERTION(fam19 && fam20 && [fam19 compare:fam20] != NSOrderedSame,
+ "system text/display font size switch point is not as expected!");
+#endif
+ }
+
+#ifdef DEBUG
+ // different system font API's always map to the same family under OSX, so
+ // just assume that and emit a warning if that ever changes
+ NSString *sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]);
+ if ([sysFamily compare:GetRealFamilyName([NSFont boldSystemFontOfSize:0.0])] != NSOrderedSame ||
+ [sysFamily compare:GetRealFamilyName([NSFont controlContentFontOfSize:0.0])] != NSOrderedSame ||
+ [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] != NSOrderedSame ||
+ [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] != NSOrderedSame) {
+ NS_WARNING("system font types map to different font families"
+ " -- please log a bug!!");
+ }
+#endif
+}
+
+gfxFontFamily*
+gfxMacPlatformFontList::FindSystemFontFamily(const nsAString& aFamily)
+{
+ nsAutoString key;
+ GenerateFontListKey(aFamily, key);
+
+ gfxFontFamily* familyEntry;
+
+ // lookup in hidden system family name list
+ if ((familyEntry = mSystemFontFamilies.GetWeak(key))) {
+ return CheckFamily(familyEntry);
+ }
+
+ // lookup in user-exposed family name list
+ if ((familyEntry = mFontFamilies.GetWeak(key))) {
+ return CheckFamily(familyEntry);
+ }
+
+ return nullptr;
+}
+
+bool
+gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+ gfxFontFamily *family = FindFamily(aFontName);
+ if (family) {
+ family->LocalizedName(aFamilyName);
+ return true;
+ }
+
+ return false;
+}
+
+void
+gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
+ void *observer,
+ CFStringRef name,
+ const void *object,
+ CFDictionaryRef userInfo)
+{
+ if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
+ return;
+ }
+
+ gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
+
+ // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
+ fl->UpdateFontList();
+
+ // modify a preference that will trigger reflow everywhere
+ fl->ForceGlobalReflow();
+}
+
+gfxFontEntry*
+gfxMacPlatformFontList::PlatformGlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily)
+{
+ CFStringRef str;
+ UniChar ch[2];
+ CFIndex length = 1;
+
+ if (IS_IN_BMP(aCh)) {
+ ch[0] = aCh;
+ str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
+ kCFAllocatorNull);
+ } else {
+ ch[0] = H_SURROGATE(aCh);
+ ch[1] = L_SURROGATE(aCh);
+ str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
+ kCFAllocatorNull);
+ if (!str) {
+ return nullptr;
+ }
+ length = 2;
+ }
+
+ // use CoreText to find the fallback family
+
+ gfxFontEntry *fontEntry = nullptr;
+ CTFontRef fallback;
+ bool cantUseFallbackFont = false;
+
+ if (!mDefaultFont) {
+ mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f,
+ NULL);
+ }
+
+ fallback = ::CTFontCreateForString(mDefaultFont, str,
+ ::CFRangeMake(0, length));
+
+ if (fallback) {
+ CFStringRef familyNameRef = ::CTFontCopyFamilyName(fallback);
+ ::CFRelease(fallback);
+
+ if (familyNameRef &&
+ ::CFStringCompare(familyNameRef, CFSTR("LastResort"),
+ kCFCompareCaseInsensitive) != kCFCompareEqualTo)
+ {
+ AutoTArray<UniChar, 1024> buffer;
+ CFIndex familyNameLen = ::CFStringGetLength(familyNameRef);
+ buffer.SetLength(familyNameLen+1);
+ ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen),
+ buffer.Elements());
+ buffer[familyNameLen] = 0;
+ nsDependentString familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()), familyNameLen);
+
+ bool needsBold; // ignored in the system fallback case
+
+ gfxFontFamily *family = FindFamily(familyNameString);
+ if (family) {
+ fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
+ if (fontEntry) {
+ if (fontEntry->HasCharacter(aCh)) {
+ *aMatchedFamily = family;
+ } else {
+ fontEntry = nullptr;
+ cantUseFallbackFont = true;
+ }
+ }
+ }
+ }
+
+ if (familyNameRef) {
+ ::CFRelease(familyNameRef);
+ }
+ }
+
+ ::CFRelease(str);
+
+ return fontEntry;
+}
+
+gfxFontFamily*
+gfxMacPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
+{
+ nsAutoreleasePool localPool;
+
+ NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
+ nsAutoString familyName;
+
+ GetStringForNSString(defaultFamily, familyName);
+ return FindFamily(familyName);
+}
+
+int32_t
+gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight)
+{
+ if (aAppleWeight < 1)
+ aAppleWeight = 1;
+ else if (aAppleWeight > kAppleMaxWeight)
+ aAppleWeight = kAppleMaxWeight;
+ return gAppleWeightToCSSWeight[aAppleWeight];
+}
+
+gfxFontEntry*
+gfxMacPlatformFontList::LookupLocalFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ nsAutoreleasePool localPool;
+
+ NSString *faceName = GetNSStringForString(aFontName);
+ MacOSFontEntry *newFontEntry;
+
+ // lookup face based on postscript or full name
+ CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
+ if (!fontRef) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(aWeight >= 100 && aWeight <= 900,
+ "bogus font weight value!");
+
+ newFontEntry =
+ new MacOSFontEntry(aFontName, fontRef, aWeight, aStretch, aStyle,
+ false, true);
+ ::CFRelease(fontRef);
+
+ return newFontEntry;
+}
+
+static void ReleaseData(void *info, const void *data, size_t size)
+{
+ free((void*)data);
+}
+
+gfxFontEntry*
+gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
+
+ NS_ASSERTION(aWeight >= 100 && aWeight <= 900, "bogus font weight value!");
+
+ // create the font entry
+ nsAutoString uniqueName;
+
+ nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ CGDataProviderRef provider =
+ ::CGDataProviderCreateWithData(nullptr, aFontData, aLength,
+ &ReleaseData);
+ CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
+ ::CGDataProviderRelease(provider);
+
+ if (!fontRef) {
+ return nullptr;
+ }
+
+ auto newFontEntry =
+ MakeUnique<MacOSFontEntry>(uniqueName, fontRef, aWeight, aStretch,
+ aStyle, true, false);
+ ::CFRelease(fontRef);
+
+ // if succeeded and font cmap is good, return the new font
+ if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
+ return newFontEntry.release();
+ }
+
+ // if something is funky about this font, delete immediately
+
+#if DEBUG
+ NS_WARNING("downloaded font not loaded properly");
+#endif
+
+ return nullptr;
+}
+
+// Webkit code uses a system font meta name, so mimic that here
+// WebCore/platform/graphics/mac/FontCacheMac.mm
+static const char kSystemFont_system[] = "-apple-system";
+
+bool
+gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle,
+ gfxFloat aDevToCssSize)
+{
+ // search for special system font name, -apple-system
+ if (aFamily.EqualsLiteral(kSystemFont_system)) {
+ if (mUseSizeSensitiveSystemFont &&
+ aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
+ aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName));
+ return true;
+ }
+ aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName));
+ return true;
+ }
+
+ return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+ aDevToCssSize);
+}
+
+void
+gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
+ nsAString& aSystemFontName,
+ gfxFontStyle &aFontStyle,
+ float aDevPixPerCSSPixel)
+{
+ // code moved here from widget/cocoa/nsLookAndFeel.mm
+ NSFont *font = nullptr;
+ char* systemFontName = nullptr;
+ switch (aSystemFontID) {
+ case LookAndFeel::eFont_MessageBox:
+ case LookAndFeel::eFont_StatusBar:
+ case LookAndFeel::eFont_List:
+ case LookAndFeel::eFont_Field:
+ case LookAndFeel::eFont_Button:
+ case LookAndFeel::eFont_Widget:
+ font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
+ systemFontName = (char*) kSystemFont_system;
+ break;
+
+ case LookAndFeel::eFont_SmallCaption:
+ font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
+ systemFontName = (char*) kSystemFont_system;
+ break;
+
+ case LookAndFeel::eFont_Icon: // used in urlbar; tried labelFont, but too small
+ case LookAndFeel::eFont_Workspace:
+ case LookAndFeel::eFont_Desktop:
+ case LookAndFeel::eFont_Info:
+ font = [NSFont controlContentFontOfSize:0.0];
+ systemFontName = (char*) kSystemFont_system;
+ break;
+
+ case LookAndFeel::eFont_PullDownMenu:
+ font = [NSFont menuBarFontOfSize:0.0];
+ systemFontName = (char*) kSystemFont_system;
+ break;
+
+ case LookAndFeel::eFont_Tooltips:
+ font = [NSFont toolTipsFontOfSize:0.0];
+ systemFontName = (char*) kSystemFont_system;
+ break;
+
+ case LookAndFeel::eFont_Caption:
+ case LookAndFeel::eFont_Menu:
+ case LookAndFeel::eFont_Dialog:
+ default:
+ font = [NSFont systemFontOfSize:0.0];
+ systemFontName = (char*) kSystemFont_system;
+ break;
+ }
+ NS_ASSERTION(font, "system font not set");
+ NS_ASSERTION(systemFontName, "system font name not set");
+
+ if (systemFontName) {
+ aSystemFontName.AssignASCII(systemFontName);
+ }
+
+ NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits];
+ aFontStyle.style =
+ (traits & NSFontItalicTrait) ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL;
+ aFontStyle.weight =
+ (traits & NSFontBoldTrait) ? NS_FONT_WEIGHT_BOLD : NS_FONT_WEIGHT_NORMAL;
+ aFontStyle.stretch =
+ (traits & NSFontExpandedTrait) ?
+ NS_FONT_STRETCH_EXPANDED : (traits & NSFontCondensedTrait) ?
+ NS_FONT_STRETCH_CONDENSED : NS_FONT_STRETCH_NORMAL;
+ // convert size from css pixels to device pixels
+ aFontStyle.size = [font pointSize] * aDevPixPerCSSPixel;
+ aFontStyle.systemFont = true;
+}
+
+// used to load system-wide font info on off-main thread
+class MacFontInfo : public FontInfoData {
+public:
+ MacFontInfo(bool aLoadOtherNames,
+ bool aLoadFaceNames,
+ bool aLoadCmaps) :
+ FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
+ {}
+
+ virtual ~MacFontInfo() {}
+
+ virtual void Load() {
+ nsAutoreleasePool localPool;
+ FontInfoData::Load();
+ }
+
+ // loads font data for all members of a given family
+ virtual void LoadFontFamilyData(const nsAString& aFamilyName);
+};
+
+void
+MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
+{
+ // family name ==> CTFontDescriptor
+ NSString *famName = GetNSStringForString(aFamilyName);
+ CFStringRef family = CFStringRef(famName);
+
+ CFMutableDictionaryRef attr =
+ CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
+ CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
+ CFRelease(attr);
+ CFArrayRef matchingFonts =
+ CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
+ CFRelease(fd);
+ if (!matchingFonts) {
+ return;
+ }
+
+ nsTArray<nsString> otherFamilyNames;
+ bool hasOtherFamilyNames = true;
+
+ // iterate over faces in the family
+ int f, numFaces = (int) CFArrayGetCount(matchingFonts);
+ for (f = 0; f < numFaces; f++) {
+ mLoadStats.fonts++;
+
+ CTFontDescriptorRef faceDesc =
+ (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
+ if (!faceDesc) {
+ continue;
+ }
+ CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
+ 0.0, nullptr);
+ if (!fontRef) {
+ NS_WARNING("failed to create a CTFontRef");
+ continue;
+ }
+
+ if (mLoadCmaps) {
+ // face name
+ CFStringRef faceName = (CFStringRef)
+ CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
+
+ AutoTArray<UniChar, 1024> buffer;
+ CFIndex len = CFStringGetLength(faceName);
+ buffer.SetLength(len+1);
+ CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
+ buffer.Elements());
+ buffer[len] = 0;
+ nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
+ len);
+
+ // load the cmap data
+ FontFaceData fontData;
+ CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
+ kCTFontTableOptionNoOptions);
+
+ if (cmapTable) {
+ const uint8_t *cmapData =
+ (const uint8_t*)CFDataGetBytePtr(cmapTable);
+ uint32_t cmapLen = CFDataGetLength(cmapTable);
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ uint32_t offset;
+ bool unicodeFont = false; // ignored
+ bool symbolFont = false;
+ nsresult rv;
+
+ rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
+ unicodeFont, symbolFont);
+ if (NS_SUCCEEDED(rv)) {
+ fontData.mCharacterMap = charmap;
+ fontData.mUVSOffset = offset;
+ fontData.mSymbolFont = symbolFont;
+ mLoadStats.cmaps++;
+ }
+ CFRelease(cmapTable);
+ }
+
+ mFontFaceData.Put(fontName, fontData);
+ CFRelease(faceName);
+ }
+
+ if (mLoadOtherNames && hasOtherFamilyNames) {
+ CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
+ kCTFontTableOptionNoOptions);
+
+ if (nameTable) {
+ const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
+ uint32_t nameLen = CFDataGetLength(nameTable);
+ gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
+ nameData, nameLen,
+ otherFamilyNames,
+ false);
+ hasOtherFamilyNames = otherFamilyNames.Length() != 0;
+ CFRelease(nameTable);
+ }
+ }
+
+ CFRelease(fontRef);
+ }
+ CFRelease(matchingFonts);
+
+ // if found other names, insert them in the hash table
+ if (otherFamilyNames.Length() != 0) {
+ mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
+ mLoadStats.othernames += otherFamilyNames.Length();
+ }
+}
+
+already_AddRefed<FontInfoData>
+gfxMacPlatformFontList::CreateFontInfoData()
+{
+ bool loadCmaps = !UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+ RefPtr<MacFontInfo> fi =
+ new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
+ return fi.forget();
+}
+
+#ifdef MOZ_BUNDLED_FONTS
+
+void
+gfxMacPlatformFontList::ActivateBundledFonts()
+{
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+ return;
+ }
+ bool isDir;
+ if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ bool hasMore;
+ while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> entry;
+ if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) {
+ break;
+ }
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+ if (!file) {
+ continue;
+ }
+ nsCString path;
+ if (NS_FAILED(file->GetNativePath(path))) {
+ continue;
+ }
+ CFURLRef fontURL =
+ ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (uint8_t*)path.get(),
+ path.Length(),
+ false);
+ if (fontURL) {
+ CFErrorRef error = nullptr;
+ ::CTFontManagerRegisterFontsForURL(fontURL,
+ kCTFontManagerScopeProcess,
+ &error);
+ ::CFRelease(fontURL);
+ }
+ }
+}
+
+#endif
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
index 21e15bf06f..7ec9139c0a 100644
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -37,6 +37,9 @@
#if defined(XP_WIN)
#include "gfxWindowsPlatform.h"
+#elif defined(XP_MACOSX)
+#include "gfxPlatformMac.h"
+#include "gfxQuartzSurface.h"
#elif defined(MOZ_WIDGET_GTK)
#include "gfxPlatformGtk.h"
#elif defined(ANDROID)
@@ -571,6 +574,8 @@ gfxPlatform::Init()
#if defined(XP_WIN)
gPlatform = new gfxWindowsPlatform;
+#elif defined(XP_MACOSX)
+ gPlatform = new gfxPlatformMac;
#elif defined(MOZ_WIDGET_GTK)
gPlatform = new gfxPlatformGtk;
#elif defined(ANDROID)
@@ -2127,7 +2132,7 @@ bool
gfxPlatform::AccelerateLayersByDefault()
{
// Note: add any new platform defines here that should get HWA by default.
-#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_UIKIT)
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_UIKIT)
return true;
#elif defined(MOZ_GL_PROVIDER)
// GL provider manually declared
diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp
new file mode 100644
index 0000000000..75c5236a87
--- /dev/null
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -0,0 +1,617 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxPlatformMac.h"
+
+#include "gfxQuartzSurface.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/MacIOSurface.h"
+
+#include "gfxMacPlatformFontList.h"
+#include "gfxMacFont.h"
+#include "gfxCoreTextShaper.h"
+#include "gfxTextRun.h"
+#include "gfxUserFontSet.h"
+
+#include "nsTArray.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "nsUnicodeProperties.h"
+#include "qcms.h"
+#include "gfx2DGlue.h"
+
+#include <dlfcn.h>
+#include <CoreVideo/CoreVideo.h>
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "VsyncSource.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::unicode;
+
+// cribbed from CTFontManager.h
+enum {
+ kAutoActivationDisabled = 1
+};
+typedef uint32_t AutoActivationSetting;
+
+// bug 567552 - disable auto-activation of fonts
+
+static void
+DisableFontActivation()
+{
+ // get the main bundle identifier
+ CFBundleRef mainBundle = ::CFBundleGetMainBundle();
+ CFStringRef mainBundleID = nullptr;
+
+ if (mainBundle) {
+ mainBundleID = ::CFBundleGetIdentifier(mainBundle);
+ }
+
+ // bug 969388 and bug 922590 - mainBundlID as null is sometimes problematic
+ if (!mainBundleID) {
+ NS_WARNING("missing bundle ID, packaging set up incorrectly");
+ return;
+ }
+
+ // if possible, fetch CTFontManagerSetAutoActivationSetting
+ void (*CTFontManagerSetAutoActivationSettingPtr)
+ (CFStringRef, AutoActivationSetting);
+ CTFontManagerSetAutoActivationSettingPtr =
+ (void (*)(CFStringRef, AutoActivationSetting))
+ dlsym(RTLD_DEFAULT, "CTFontManagerSetAutoActivationSetting");
+
+ // bug 567552 - disable auto-activation of fonts
+ if (CTFontManagerSetAutoActivationSettingPtr) {
+ CTFontManagerSetAutoActivationSettingPtr(mainBundleID,
+ kAutoActivationDisabled);
+ }
+}
+
+gfxPlatformMac::gfxPlatformMac()
+{
+ DisableFontActivation();
+ mFontAntiAliasingThreshold = ReadAntiAliasingThreshold();
+
+ uint32_t canvasMask = BackendTypeBit(BackendType::SKIA);
+ uint32_t contentMask = BackendTypeBit(BackendType::SKIA);
+ InitBackendPrefs(canvasMask, BackendType::SKIA,
+ contentMask, BackendType::SKIA);
+
+ // XXX: Bug 1036682 - we run out of fds on Mac when using tiled layers because
+ // with 256x256 tiles we can easily hit the soft limit of 800 when using double
+ // buffered tiles in e10s, so let's bump the soft limit to the hard limit for the OS
+ // up to a new cap of OPEN_MAX.
+ struct rlimit limits;
+ if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
+ limits.rlim_cur = std::min(rlim_t(OPEN_MAX), limits.rlim_max);
+ if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
+ NS_WARNING("Unable to bump RLIMIT_NOFILE to the maximum number on this OS");
+ }
+ }
+
+ MacIOSurfaceLib::LoadLibrary();
+}
+
+gfxPlatformMac::~gfxPlatformMac()
+{
+ gfxCoreTextShaper::Shutdown();
+}
+
+gfxPlatformFontList*
+gfxPlatformMac::CreatePlatformFontList()
+{
+ gfxPlatformFontList* list = new gfxMacPlatformFontList();
+ if (NS_SUCCEEDED(list->InitFontList())) {
+ return list;
+ }
+ gfxPlatformFontList::Shutdown();
+ return nullptr;
+}
+
+already_AddRefed<gfxASurface>
+gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat)
+{
+ if (!Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> newSurface =
+ new gfxQuartzSurface(aSize, aFormat);
+ return newSurface.forget();
+}
+
+already_AddRefed<ScaledFont>
+gfxPlatformMac::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
+{
+ gfxMacFont *font = static_cast<gfxMacFont*>(aFont);
+ return font->GetScaledFont(aTarget);
+}
+
+gfxFontGroup *
+gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize)
+{
+ return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
+ aUserFontSet, aDevToCssSize);
+}
+
+bool
+gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
+{
+ // check for strange format flags
+ NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
+ "strange font format hint set");
+
+ // accept supported formats
+ if (aFormatFlags & (gfxUserFontSet::FLAG_FORMATS_COMMON |
+ gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT)) {
+ return true;
+ }
+
+ // reject all other formats, known and unknown
+ if (aFormatFlags != 0) {
+ return false;
+ }
+
+ // no format hint set, need to look at data
+ return true;
+}
+
+static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
+static const char kFontAppleBraille[] = "Apple Braille";
+static const char kFontAppleColorEmoji[] = "Apple Color Emoji";
+static const char kFontAppleSymbols[] = "Apple Symbols";
+static const char kFontDevanagariSangamMN[] = "Devanagari Sangam MN";
+static const char kFontEuphemiaUCAS[] = "Euphemia UCAS";
+static const char kFontGeneva[] = "Geneva";
+static const char kFontGeezaPro[] = "Geeza Pro";
+static const char kFontGujaratiSangamMN[] = "Gujarati Sangam MN";
+static const char kFontGurmukhiMN[] = "Gurmukhi MN";
+static const char kFontHiraginoKakuGothic[] = "Hiragino Kaku Gothic ProN";
+static const char kFontHiraginoSansGB[] = "Hiragino Sans GB";
+static const char kFontKefa[] = "Kefa";
+static const char kFontKhmerMN[] = "Khmer MN";
+static const char kFontLaoMN[] = "Lao MN";
+static const char kFontLucidaGrande[] = "Lucida Grande";
+static const char kFontMenlo[] = "Menlo";
+static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
+static const char kFontMingLiUExtB[] = "MingLiU-ExtB";
+static const char kFontMyanmarMN[] = "Myanmar MN";
+static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
+static const char kFontSimSunExtB[] = "SimSun-ExtB";
+static const char kFontSongtiSC[] = "Songti SC";
+static const char kFontSTHeiti[] = "STHeiti";
+static const char kFontSTIXGeneral[] = "STIXGeneral";
+static const char kFontTamilMN[] = "Tamil MN";
+
+void
+gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ nsTArray<const char*>& aFontList)
+{
+ EmojiPresentation emoji = GetEmojiPresentation(aCh);
+ if (emoji != EmojiPresentation::TextOnly) {
+ if (aNextCh == kVariationSelector16 ||
+ (aNextCh != kVariationSelector15 &&
+ emoji == EmojiPresentation::EmojiDefault)) {
+ // if char is followed by VS16, try for a color emoji glyph
+ aFontList.AppendElement(kFontAppleColorEmoji);
+ }
+ }
+
+ aFontList.AppendElement(kFontLucidaGrande);
+
+ if (!IS_IN_BMP(aCh)) {
+ uint32_t p = aCh >> 16;
+ if (p == 1) {
+ aFontList.AppendElement(kFontAppleSymbols);
+ aFontList.AppendElement(kFontSTIXGeneral);
+ aFontList.AppendElement(kFontGeneva);
+ } else if (p == 2) {
+ // OSX installations with MS Office may have these fonts
+ aFontList.AppendElement(kFontMingLiUExtB);
+ aFontList.AppendElement(kFontSimSunExtB);
+ }
+ } else {
+ uint32_t b = (aCh >> 8) & 0xff;
+
+ switch (b) {
+ case 0x03:
+ case 0x05:
+ aFontList.AppendElement(kFontGeneva);
+ break;
+ case 0x07:
+ aFontList.AppendElement(kFontGeezaPro);
+ break;
+ case 0x09:
+ aFontList.AppendElement(kFontDevanagariSangamMN);
+ break;
+ case 0x0a:
+ aFontList.AppendElement(kFontGurmukhiMN);
+ aFontList.AppendElement(kFontGujaratiSangamMN);
+ break;
+ case 0x0b:
+ aFontList.AppendElement(kFontTamilMN);
+ break;
+ case 0x0e:
+ aFontList.AppendElement(kFontLaoMN);
+ break;
+ case 0x0f:
+ aFontList.AppendElement(kFontSongtiSC);
+ break;
+ case 0x10:
+ aFontList.AppendElement(kFontMenlo);
+ aFontList.AppendElement(kFontMyanmarMN);
+ break;
+ case 0x13: // Cherokee
+ aFontList.AppendElement(kFontPlantagenetCherokee);
+ aFontList.AppendElement(kFontKefa);
+ break;
+ case 0x14: // Unified Canadian Aboriginal Syllabics
+ case 0x15:
+ case 0x16:
+ aFontList.AppendElement(kFontEuphemiaUCAS);
+ aFontList.AppendElement(kFontGeneva);
+ break;
+ case 0x18: // Mongolian, UCAS
+ aFontList.AppendElement(kFontSTHeiti);
+ aFontList.AppendElement(kFontEuphemiaUCAS);
+ break;
+ case 0x19: // Khmer
+ aFontList.AppendElement(kFontKhmerMN);
+ aFontList.AppendElement(kFontMicrosoftTaiLe);
+ break;
+ case 0x1d:
+ case 0x1e:
+ aFontList.AppendElement(kFontGeneva);
+ break;
+ case 0x20: // Symbol ranges
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2e:
+ aFontList.AppendElement(kFontHiraginoKakuGothic);
+ aFontList.AppendElement(kFontAppleSymbols);
+ aFontList.AppendElement(kFontMenlo);
+ aFontList.AppendElement(kFontSTIXGeneral);
+ aFontList.AppendElement(kFontGeneva);
+ aFontList.AppendElement(kFontAppleColorEmoji);
+ break;
+ case 0x2c:
+ aFontList.AppendElement(kFontGeneva);
+ break;
+ case 0x2d:
+ aFontList.AppendElement(kFontKefa);
+ aFontList.AppendElement(kFontGeneva);
+ break;
+ case 0x28: // Braille
+ aFontList.AppendElement(kFontAppleBraille);
+ break;
+ case 0x31:
+ aFontList.AppendElement(kFontHiraginoSansGB);
+ break;
+ case 0x4d:
+ aFontList.AppendElement(kFontAppleSymbols);
+ break;
+ case 0xa0: // Yi
+ case 0xa1:
+ case 0xa2:
+ case 0xa3:
+ case 0xa4:
+ aFontList.AppendElement(kFontSTHeiti);
+ break;
+ case 0xa6:
+ case 0xa7:
+ aFontList.AppendElement(kFontGeneva);
+ aFontList.AppendElement(kFontAppleSymbols);
+ break;
+ case 0xab:
+ aFontList.AppendElement(kFontKefa);
+ break;
+ case 0xfc:
+ case 0xff:
+ aFontList.AppendElement(kFontAppleSymbols);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Arial Unicode MS has lots of glyphs for obscure, use it as a last resort
+ aFontList.AppendElement(kFontArialUnicodeMS);
+}
+
+/*static*/ void
+gfxPlatformMac::LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
+ nsAString& aSystemFontName,
+ gfxFontStyle& aFontStyle,
+ float aDevPixPerCSSPixel)
+{
+ gfxMacPlatformFontList* pfl = gfxMacPlatformFontList::PlatformFontList();
+ return pfl->LookupSystemFont(aSystemFontID, aSystemFontName, aFontStyle,
+ aDevPixPerCSSPixel);
+}
+
+uint32_t
+gfxPlatformMac::ReadAntiAliasingThreshold()
+{
+ uint32_t threshold = 0; // default == no threshold
+
+ // first read prefs flag to determine whether to use the setting or not
+ bool useAntiAliasingThreshold = Preferences::GetBool("gfx.use_text_smoothing_setting", false);
+
+ // if the pref setting is disabled, return 0 which effectively disables this feature
+ if (!useAntiAliasingThreshold)
+ return threshold;
+
+ // value set via Appearance pref panel, "Turn off text smoothing for font sizes xxx and smaller"
+ CFNumberRef prefValue = (CFNumberRef)CFPreferencesCopyAppValue(CFSTR("AppleAntiAliasingThreshold"), kCFPreferencesCurrentApplication);
+
+ if (prefValue) {
+ if (!CFNumberGetValue(prefValue, kCFNumberIntType, &threshold)) {
+ threshold = 0;
+ }
+ CFRelease(prefValue);
+ }
+
+ return threshold;
+}
+
+// This is the renderer output callback function, called on the vsync thread
+static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
+ const CVTimeStamp* aNow,
+ const CVTimeStamp* aOutputTime,
+ CVOptionFlags aFlagsIn,
+ CVOptionFlags* aFlagsOut,
+ void* aDisplayLinkContext);
+
+class OSXVsyncSource final : public VsyncSource
+{
+public:
+ OSXVsyncSource()
+ {
+ }
+
+ virtual Display& GetGlobalDisplay() override
+ {
+ return mGlobalDisplay;
+ }
+
+ class OSXDisplay final : public VsyncSource::Display
+ {
+ public:
+ OSXDisplay()
+ : mDisplayLink(nullptr)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ }
+
+ ~OSXDisplay()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
+ MOZ_ASSERT(osxDisplay);
+ osxDisplay->EnableVsync();
+ }
+
+ virtual void EnableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (IsVsyncEnabled()) {
+ return;
+ }
+
+ // Create a display link capable of being used with all active displays
+ // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
+ // situations. According to the docs, it is compatible with all displays running on the computer
+ // But if we have different monitors at different display rates, we may hit issues.
+ if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
+ NS_WARNING("Could not create a display link with all active displays. Retrying");
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
+
+ // bug 1142708 - When coming back from sleep,
+ // or when changing displays, active displays may not be ready yet,
+ // even if listening for the kIOMessageSystemHasPoweredOn event
+ // from OS X sleep notifications.
+ // Active displays are those that are drawable.
+ // bug 1144638 - When changing display configurations and getting
+ // notifications from CGDisplayReconfigurationCallBack, the
+ // callback gets called twice for each active display
+ // so it's difficult to know when all displays are active.
+ // Instead, try again soon. The delay is arbitrary. 100ms chosen
+ // because on a late 2013 15" retina, it takes about that
+ // long to come back up from sleep.
+ uint32_t delay = 100;
+ mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT);
+ return;
+ }
+
+ if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
+ NS_WARNING("Could not set displaylink output callback");
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
+ return;
+ }
+
+ mPreviousTimestamp = TimeStamp::Now();
+ if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
+ NS_WARNING("Could not activate the display link");
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
+ }
+
+ CVTime vsyncRate = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(mDisplayLink);
+ if (vsyncRate.flags & kCVTimeIsIndefinite) {
+ NS_WARNING("Could not get vsync rate, setting to 60.");
+ mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
+ } else {
+ int64_t timeValue = vsyncRate.timeValue;
+ int64_t timeScale = vsyncRate.timeScale;
+ const int milliseconds = 1000;
+ float rateInMs = ((double) timeValue / (double) timeScale) * milliseconds;
+ mVsyncRate = TimeDuration::FromMilliseconds(rateInMs);
+ }
+ }
+
+ virtual void DisableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!IsVsyncEnabled()) {
+ return;
+ }
+
+ // Release the display link
+ if (mDisplayLink) {
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
+ }
+ }
+
+ virtual bool IsVsyncEnabled() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mDisplayLink != nullptr;
+ }
+
+ virtual TimeDuration GetVsyncRate() override
+ {
+ return mVsyncRate;
+ }
+
+ virtual void Shutdown() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ mTimer->Cancel();
+ mTimer = nullptr;
+ DisableVsync();
+ }
+
+ // The vsync timestamps given by the CVDisplayLinkCallback are
+ // in the future for the NEXT frame. Large parts of Gecko, such
+ // as animations assume a timestamp at either now or in the past.
+ // Normalize the timestamps given to the VsyncDispatchers to the vsync
+ // that just occured, not the vsync that is upcoming.
+ TimeStamp mPreviousTimestamp;
+
+ private:
+ // Manages the display link render thread
+ CVDisplayLinkRef mDisplayLink;
+ RefPtr<nsITimer> mTimer;
+ TimeDuration mVsyncRate;
+ }; // OSXDisplay
+
+private:
+ virtual ~OSXVsyncSource()
+ {
+ }
+
+ OSXDisplay mGlobalDisplay;
+}; // OSXVsyncSource
+
+static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
+ const CVTimeStamp* aNow,
+ const CVTimeStamp* aOutputTime,
+ CVOptionFlags aFlagsIn,
+ CVOptionFlags* aFlagsOut,
+ void* aDisplayLinkContext)
+{
+ // Executed on OS X hardware vsync thread
+ OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
+ int64_t nextVsyncTimestamp = aOutputTime->hostTime;
+
+ mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
+ mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
+ mozilla::TimeStamp now = TimeStamp::Now();
+
+ // Snow leopard sometimes sends vsync timestamps very far in the past.
+ // Normalize the vsync timestamps to now.
+ if (nextVsync <= previousVsync) {
+ nextVsync = now;
+ previousVsync = now;
+ } else if (now < previousVsync) {
+ // Bug 1158321 - The VsyncCallback can sometimes execute before the reported
+ // vsync time. In those cases, normalize the timestamp to Now() as sending
+ // timestamps in the future has undefined behavior. See the comment above
+ // OSXDisplay::mPreviousTimestamp
+ previousVsync = now;
+ }
+
+ display->mPreviousTimestamp = nextVsync;
+
+ display->NotifyVsync(previousVsync);
+ return kCVReturnSuccess;
+}
+
+already_AddRefed<mozilla::gfx::VsyncSource>
+gfxPlatformMac::CreateHardwareVsyncSource()
+{
+ RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource();
+ VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay();
+ primaryDisplay.EnableVsync();
+ if (!primaryDisplay.IsVsyncEnabled()) {
+ NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.");
+ return gfxPlatform::CreateHardwareVsyncSource();
+ }
+
+ primaryDisplay.DisableVsync();
+ return osxVsyncSource.forget();
+}
+
+void
+gfxPlatformMac::GetPlatformCMSOutputProfile(void* &mem, size_t &size)
+{
+ mem = nullptr;
+ size = 0;
+
+ CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
+ if (!cspace) {
+ cspace = ::CGColorSpaceCreateDeviceRGB();
+ }
+ if (!cspace) {
+ return;
+ }
+
+ CFDataRef iccp = ::CGColorSpaceCopyICCProfile(cspace);
+
+ ::CFRelease(cspace);
+
+ if (!iccp) {
+ return;
+ }
+
+ // copy to external buffer
+ size = static_cast<size_t>(::CFDataGetLength(iccp));
+ if (size > 0) {
+ void *data = malloc(size);
+ if (data) {
+ memcpy(data, ::CFDataGetBytePtr(iccp), size);
+ mem = data;
+ } else {
+ size = 0;
+ }
+ }
+
+ ::CFRelease(iccp);
+}
diff --git a/gfx/thebes/gfxPlatformMac.h b/gfx/thebes/gfxPlatformMac.h
new file mode 100644
index 0000000000..ea4c1a1011
--- /dev/null
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PLATFORM_MAC_H
+#define GFX_PLATFORM_MAC_H
+
+#include "nsTArrayForwardDeclare.h"
+#include "gfxPlatform.h"
+#include "mozilla/LookAndFeel.h"
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+class VsyncSource;
+} // namespace gfx
+} // namespace mozilla
+
+class gfxPlatformMac : public gfxPlatform {
+public:
+ gfxPlatformMac();
+ virtual ~gfxPlatformMac();
+
+ static gfxPlatformMac *GetPlatform() {
+ return (gfxPlatformMac*) gfxPlatform::GetPlatform();
+ }
+
+ virtual already_AddRefed<gfxASurface>
+ CreateOffscreenSurface(const IntSize& aSize,
+ gfxImageFormat aFormat) override;
+
+ already_AddRefed<mozilla::gfx::ScaledFont>
+ GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
+
+ gfxFontGroup*
+ CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxTextPerfMetrics* aTextPerf,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize) override;
+
+ virtual gfxPlatformFontList* CreatePlatformFontList() override;
+
+ bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) override;
+
+ virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ nsTArray<const char*>& aFontList) override;
+
+ // lookup the system font for a particular system font type and set
+ // the name and style characteristics
+ static void
+ LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
+ nsAString& aSystemFontName,
+ gfxFontStyle &aFontStyle,
+ float aDevPixPerCSSPixel);
+
+ virtual bool CanRenderContentToDataSurface() const override {
+ return true;
+ }
+
+ virtual bool SupportsApzWheelInput() const override {
+ return true;
+ }
+
+ bool RespectsFontStyleSmoothing() const override {
+ // gfxMacFont respects the font smoothing hint.
+ return true;
+ }
+
+ bool RequiresAcceleratedGLContextForCompositorOGL() const override {
+ // On OS X in a VM, unaccelerated CompositorOGL shows black flashes, so we
+ // require accelerated GL for CompositorOGL but allow unaccelerated GL for
+ // BasicCompositor.
+ return true;
+ }
+
+ virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
+
+ // lower threshold on font anti-aliasing
+ uint32_t GetAntiAliasingThreshold() { return mFontAntiAliasingThreshold; }
+
+private:
+ virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size) override;
+
+ // read in the pref value for the lower threshold on font anti-aliasing
+ static uint32_t ReadAntiAliasingThreshold();
+
+ uint32_t mFontAntiAliasingThreshold;
+};
+
+#endif /* GFX_PLATFORM_MAC_H */
diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
index b02638e5a6..ac5bdd45a2 100644
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -424,6 +424,9 @@ private:
DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist", IgnoreDXInterop2Blacklist, bool, false);
DECL_GFX_PREF(Live, "gl.msaa-level", MSAALevel, uint32_t, 2);
+#if defined(XP_MACOSX)
+ DECL_GFX_PREF(Live, "gl.multithreaded", GLMultithreaded, bool, false);
+#endif
DECL_GFX_PREF(Live, "gl.require-hardware", RequireHardwareGL, bool, false);
DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
diff --git a/gfx/thebes/gfxQuartzNativeDrawing.cpp b/gfx/thebes/gfxQuartzNativeDrawing.cpp
new file mode 100644
index 0000000000..f14b71d779
--- /dev/null
+++ b/gfx/thebes/gfxQuartzNativeDrawing.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxQuartzNativeDrawing.h"
+#include "gfxPlatform.h"
+#include "mozilla/gfx/Helpers.h"
+
+using namespace mozilla::gfx;
+using namespace mozilla;
+
+gfxQuartzNativeDrawing::gfxQuartzNativeDrawing(DrawTarget& aDrawTarget,
+ const Rect& nativeRect)
+ : mDrawTarget(&aDrawTarget)
+ , mNativeRect(nativeRect)
+ , mCGContext(nullptr)
+{
+}
+
+CGContextRef
+gfxQuartzNativeDrawing::BeginNativeDrawing()
+{
+ NS_ASSERTION(!mCGContext, "BeginNativeDrawing called when drawing already in progress");
+
+ DrawTarget *dt = mDrawTarget;
+ if (dt->IsDualDrawTarget() || dt->IsTiledDrawTarget() ||
+ dt->GetBackendType() != BackendType::SKIA) {
+ // We need a DrawTarget that we can get a CGContextRef from:
+ Matrix transform = dt->GetTransform();
+
+ mNativeRect = transform.TransformBounds(mNativeRect);
+ mNativeRect.RoundOut();
+ // Quartz theme drawing often adjusts drawing rects, so make
+ // sure our surface is big enough for that.
+ mNativeRect.Inflate(5);
+ if (mNativeRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ mTempDrawTarget =
+ Factory::CreateDrawTarget(BackendType::SKIA,
+ IntSize::Truncate(mNativeRect.width, mNativeRect.height),
+ SurfaceFormat::B8G8R8A8);
+
+ if (mTempDrawTarget) {
+ transform.PostTranslate(-mNativeRect.x, -mNativeRect.y);
+ mTempDrawTarget->SetTransform(transform);
+ }
+ dt = mTempDrawTarget;
+ }
+ if (dt) {
+ MOZ_ASSERT(dt->GetBackendType() == BackendType::SKIA);
+ mCGContext = mBorrowedContext.Init(dt);
+ MOZ_ASSERT(mCGContext);
+ }
+ return mCGContext;
+}
+
+void
+gfxQuartzNativeDrawing::EndNativeDrawing()
+{
+ NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing");
+
+ mBorrowedContext.Finish();
+ if (mTempDrawTarget) {
+ RefPtr<SourceSurface> source = mTempDrawTarget->Snapshot();
+
+ AutoRestoreTransform autoRestore(mDrawTarget);
+ mDrawTarget->SetTransform(Matrix());
+ mDrawTarget->DrawSurface(source, mNativeRect,
+ Rect(0, 0, mNativeRect.width, mNativeRect.height));
+ }
+}
diff --git a/gfx/thebes/gfxQuartzNativeDrawing.h b/gfx/thebes/gfxQuartzNativeDrawing.h
new file mode 100644
index 0000000000..736f9ce836
--- /dev/null
+++ b/gfx/thebes/gfxQuartzNativeDrawing.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _GFXQUARTZNATIVEDRAWING_H_
+#define _GFXQUARTZNATIVEDRAWING_H_
+
+#include "mozilla/Attributes.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BorrowedContext.h"
+#include "mozilla/RefPtr.h"
+
+class gfxQuartzNativeDrawing {
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::Rect Rect;
+public:
+
+ /* Create native Quartz drawing for a rectangle bounded by
+ * nativeRect.
+ *
+ * Typical usage looks like:
+ *
+ * gfxQuartzNativeDrawing nativeDraw(ctx, nativeRect);
+ * CGContextRef cgContext = nativeDraw.BeginNativeDrawing();
+ * if (!cgContext)
+ * return NS_ERROR_FAILURE;
+ *
+ * ... call Quartz operations on CGContextRef to draw to nativeRect ...
+ *
+ * nativeDraw.EndNativeDrawing();
+ *
+ * aNativeRect is the size of the surface (in Quartz/Cocoa points) that
+ * will be created _if_ the gfxQuartzNativeDrawing decides to create a new
+ * surface and CGContext for its drawing operations, which it then
+ * composites into the target DrawTarget.
+ *
+ * (Note that aNativeRect will be ignored if the gfxQuartzNativeDrawing
+ * uses the target DrawTarget directly.)
+ *
+ * The optional aBackingScale parameter is a scaling factor that will be
+ * applied when creating and rendering into such a temporary surface.
+ */
+ gfxQuartzNativeDrawing(DrawTarget& aDrawTarget,
+ const Rect& aNativeRect);
+
+ /* Returns a CGContextRef which may be used for native drawing. This
+ * CGContextRef is valid until EndNativeDrawing is called; if it is used
+ * for drawing after that time, the result is undefined. */
+ CGContextRef BeginNativeDrawing();
+
+ /* Marks the end of native drawing */
+ void EndNativeDrawing();
+
+private:
+ // don't allow copying via construction or assignment
+ gfxQuartzNativeDrawing(const gfxQuartzNativeDrawing&) = delete;
+ const gfxQuartzNativeDrawing& operator=(const gfxQuartzNativeDrawing&) = delete;
+
+ // Final destination context
+ RefPtr<DrawTarget> mDrawTarget;
+ RefPtr<DrawTarget> mTempDrawTarget;
+ mozilla::gfx::BorrowedCGContext mBorrowedContext;
+ mozilla::gfx::Rect mNativeRect;
+
+ // saved state
+ CGContextRef mCGContext;
+};
+
+#endif
diff --git a/gfx/thebes/gfxQuartzSurface.cpp b/gfx/thebes/gfxQuartzSurface.cpp
new file mode 100644
index 0000000000..99553e0c07
--- /dev/null
+++ b/gfx/thebes/gfxQuartzSurface.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxQuartzSurface.h"
+#include "gfxContext.h"
+#include "gfxImageSurface.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/HelpersCairo.h"
+
+#include "cairo-quartz.h"
+
+void
+gfxQuartzSurface::MakeInvalid()
+{
+ mSize = mozilla::gfx::IntSize(-1, -1);
+}
+
+gfxQuartzSurface::gfxQuartzSurface(const mozilla::gfx::IntSize& desiredSize,
+ gfxImageFormat format)
+ : mCGContext(nullptr), mSize(desiredSize)
+{
+ if (!mozilla::gfx::Factory::CheckSurfaceSize(desiredSize))
+ MakeInvalid();
+
+ unsigned int width = static_cast<unsigned int>(mSize.width);
+ unsigned int height = static_cast<unsigned int>(mSize.height);
+
+ cairo_format_t cformat = GfxFormatToCairoFormat(format);
+ cairo_surface_t *surf = cairo_quartz_surface_create(cformat, width, height);
+
+ mCGContext = cairo_quartz_surface_get_cg_context (surf);
+
+ CGContextRetain(mCGContext);
+
+ Init(surf);
+ if (mSurfaceValid) {
+ RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
+ }
+}
+
+gfxQuartzSurface::gfxQuartzSurface(CGContextRef context,
+ const mozilla::gfx::IntSize& size)
+ : mCGContext(context), mSize(size)
+{
+ if (!mozilla::gfx::Factory::CheckSurfaceSize(size))
+ MakeInvalid();
+
+ unsigned int width = static_cast<unsigned int>(mSize.width);
+ unsigned int height = static_cast<unsigned int>(mSize.height);
+
+ cairo_surface_t *surf =
+ cairo_quartz_surface_create_for_cg_context(context,
+ width, height);
+
+ CGContextRetain(mCGContext);
+
+ Init(surf);
+ if (mSurfaceValid) {
+ RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
+ }
+}
+
+gfxQuartzSurface::gfxQuartzSurface(cairo_surface_t *csurf,
+ const mozilla::gfx::IntSize& aSize) :
+ mSize(aSize)
+{
+ mCGContext = cairo_quartz_surface_get_cg_context (csurf);
+ CGContextRetain (mCGContext);
+
+ Init(csurf, true);
+}
+
+gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
+ const mozilla::gfx::IntSize& aSize,
+ long stride,
+ gfxImageFormat format)
+ : mCGContext(nullptr), mSize(aSize.width, aSize.height)
+{
+ if (!mozilla::gfx::Factory::CheckSurfaceSize(aSize))
+ MakeInvalid();
+
+ cairo_format_t cformat = GfxFormatToCairoFormat(format);
+ cairo_surface_t *surf = cairo_quartz_surface_create_for_data
+ (data, cformat, aSize.width, aSize.height, stride);
+
+ mCGContext = cairo_quartz_surface_get_cg_context (surf);
+
+ CGContextRetain(mCGContext);
+
+ Init(surf);
+ if (mSurfaceValid) {
+ RecordMemoryUsed(mSize.height * stride + sizeof(gfxQuartzSurface));
+ }
+}
+
+already_AddRefed<gfxASurface>
+gfxQuartzSurface::CreateSimilarSurface(gfxContentType aType,
+ const mozilla::gfx::IntSize& aSize)
+{
+ cairo_surface_t *surface =
+ cairo_quartz_surface_create_cg_layer(mSurface, (cairo_content_t)aType,
+ aSize.width, aSize.height);
+ if (cairo_surface_status(surface)) {
+ cairo_surface_destroy(surface);
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> result = Wrap(surface, aSize);
+ cairo_surface_destroy(surface);
+ return result.forget();
+}
+
+already_AddRefed<gfxImageSurface> gfxQuartzSurface::GetAsImageSurface()
+{
+ cairo_surface_t *surface = cairo_quartz_surface_get_image(mSurface);
+ if (!surface || cairo_surface_status(surface))
+ return nullptr;
+
+ RefPtr<gfxASurface> img = Wrap(surface);
+
+ // cairo_quartz_surface_get_image returns a referenced image, and thebes
+ // shares the refcounts of Cairo surfaces. However, Wrap also adds a
+ // reference to the image. We need to remove one of these references
+ // explicitly so we don't leak.
+ img.get()->Release();
+
+ img->SetOpaqueRect(GetOpaqueRect());
+
+ return img.forget().downcast<gfxImageSurface>();
+}
+
+gfxQuartzSurface::~gfxQuartzSurface()
+{
+ CGContextRelease(mCGContext);
+}
diff --git a/gfx/thebes/gfxQuartzSurface.h b/gfx/thebes/gfxQuartzSurface.h
new file mode 100644
index 0000000000..50e2bfb2c3
--- /dev/null
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_QUARTZSURFACE_H
+#define GFX_QUARTZSURFACE_H
+
+#include "gfxASurface.h"
+#include "nsSize.h"
+#include "gfxPoint.h"
+
+#include <Carbon/Carbon.h>
+
+class gfxContext;
+class gfxImageSurface;
+
+class gfxQuartzSurface : public gfxASurface {
+public:
+ gfxQuartzSurface(const mozilla::gfx::IntSize&, gfxImageFormat format);
+ gfxQuartzSurface(CGContextRef context, const mozilla::gfx::IntSize& size);
+ gfxQuartzSurface(cairo_surface_t *csurf, const mozilla::gfx::IntSize& aSize);
+ gfxQuartzSurface(unsigned char *data, const mozilla::gfx::IntSize& size, long stride, gfxImageFormat format);
+
+ virtual ~gfxQuartzSurface();
+
+ virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
+ const mozilla::gfx::IntSize& aSize);
+
+ virtual const mozilla::gfx::IntSize GetSize() const { return mozilla::gfx::IntSize(mSize.width, mSize.height); }
+
+ CGContextRef GetCGContext() { return mCGContext; }
+
+ already_AddRefed<gfxImageSurface> GetAsImageSurface();
+
+protected:
+ void MakeInvalid();
+
+ CGContextRef mCGContext;
+ mozilla::gfx::IntSize mSize;
+};
+
+#endif /* GFX_QUARTZSURFACE_H */
diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp
index ae746b8c5b..db9fc346b4 100644
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -518,6 +518,11 @@ HasSyntheticBoldOrColor(const gfxTextRun *aRun, gfxTextRun::Range aRange)
if (fe->TryGetSVGData(font) || fe->TryGetColorGlyphs()) {
return true;
}
+#if defined(XP_MACOSX) // sbix fonts only supported via Core Text
+ if (fe->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
+ return true;
+ }
+#endif
}
}
return false;
diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build
index be28f4e51c..1d3cf3bc93 100644
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -57,7 +57,24 @@ EXPORTS.mozilla.gfx += [
'PrintTargetThebes.h',
]
-if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ EXPORTS += [
+ 'gfxPlatformMac.h',
+ 'gfxQuartzNativeDrawing.h',
+ 'gfxQuartzSurface.h',
+ ]
+ EXPORTS.mozilla.gfx += [
+ 'PrintTargetCG.h',
+ ]
+ SOURCES += [
+ 'gfxCoreTextShaper.cpp',
+ 'gfxMacFont.cpp',
+ 'gfxPlatformMac.cpp',
+ 'gfxQuartzNativeDrawing.cpp',
+ 'gfxQuartzSurface.cpp',
+ 'PrintTargetCG.cpp',
+ ]
+elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
EXPORTS += [
'gfxFontconfigFonts.h',
'gfxFT2FontBase.h',
@@ -165,7 +182,11 @@ SOURCES += [
'VsyncSource.cpp',
]
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'gfxMacPlatformFontList.mm',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'D3D11Checks.cpp',
'DeviceManagerDx.cpp',