michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ScaledFontMac.h" michael@0: #ifdef USE_SKIA michael@0: #include "PathSkia.h" michael@0: #include "skia/SkPaint.h" michael@0: #include "skia/SkPath.h" michael@0: #include "skia/SkTypeface_mac.h" michael@0: #endif michael@0: #include "DrawTargetCG.h" michael@0: #include michael@0: #include michael@0: michael@0: // prototype for private API michael@0: extern "C" { michael@0: CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph); michael@0: }; michael@0: michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: ScaledFontMac::CTFontDrawGlyphsFuncT* ScaledFontMac::CTFontDrawGlyphsPtr = nullptr; michael@0: bool ScaledFontMac::sSymbolLookupDone = false; michael@0: michael@0: ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize) michael@0: : ScaledFontBase(aSize) michael@0: { michael@0: if (!sSymbolLookupDone) { michael@0: CTFontDrawGlyphsPtr = michael@0: (CTFontDrawGlyphsFuncT*)dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); michael@0: sSymbolLookupDone = true; michael@0: } michael@0: michael@0: // XXX: should we be taking a reference michael@0: mFont = CGFontRetain(aFont); michael@0: if (CTFontDrawGlyphsPtr != nullptr) { michael@0: // only create mCTFont if we're going to be using the CTFontDrawGlyphs API michael@0: mCTFont = CTFontCreateWithGraphicsFont(aFont, aSize, nullptr, nullptr); michael@0: } else { michael@0: mCTFont = nullptr; michael@0: } michael@0: } michael@0: michael@0: ScaledFontMac::~ScaledFontMac() michael@0: { michael@0: if (mCTFont) { michael@0: CFRelease(mCTFont); michael@0: } michael@0: CGFontRelease(mFont); michael@0: } michael@0: michael@0: #ifdef USE_SKIA michael@0: SkTypeface* ScaledFontMac::GetSkTypeface() michael@0: { michael@0: if (!mTypeface) { michael@0: if (mCTFont) { michael@0: mTypeface = SkCreateTypefaceFromCTFont(mCTFont); michael@0: } else { michael@0: CTFontRef fontFace = CTFontCreateWithGraphicsFont(mFont, mSize, nullptr, nullptr); michael@0: mTypeface = SkCreateTypefaceFromCTFont(fontFace); michael@0: CFRelease(fontFace); michael@0: } michael@0: } michael@0: return mTypeface; michael@0: } michael@0: #endif michael@0: michael@0: // private API here are the public options on OS X michael@0: // CTFontCreatePathForGlyph michael@0: // ATSUGlyphGetCubicPaths michael@0: // we've used this in cairo sucessfully for some time. michael@0: // Note: cairo dlsyms it. We could do that but maybe it's michael@0: // safe just to use? michael@0: michael@0: TemporaryRef michael@0: ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) michael@0: { michael@0: if (aTarget->GetType() == BackendType::COREGRAPHICS || aTarget->GetType() == BackendType::COREGRAPHICS_ACCELERATED) { michael@0: CGMutablePathRef path = CGPathCreateMutable(); michael@0: michael@0: for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { michael@0: // XXX: we could probably fold both of these transforms together to avoid extra work michael@0: CGAffineTransform flip = CGAffineTransformMakeScale(1, -1); michael@0: CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex); michael@0: michael@0: CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize, michael@0: aBuffer.mGlyphs[i].mPosition.x, michael@0: aBuffer.mGlyphs[i].mPosition.y); michael@0: CGPathAddPath(path, &matrix, glyphPath); michael@0: CGPathRelease(glyphPath); michael@0: } michael@0: TemporaryRef ret = new PathCG(path, FillRule::FILL_WINDING); michael@0: CGPathRelease(path); michael@0: return ret; michael@0: } else { michael@0: return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) michael@0: { michael@0: if (!(aBackendType == BackendType::COREGRAPHICS || aBackendType == BackendType::COREGRAPHICS_ACCELERATED)) { michael@0: ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); michael@0: return; michael@0: } michael@0: michael@0: PathBuilderCG *pathBuilderCG = michael@0: static_cast(aBuilder); michael@0: // XXX: check builder type michael@0: for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { michael@0: // XXX: we could probably fold both of these transforms together to avoid extra work michael@0: CGAffineTransform flip = CGAffineTransformMakeScale(1, -1); michael@0: CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex); michael@0: michael@0: CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize, michael@0: aBuffer.mGlyphs[i].mPosition.x, michael@0: aBuffer.mGlyphs[i].mPosition.y); michael@0: CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath); michael@0: CGPathRelease(glyphPath); michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false) michael@0: { michael@0: uint32_t sum = 0L; michael@0: const uint32_t *table = tableStart; michael@0: const uint32_t *end = table+((length+3) & ~3) / sizeof(uint32_t); michael@0: while (table < end) { michael@0: if (skipChecksumAdjust && (table - tableStart) == 2) { michael@0: table++; michael@0: } else { michael@0: sum += CFSwapInt32BigToHost(*table++); michael@0: } michael@0: } michael@0: return sum; michael@0: } michael@0: michael@0: struct TableRecord { michael@0: uint32_t tag; michael@0: uint32_t checkSum; michael@0: uint32_t offset; michael@0: uint32_t length; michael@0: CFDataRef data; michael@0: }; michael@0: michael@0: int maxPow2LessThan(int a) michael@0: { michael@0: int x = 1; michael@0: int shift = 0; michael@0: while ((x<<(shift+1)) < a) { michael@0: shift++; michael@0: } michael@0: return shift; michael@0: } michael@0: michael@0: struct writeBuf michael@0: { michael@0: writeBuf(int size) michael@0: { michael@0: this->data = new unsigned char [size]; michael@0: this->offset = 0; michael@0: } michael@0: ~writeBuf() { michael@0: delete this->data; michael@0: } michael@0: michael@0: template michael@0: void writeElement(T a) michael@0: { michael@0: *reinterpret_cast(&this->data[this->offset]) = a; michael@0: this->offset += sizeof(T); michael@0: } michael@0: michael@0: void writeMem(const void *data, unsigned long length) michael@0: { michael@0: memcpy(&this->data[this->offset], data, length); michael@0: this->offset += length; michael@0: } michael@0: michael@0: void align() michael@0: { michael@0: while (this->offset & 3) { michael@0: this->data[this->offset] = 0; michael@0: this->offset++; michael@0: } michael@0: } michael@0: michael@0: unsigned char *data; michael@0: int offset; michael@0: }; michael@0: michael@0: bool michael@0: ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) michael@0: { michael@0: // We'll reconstruct a TTF font from the tables we can get from the CGFont michael@0: CFArrayRef tags = CGFontCopyTableTags(mFont); michael@0: CFIndex count = CFArrayGetCount(tags); michael@0: michael@0: TableRecord *records = new TableRecord[count]; michael@0: uint32_t offset = 0; michael@0: offset += sizeof(uint32_t)*3; michael@0: offset += sizeof(uint32_t)*4*count; michael@0: bool CFF = false; michael@0: for (CFIndex i = 0; i(CFDataGetBytePtr(data)), michael@0: records[i].length, skipChecksumAdjust); michael@0: offset += records[i].length; michael@0: // 32 bit align the tables michael@0: offset = (offset + 3) & ~3; michael@0: } michael@0: CFRelease(tags); michael@0: michael@0: struct writeBuf buf(offset); michael@0: // write header/offset table michael@0: if (CFF) { michael@0: buf.writeElement(CFSwapInt32HostToBig(0x4f54544f)); michael@0: } else { michael@0: buf.writeElement(CFSwapInt32HostToBig(0x00010000)); michael@0: } michael@0: buf.writeElement(CFSwapInt16HostToBig(count)); michael@0: buf.writeElement(CFSwapInt16HostToBig((1<(buf.data), offset)); michael@0: // set checkSumAdjust to the computed checksum michael@0: memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum)); michael@0: michael@0: // we always use an index of 0 michael@0: aDataCallback(buf.data, buf.offset, 0, mSize, aBaton); michael@0: michael@0: return true; michael@0: michael@0: } michael@0: michael@0: } michael@0: }