1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtGui module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qfontengine_coretext_p.h"
44
#include <QtCore/qendian.h>
45
#include <QtCore/qsettings.h>
47
#include <private/qimage_p.h>
49
#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
53
static float SYNTHETIC_ITALIC_SKEW = tanf(14 * acosf(0) / 90);
55
static void loadAdvancesForGlyphs(CTFontRef ctfont,
56
QVarLengthArray<CGGlyph> &cgGlyphs,
57
QGlyphLayout *glyphs, int len,
58
QFontEngine::ShaperFlags flags,
59
const QFontDef &fontDef)
62
QVarLengthArray<CGSize> advances(len);
63
CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len);
65
for (int i = 0; i < len; ++i) {
66
if (glyphs->glyphs[i] & 0xff000000)
68
glyphs->advances_x[i] = QFixed::fromReal(advances[i].width);
69
glyphs->advances_y[i] = QFixed::fromReal(advances[i].height);
72
if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
73
for (int i = 0; i < len; ++i) {
74
glyphs->advances_x[i] = glyphs->advances_x[i].round();
75
glyphs->advances_y[i] = glyphs->advances_y[i].round();
81
int QCoreTextFontEngine::antialiasingThreshold = 0;
82
QFontEngineGlyphCache::Type QCoreTextFontEngine::defaultGlyphFormat = QFontEngineGlyphCache::Raster_RGBMask;
84
CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef)
86
CGAffineTransform transform = CGAffineTransformIdentity;
87
if (fontDef.stretch != 100)
88
transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
92
QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def)
95
transform = qt_transform_from_fontdef(fontDef);
98
cgFont = CTFontCopyGraphicsFont(font, NULL);
102
QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def)
105
transform = qt_transform_from_fontdef(fontDef);
107
// Keep reference count balanced
109
ctfont = CTFontCreateWithGraphicsFont(font, fontDef.pixelSize, &transform, NULL);
113
QCoreTextFontEngine::~QCoreTextFontEngine()
119
static QFont::Weight weightFromInteger(int weight)
123
else if (weight < 600)
124
return QFont::Normal;
125
else if (weight < 700)
126
return QFont::DemiBold;
127
else if (weight < 800)
133
int getTraitValue(CFDictionaryRef allTraits, CFStringRef trait)
135
if (CFDictionaryContainsKey(allTraits, trait)) {
136
CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait);
138
CFNumberGetValue(traitNum, kCFNumberFloatType, &v);
139
// the value we get from CFNumberRef is from -1.0 to 1.0
140
int value = v * 500 + 500;
147
void QCoreTextFontEngine::init()
149
Q_ASSERT(ctfont != NULL);
150
Q_ASSERT(cgFont != NULL);
152
glyphFormat = defaultGlyphFormat;
154
QCFString family = CTFontCopyFamilyName(ctfont);
155
fontDef.family = family;
157
QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute);
158
fontDef.styleName = styleName;
161
CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
162
if (traits & kCTFontItalicTrait)
163
fontDef.style = QFont::StyleItalic;
165
CFDictionaryRef allTraits = CTFontCopyTraits(ctfont);
166
fontDef.weight = weightFromInteger(getTraitValue(allTraits, kCTFontWeightTrait));
167
int slant = getTraitValue(allTraits, kCTFontSlantTrait);
168
if (slant > 500 && !(traits & kCTFontItalicTrait))
169
fontDef.style = QFont::StyleOblique;
170
CFRelease(allTraits);
172
if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait))
173
synthesisFlags |= SynthesizedBold;
174
// XXX: we probably don't need to synthesis italic for oblique font
175
if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait))
176
synthesisFlags |= SynthesizedItalic;
179
QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
180
unsigned emSize = CTFontGetUnitsPerEm(ctfont);
181
if (os2Table.size() >= 10) {
182
fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8));
183
// qAbs is a workaround for weird fonts like Lucida Grande
184
qint16 width = qAbs(qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 2)));
185
avgCharWidth = QFixed::fromReal(width * fontDef.pixelSize / emSize);
187
avgCharWidth = QFontEngine::averageCharWidth();
190
bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
191
int *nglyphs, QFontEngine::ShaperFlags flags) const
193
QCFType<CFStringRef> cfstring;
195
QVarLengthArray<CGGlyph> cgGlyphs(len);
196
CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len);
199
for (int i = 0; i < len; ++i) {
201
glyphs->glyphs[glyph_pos] = cgGlyphs[i];
203
cgGlyphs[glyph_pos] = cgGlyphs[i];
207
// If it's a non-BMP char, skip the lower part of surrogate pair and go
208
// directly to the next char without increasing glyph_pos
209
if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate())
213
*nglyphs = glyph_pos;
214
glyphs->numGlyphs = glyph_pos;
216
if (flags & GlyphIndicesOnly)
219
QVarLengthArray<CGSize> advances(glyph_pos);
220
CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), glyph_pos);
222
for (int i = 0; i < glyph_pos; ++i) {
223
if (glyphs->glyphs[i] & 0xff000000)
225
glyphs->advances_x[i] = QFixed::fromReal(advances[i].width);
226
glyphs->advances_y[i] = QFixed::fromReal(advances[i].height);
229
if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
230
for (int i = 0; i < glyph_pos; ++i) {
231
glyphs->advances_x[i] = glyphs->advances_x[i].round();
232
glyphs->advances_y[i] = glyphs->advances_y[i].round();
238
glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
241
bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
243
for (int i = 0; i < glyphs.numGlyphs; ++i) {
244
w += round ? glyphs.effectiveAdvance(i).round()
245
: glyphs.effectiveAdvance(i);
247
return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0);
250
glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
254
CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1);
255
if (synthesisFlags & QFontEngine::SynthesizedItalic) {
256
rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW;
258
ret.width = QFixed::fromReal(rect.size.width);
259
ret.height = QFixed::fromReal(rect.size.height);
260
ret.x = QFixed::fromReal(rect.origin.x);
261
ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
263
CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1);
264
ret.xoff = QFixed::fromReal(advances[0].width);
265
ret.yoff = QFixed::fromReal(advances[0].height);
267
if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
268
ret.xoff = ret.xoff.round();
269
ret.yoff = ret.yoff.round();
275
QFixed QCoreTextFontEngine::ascent() const
277
return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
278
? QFixed::fromReal(CTFontGetAscent(ctfont)).round()
279
: QFixed::fromReal(CTFontGetAscent(ctfont));
281
QFixed QCoreTextFontEngine::descent() const
283
QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont));
284
if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
289
QFixed QCoreTextFontEngine::leading() const
291
return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
292
? QFixed::fromReal(CTFontGetLeading(ctfont)).round()
293
: QFixed::fromReal(CTFontGetLeading(ctfont));
295
QFixed QCoreTextFontEngine::xHeight() const
297
return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
298
? QFixed::fromReal(CTFontGetXHeight(ctfont)).round()
299
: QFixed::fromReal(CTFontGetXHeight(ctfont));
302
QFixed QCoreTextFontEngine::averageCharWidth() const
304
return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
305
? avgCharWidth.round() : avgCharWidth;
308
qreal QCoreTextFontEngine::maxCharWidth() const
313
qreal QCoreTextFontEngine::minLeftBearing() const
318
qreal QCoreTextFontEngine::minRightBearing() const
323
void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
325
QVarLengthArray<QFixedPoint> positions;
326
QVarLengthArray<glyph_t> glyphs;
328
matrix.translate(x, y);
329
getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
330
if (glyphs.size() == 0)
333
CGContextSetFontSize(ctx, fontDef.pixelSize);
335
CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
337
CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
339
CGAffineTransformConcat(cgMatrix, oldTextMatrix);
341
if (synthesisFlags & QFontEngine::SynthesizedItalic)
342
cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
344
cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
346
CGContextSetTextMatrix(ctx, cgMatrix);
348
CGContextSetTextDrawingMode(ctx, kCGTextFill);
351
QVarLengthArray<CGSize> advances(glyphs.size());
352
QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
354
for (int i = 0; i < glyphs.size() - 1; ++i) {
355
advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
356
advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
357
cgGlyphs[i] = glyphs[i];
359
advances[glyphs.size() - 1].width = 0;
360
advances[glyphs.size() - 1].height = 0;
361
cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
363
CGContextSetFont(ctx, cgFont);
364
//NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont));
366
CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
368
CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
370
if (synthesisFlags & QFontEngine::SynthesizedBold) {
371
CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
372
positions[0].y.toReal());
374
CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
377
CGContextSetTextMatrix(ctx, oldTextMatrix);
380
struct ConvertPathInfo
382
ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {}
387
static void convertCGPathToQPainterPath(void *info, const CGPathElement *element)
389
ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info);
390
switch(element->type) {
391
case kCGPathElementMoveToPoint:
392
myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(),
393
element->points[0].y + myInfo->pos.y());
395
case kCGPathElementAddLineToPoint:
396
myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(),
397
element->points[0].y + myInfo->pos.y());
399
case kCGPathElementAddQuadCurveToPoint:
400
myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(),
401
element->points[0].y + myInfo->pos.y(),
402
element->points[1].x + myInfo->pos.x(),
403
element->points[1].y + myInfo->pos.y());
405
case kCGPathElementAddCurveToPoint:
406
myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(),
407
element->points[0].y + myInfo->pos.y(),
408
element->points[1].x + myInfo->pos.x(),
409
element->points[1].y + myInfo->pos.y(),
410
element->points[2].x + myInfo->pos.x(),
411
element->points[2].y + myInfo->pos.y());
413
case kCGPathElementCloseSubpath:
414
myInfo->path->closeSubpath();
417
qDebug() << "Unhandled path transform type: " << element->type;
422
void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
423
QPainterPath *path, QTextItem::RenderFlags)
425
CGAffineTransform cgMatrix = CGAffineTransformIdentity;
426
cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
428
if (synthesisFlags & QFontEngine::SynthesizedItalic)
429
cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
431
for (int i = 0; i < nGlyphs; ++i) {
432
QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
433
ConvertPathInfo info(path, positions[i].toPointF());
434
CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
438
QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, bool aa, const QTransform &m)
440
glyph_metrics_t br = boundingBox(glyph);
443
qreal hscale = m.m11();
444
qreal vscale = m.m22();
445
br.width = QFixed::fromReal(br.width.toReal() * hscale);
446
br.height = QFixed::fromReal(br.height.toReal() * vscale);
447
br.x = QFixed::fromReal(br.x.toReal() * hscale);
448
br.y = QFixed::fromReal(br.y.toReal() * vscale);
451
QImage im(qAbs(qRound(br.width))+2, qAbs(qRound(br.height))+2, QImage::Format_RGB32);
455
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
457
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
459
uint cgflags = kCGImageAlphaNoneSkipFirst;
460
#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
461
cgflags |= kCGBitmapByteOrder32Host;
463
CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
464
8, im.bytesPerLine(), colorspace,
466
CGContextSetFontSize(ctx, fontDef.pixelSize);
467
CGContextSetShouldAntialias(ctx, (aa || fontDef.pointSize > antialiasingThreshold)
468
&& !(fontDef.styleStrategy & QFont::NoAntialias));
469
CGContextSetShouldSmoothFonts(ctx, aa);
470
CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
471
CGAffineTransform cgMatrix = CGAffineTransformIdentity;
473
CGAffineTransformConcat(cgMatrix, oldTextMatrix);
475
if (synthesisFlags & QFontEngine::SynthesizedItalic)
476
cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
478
cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
480
cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMakeScale(m.m11(), m.m22()));
482
CGContextSetTextMatrix(ctx, cgMatrix);
483
CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
484
CGContextSetTextDrawingMode(ctx, kCGTextFill);
486
CGContextSetFont(ctx, cgFont);
488
qreal pos_x = -br.x.truncate() + subPixelPosition.toReal();
489
qreal pos_y = im.height() + br.y.toReal();
490
CGContextSetTextPosition(ctx, pos_x, pos_y);
495
CGGlyph cgGlyph = glyph;
496
CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
498
if (synthesisFlags & QFontEngine::SynthesizedBold) {
499
CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
500
CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
503
CGContextRelease(ctx);
504
CGColorSpaceRelease(colorspace);
509
QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
511
QImage im = imageForGlyph(glyph, subPixelPosition, false, QTransform());
513
QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
514
QVector<QRgb> colors(256);
515
for (int i=0; i<256; ++i)
516
colors[i] = qRgba(0, 0, 0, i);
517
indexed.setColorTable(colors);
519
for (int y=0; y<im.height(); ++y) {
520
uint *src = (uint*) im.scanLine(y);
521
uchar *dst = indexed.scanLine(y);
522
for (int x=0; x<im.width(); ++x) {
532
QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &x)
534
if (x.type() > QTransform::TxScale)
535
return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, x);
537
QImage im = imageForGlyph(glyph, subPixelPosition, true, x);
538
qGamma_correct_back_to_linear_cs(&im);
542
void QCoreTextFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
544
int i, numGlyphs = glyphs->numGlyphs;
545
QVarLengthArray<CGGlyph> cgGlyphs(numGlyphs);
547
for (i = 0; i < numGlyphs; ++i) {
548
if (glyphs->glyphs[i] & 0xff000000)
551
cgGlyphs[i] = glyphs->glyphs[i];
554
loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, numGlyphs, flags, fontDef);
557
QFontEngine::FaceId QCoreTextFontEngine::faceId() const
559
return QFontEngine::FaceId();
562
bool QCoreTextFontEngine::canRender(const QChar *string, int len)
564
QVarLengthArray<CGGlyph> cgGlyphs(len);
565
return CTFontGetGlyphsForCharacters(ctfont, (const UniChar *) string, cgGlyphs.data(), len);
568
bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
570
QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0);
571
if (!table || !length)
573
CFIndex tableLength = CFDataGetLength(table);
574
int availableLength = *length;
575
*length = tableLength;
577
if (tableLength > availableLength)
579
CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer);
584
void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *)
589
QFixed QCoreTextFontEngine::emSquareSize() const
591
return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont)));
594
QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
596
QFontDef newFontDef = fontDef;
597
newFontDef.pixelSize = pixelSize;
598
newFontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi();
600
return new QCoreTextFontEngine(cgFont, newFontDef);
603
bool QCoreTextFontEngine::supportsTransformations(const QTransform &transform) const
605
return transform.type() > QTransform::TxTranslate;
610
#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)