source: trunk/src/gui/text/qfontengine_mac.mm@ 439

Last change on this file since 439 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 60.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qapplication_p.h>
43#include <private/qfontengine_p.h>
44#include <private/qpainter_p.h>
45#include <private/qtextengine_p.h>
46#include <qbitmap.h>
47#include <private/qpaintengine_mac_p.h>
48#include <private/qprintengine_mac_p.h>
49#include <private/qpdf_p.h>
50#include <qglobal.h>
51#include <qpixmap.h>
52#include <qpixmapcache.h>
53#include <qvarlengtharray.h>
54#include <qdebug.h>
55#include <qendian.h>
56
57#include <ApplicationServices/ApplicationServices.h>
58#include <AppKit/AppKit.h>
59
60QT_BEGIN_NAMESPACE
61
62/*****************************************************************************
63 QFontEngine debug facilities
64 *****************************************************************************/
65//#define DEBUG_ADVANCES
66
67extern int qt_antialiasing_threshold; // QApplication.cpp
68
69#ifndef FixedToQFixed
70#define FixedToQFixed(a) QFixed::fromFixed((a) >> 10)
71#define QFixedToFixed(x) ((x).value() << 10)
72#endif
73
74class QMacFontPath
75{
76 float x, y;
77 QPainterPath *path;
78public:
79 inline QMacFontPath(float _x, float _y, QPainterPath *_path) : x(_x), y(_y), path(_path) { }
80 inline void setPosition(float _x, float _y) { x = _x; y = _y; }
81 inline void advance(float _x) { x += _x; }
82 static OSStatus lineTo(const Float32Point *, void *);
83 static OSStatus cubicTo(const Float32Point *, const Float32Point *,
84 const Float32Point *, void *);
85 static OSStatus moveTo(const Float32Point *, void *);
86 static OSStatus closePath(void *);
87};
88
89OSStatus QMacFontPath::lineTo(const Float32Point *pt, void *data)
90
91{
92 QMacFontPath *p = static_cast<QMacFontPath*>(data);
93 p->path->lineTo(p->x + pt->x, p->y + pt->y);
94 return noErr;
95}
96
97OSStatus QMacFontPath::cubicTo(const Float32Point *cp1, const Float32Point *cp2,
98 const Float32Point *ep, void *data)
99
100{
101 QMacFontPath *p = static_cast<QMacFontPath*>(data);
102 p->path->cubicTo(p->x + cp1->x, p->y + cp1->y,
103 p->x + cp2->x, p->y + cp2->y,
104 p->x + ep->x, p->y + ep->y);
105 return noErr;
106}
107
108OSStatus QMacFontPath::moveTo(const Float32Point *pt, void *data)
109{
110 QMacFontPath *p = static_cast<QMacFontPath*>(data);
111 p->path->moveTo(p->x + pt->x, p->y + pt->y);
112 return noErr;
113}
114
115OSStatus QMacFontPath::closePath(void *data)
116{
117 static_cast<QMacFontPath*>(data)->path->closeSubpath();
118 return noErr;
119}
120
121
122#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
123QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
124 : QFontEngineMulti(0)
125{
126 this->fontDef = fontDef;
127 CTFontSymbolicTraits symbolicTraits = 0;
128 if (fontDef.weight >= QFont::Bold)
129 symbolicTraits |= kCTFontBoldTrait;
130 switch (fontDef.style) {
131 case QFont::StyleNormal:
132 break;
133 case QFont::StyleItalic:
134 case QFont::StyleOblique:
135 symbolicTraits |= kCTFontItalicTrait;
136 break;
137 }
138
139 QCFString name;
140 ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name);
141 QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize);
142 QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, 0);
143 ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, 0, symbolicTraits, symbolicTraits);
144
145 // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does
146 // not exist for the given font. (for example italic)
147 if (ctfont == 0) {
148 ctfont = baseFont;
149 CFRetain(ctfont);
150 }
151
152 attributeDict = CFDictionaryCreateMutable(0, 2,
153 &kCFTypeDictionaryKeyCallBacks,
154 &kCFTypeDictionaryValueCallBacks);
155 CFDictionaryAddValue(attributeDict, NSFontAttributeName, ctfont);
156 if (!kerning) {
157 float zero = 0.0;
158 QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero);
159 CFDictionaryAddValue(attributeDict, kCTKernAttributeName, &noKern);
160 }
161
162 QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef, this);
163 fe->ref.ref();
164 engines.append(fe);
165
166}
167
168QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti()
169{
170 CFRelease(ctfont);
171}
172
173uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef id) const
174{
175 for (int i = 0; i < engines.count(); ++i) {
176 if (CFEqual(engineAt(i)->ctfont, id))
177 return i;
178 }
179
180 QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this);
181 QCoreTextFontEngine *fe = new QCoreTextFontEngine(id, fontDef, that);
182 fe->ref.ref();
183 that->engines.append(fe);
184 return engines.count() - 1;
185}
186
187bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags,
188 unsigned short *logClusters, const HB_CharAttributes *) const
189{
190 QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0,
191 reinterpret_cast<const UniChar *>(str),
192 len, kCFAllocatorNull);
193 QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict);
194 QCFType<CTTypesetterRef> typeSetter = CTTypesetterCreateWithAttributedString(attributedString);
195 CFRange range = {0, 0};
196 QCFType<CTLineRef> line = CTTypesetterCreateLine(typeSetter, range);
197 CFArrayRef array = CTLineGetGlyphRuns(line);
198 uint arraySize = CFArrayGetCount(array);
199 glyph_t *outGlyphs = glyphs->glyphs;
200 HB_GlyphAttributes *outAttributes = glyphs->attributes;
201 QFixed *outAdvances_x = glyphs->advances_x;
202 QFixed *outAdvances_y = glyphs->advances_y;
203 glyph_t *initialGlyph = outGlyphs;
204
205 if (arraySize == 0)
206 return false;
207
208 const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft);
209
210 bool outOBounds = false;
211 for (uint i = 0; i < arraySize; ++i) {
212 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, rtl ? (arraySize - 1 - i) : i));
213 CFIndex glyphCount = CTRunGetGlyphCount(run);
214 if (glyphCount == 0)
215 continue;
216
217 Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl);
218
219 if (!outOBounds && outGlyphs + glyphCount - initialGlyph > *nglyphs) {
220 outOBounds = true;
221 }
222 if (!outOBounds) {
223 CFDictionaryRef runAttribs = CTRunGetAttributes(run);
224 //NSLog(@"Dictionary %@", runAttribs);
225 if (!runAttribs)
226 runAttribs = attributeDict;
227 CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName));
228 const uint fontIndex = (fontIndexForFont(runFont) << 24);
229 //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont));
230 QVarLengthArray<CGGlyph, 512> cgglyphs(0);
231 const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run);
232 if (!tmpGlyphs) {
233 cgglyphs.resize(glyphCount);
234 CTRunGetGlyphs(run, range, cgglyphs.data());
235 tmpGlyphs = cgglyphs.constData();
236 }
237 QVarLengthArray<CGPoint, 512> cgpoints(0);
238 const CGPoint *tmpPoints = CTRunGetPositionsPtr(run);
239 if (!tmpPoints) {
240 cgpoints.resize(glyphCount);
241 CTRunGetPositions(run, range, cgpoints.data());
242 tmpPoints = cgpoints.constData();
243 }
244
245 const int rtlOffset = rtl ? (glyphCount - 1) : 0;
246 const int rtlSign = rtl ? -1 : 1;
247
248 if (logClusters) {
249 CFRange stringRange = CTRunGetStringRange(run);
250 QVarLengthArray<CFIndex, 512> stringIndices(0);
251 const CFIndex *tmpIndices = CTRunGetStringIndicesPtr(run);
252 if (!tmpIndices) {
253 stringIndices.resize(glyphCount);
254 CTRunGetStringIndices(run, range, stringIndices.data());
255 tmpIndices = stringIndices.constData();
256 }
257
258 const int firstGlyphIndex = outGlyphs - initialGlyph;
259 outAttributes[0].clusterStart = true;
260
261 CFIndex k = 0;
262 CFIndex i = 0;
263 for (i = stringRange.location;
264 (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) {
265 if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location) {
266 logClusters[i] = k + firstGlyphIndex;
267 outAttributes[k].clusterStart = true;
268 ++k;
269 } else {
270 logClusters[i] = k + firstGlyphIndex - 1;
271 }
272 }
273 // in case of a ligature at the end, fill the remaining logcluster entries
274 for (;i < stringRange.location + stringRange.length; i++) {
275 logClusters[i] = k + firstGlyphIndex - 1;
276 }
277 }
278 for (CFIndex i = 0; i < glyphCount - 1; ++i) {
279 int idx = rtlOffset + rtlSign * i;
280 outGlyphs[idx] = tmpGlyphs[i] | fontIndex;
281 outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x);
282 outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y);
283 }
284 CGSize lastGlyphAdvance;
285 CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1);
286
287 outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex;
288 outAdvances_x[rtl ? 0 : (glyphCount - 1)] = QFixed::fromReal(lastGlyphAdvance.width).ceil();
289 }
290 outGlyphs += glyphCount;
291 outAttributes += glyphCount;
292 outAdvances_x += glyphCount;
293 outAdvances_y += glyphCount;
294 }
295 *nglyphs = (outGlyphs - initialGlyph);
296 return !outOBounds;
297}
298
299bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
300 int *nglyphs, QTextEngine::ShaperFlags flags) const
301{
302 return stringToCMap(str, len, glyphs, nglyphs, flags, 0, 0);
303}
304
305void QCoreTextFontEngineMulti::recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const
306{
307}
308void QCoreTextFontEngineMulti::doKerning(int , QGlyphLayout *, QTextEngine::ShaperFlags) const
309{
310}
311
312void QCoreTextFontEngineMulti::loadEngine(int)
313{
314 // Do nothing
315 Q_ASSERT(false);
316}
317
318
319
320QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def,
321 QCoreTextFontEngineMulti *multiEngine)
322{
323 fontDef = def;
324 parentEngine = multiEngine;
325 synthesisFlags = 0;
326 ctfont = font;
327 CFRetain(ctfont);
328 ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0);
329 cgFont = CGFontCreateWithPlatformFont(&atsfont);
330 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
331 if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) {
332 synthesisFlags |= SynthesizedBold;
333 }
334
335 if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) {
336 synthesisFlags |= SynthesizedItalic;
337 }
338
339 QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
340 if (os2Table.size() >= 10)
341 fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8));
342}
343
344QCoreTextFontEngine::~QCoreTextFontEngine()
345{
346 CFRelease(ctfont);
347 CFRelease(cgFont);
348}
349
350bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int *, QTextEngine::ShaperFlags) const
351{
352 return false;
353}
354
355glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
356{
357 QFixed w;
358 for (int i = 0; i < glyphs.numGlyphs; ++i)
359 w += glyphs.effectiveAdvance(i);
360 return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0);
361}
362glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
363{
364 glyph_metrics_t ret;
365 CGGlyph g = glyph;
366 CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1);
367 ret.width = QFixed::fromReal(rect.size.width);
368 ret.height = QFixed::fromReal(rect.size.height);
369 ret.x = QFixed::fromReal(rect.origin.x);
370 ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
371 CGSize advances[1];
372 CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1);
373 ret.xoff = QFixed::fromReal(advances[0].width).ceil();
374 ret.yoff = QFixed::fromReal(advances[0].height).ceil();
375 return ret;
376}
377
378QFixed QCoreTextFontEngine::ascent() const
379{
380 return QFixed::fromReal(CTFontGetAscent(ctfont)).ceil();
381}
382QFixed QCoreTextFontEngine::descent() const
383{
384 return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil();
385}
386QFixed QCoreTextFontEngine::leading() const
387{
388 return QFixed::fromReal(CTFontGetLeading(ctfont)).ceil();
389}
390QFixed QCoreTextFontEngine::xHeight() const
391{
392 return QFixed::fromReal(CTFontGetXHeight(ctfont)).ceil();
393}
394QFixed QCoreTextFontEngine::averageCharWidth() const
395{
396 // ### Need to implement properly and get the information from the OS/2 Table.
397 return QFontEngine::averageCharWidth();
398}
399
400qreal QCoreTextFontEngine::maxCharWidth() const
401{
402 // ### Max Help!
403 return 0;
404
405}
406qreal QCoreTextFontEngine::minLeftBearing() const
407{
408 // ### Min Help!
409 return 0;
410
411}
412qreal QCoreTextFontEngine::minRightBearing() const
413{
414 // ### Max Help! (even thought it's right)
415 return 0;
416
417}
418
419void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
420{
421 QVarLengthArray<QFixedPoint> positions;
422 QVarLengthArray<glyph_t> glyphs;
423 QTransform matrix;
424 matrix.translate(x, y);
425 getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
426 if (glyphs.size() == 0)
427 return;
428
429 CGContextSetFontSize(ctx, fontDef.pixelSize);
430
431 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
432
433 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
434
435 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
436
437 if (synthesisFlags & QFontEngine::SynthesizedItalic)
438 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
439
440// ### cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
441
442 CGContextSetTextMatrix(ctx, cgMatrix);
443
444 CGContextSetTextDrawingMode(ctx, kCGTextFill);
445
446
447 QVarLengthArray<CGSize> advances(glyphs.size());
448 QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
449
450 for (int i = 0; i < glyphs.size() - 1; ++i) {
451 advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
452 advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
453 cgGlyphs[i] = glyphs[i];
454 }
455 advances[glyphs.size() - 1].width = 0;
456 advances[glyphs.size() - 1].height = 0;
457 cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
458
459 CGContextSetFont(ctx, cgFont);
460 //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont));
461
462 CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
463
464 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
465
466 if (synthesisFlags & QFontEngine::SynthesizedBold) {
467 CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
468 positions[0].y.toReal());
469
470 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
471 }
472
473 CGContextSetTextMatrix(ctx, oldTextMatrix);
474}
475
476struct ConvertPathInfo
477{
478 ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {}
479 QPainterPath *path;
480 QPointF pos;
481};
482
483static void convertCGPathToQPainterPath(void *info, const CGPathElement *element)
484{
485 ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info);
486 switch(element->type) {
487 case kCGPathElementMoveToPoint:
488 myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(),
489 element->points[0].y + myInfo->pos.y());
490 break;
491 case kCGPathElementAddLineToPoint:
492 myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(),
493 element->points[0].y + myInfo->pos.y());
494 break;
495 case kCGPathElementAddQuadCurveToPoint:
496 myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(),
497 element->points[0].y + myInfo->pos.y(),
498 element->points[1].x + myInfo->pos.x(),
499 element->points[1].y + myInfo->pos.y());
500 break;
501 case kCGPathElementAddCurveToPoint:
502 myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(),
503 element->points[0].y + myInfo->pos.y(),
504 element->points[1].x + myInfo->pos.x(),
505 element->points[1].y + myInfo->pos.y(),
506 element->points[2].x + myInfo->pos.x(),
507 element->points[2].y + myInfo->pos.y());
508 break;
509 case kCGPathElementCloseSubpath:
510 myInfo->path->closeSubpath();
511 break;
512 default:
513 qDebug() << "Unhandled path transform type: " << element->type;
514 }
515
516}
517
518void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
519 QPainterPath *path, QTextItem::RenderFlags)
520{
521
522 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
523 cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
524
525 if (synthesisFlags & QFontEngine::SynthesizedItalic)
526 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
527
528
529 for (int i = 0; i < nGlyphs; ++i) {
530 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
531 ConvertPathInfo info(path, positions[i].toPointF());
532 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
533 }
534}
535
536QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph)
537{
538 const glyph_metrics_t br = boundingBox(glyph);
539 QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32);
540 im.fill(0);
541
542 CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
543#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
544 uint cgflags = kCGImageAlphaNoneSkipFirst;
545#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
546 if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
547 cgflags |= kCGBitmapByteOrder32Host;
548#endif
549#else
550 CGImageAlphaInfo cgflags = kCGImageAlphaNoneSkipFirst;
551#endif
552 CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
553 8, im.bytesPerLine(), colorspace,
554 cgflags);
555 CGContextSetFontSize(ctx, fontDef.pixelSize);
556 CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
557 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
558 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
559
560 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
561
562 if (synthesisFlags & QFontEngine::SynthesizedItalic)
563 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
564
565// ### cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
566
567 CGContextSetTextMatrix(ctx, cgMatrix);
568 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
569 CGContextSetTextDrawingMode(ctx, kCGTextFill);
570
571 ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0);
572 QCFType<CGFontRef> cgFont = CGFontCreateWithPlatformFont(&atsfont);
573 CGContextSetFont(ctx, cgFont);
574
575 qreal pos_x = -br.x.toReal()+1, pos_y = im.height()+br.y.toReal();
576 CGContextSetTextPosition(ctx, pos_x, pos_y);
577
578 CGSize advance;
579 advance.width = 0;
580 advance.height = 0;
581 CGGlyph cgGlyph = glyph;
582 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
583
584 if (synthesisFlags & QFontEngine::SynthesizedBold) {
585 CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
586 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
587 }
588
589 CGContextRelease(ctx);
590
591 QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
592 QVector<QRgb> colors(256);
593 for (int i=0; i<256; ++i)
594 colors[i] = qRgba(0, 0, 0, i);
595 indexed.setColorTable(colors);
596
597 for (int y=0; y<im.height(); ++y) {
598 uint *src = (uint*) im.scanLine(y);
599 uchar *dst = indexed.scanLine(y);
600 for (int x=0; x<im.width(); ++x) {
601 *dst = qGray(*src);
602 ++dst;
603 ++src;
604 }
605 }
606
607 return indexed;
608}
609
610void QCoreTextFontEngine::recalcAdvances(int numGlyphs, QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
611{
612 Q_ASSERT(false);
613 Q_UNUSED(numGlyphs);
614 Q_UNUSED(glyphs);
615 Q_UNUSED(flags);
616}
617
618QFontEngine::FaceId QCoreTextFontEngine::faceId() const
619{
620 return QFontEngine::FaceId();
621}
622
623bool QCoreTextFontEngine::canRender(const QChar *string, int len)
624{
625 QCFType<CTFontRef> retFont = CTFontCreateForString(ctfont,
626 QCFType<CFStringRef>(CFStringCreateWithCharactersNoCopy(0,
627 reinterpret_cast<const UniChar *>(string),
628 len, kCFAllocatorNull)),
629 CFRangeMake(0, len));
630 return retFont != 0;
631 return false;
632}
633
634 bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
635 {
636 QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0);
637 if (!table || !length)
638 return false;
639 CFIndex tableLength = CFDataGetLength(table);
640 int availableLength = *length;
641 *length = tableLength;
642 if (buffer) {
643 if (tableLength > availableLength)
644 return false;
645 CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer);
646 }
647 return true;
648 }
649
650void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *)
651{
652 // ###
653}
654
655#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
656
657#ifndef QT_MAC_USE_COCOA
658QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
659 : QFontEngineMulti(0)
660{
661 this->fontDef = fontDef;
662 this->kerning = kerning;
663
664 // hopefully (CTFontCreateWithName or CTFontCreateWithFontDescriptor) + CTFontCreateCopyWithSymbolicTraits
665 // (or CTFontCreateWithQuickdrawInstance)
666 FMFontFamily fmFamily;
667 FMFontStyle fntStyle = 0;
668 fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
669 if (fmFamily == kInvalidFontFamily) {
670 // Use the ATSFont then...
671 fontID = FMGetFontFromATSFontRef(atsFontRef);
672 } else {
673 if (fontDef.weight >= QFont::Bold)
674 fntStyle |= ::bold;
675 if (fontDef.style != QFont::StyleNormal)
676 fntStyle |= ::italic;
677
678 FMFontStyle intrinsicStyle;
679 FMFont fnt = 0;
680 if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
681 fontID = FMGetATSFontRefFromFont(fnt);
682 }
683
684 // CFDictionaryRef, <CTStringAttributes.h>
685 OSStatus status;
686
687 status = ATSUCreateTextLayout(&textLayout);
688 Q_ASSERT(status == noErr);
689
690 const int maxAttributeCount = 5;
691 ATSUAttributeTag tags[maxAttributeCount + 1];
692 ByteCount sizes[maxAttributeCount + 1];
693 ATSUAttributeValuePtr values[maxAttributeCount + 1];
694 int attributeCount = 0;
695
696 Fixed size = FixRatio(fontDef.pixelSize, 1);
697 tags[attributeCount] = kATSUSizeTag;
698 sizes[attributeCount] = sizeof(size);
699 values[attributeCount] = &size;
700 ++attributeCount;
701
702 tags[attributeCount] = kATSUFontTag;
703 sizes[attributeCount] = sizeof(fontID);
704 values[attributeCount] = &this->fontID;
705 ++attributeCount;
706
707 transform = CGAffineTransformIdentity;
708 if (fontDef.stretch != 100) {
709 transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
710 tags[attributeCount] = kATSUFontMatrixTag;
711 sizes[attributeCount] = sizeof(transform);
712 values[attributeCount] = &transform;
713 ++attributeCount;
714 }
715
716 status = ATSUCreateStyle(&style);
717 Q_ASSERT(status == noErr);
718
719 Q_ASSERT(attributeCount < maxAttributeCount + 1);
720 status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
721 Q_ASSERT(status == noErr);
722
723 QFontEngineMac *fe = new QFontEngineMac(style, fontID, fontDef, this);
724 fe->ref.ref();
725 engines.append(fe);
726}
727
728QFontEngineMacMulti::~QFontEngineMacMulti()
729{
730 ATSUDisposeTextLayout(textLayout);
731 ATSUDisposeStyle(style);
732
733 for (int i = 0; i < engines.count(); ++i) {
734 QFontEngineMac *fe = const_cast<QFontEngineMac *>(static_cast<const QFontEngineMac *>(engines.at(i)));
735 fe->multiEngine = 0;
736 if (!fe->ref.deref())
737 delete fe;
738 }
739 engines.clear();
740}
741
742struct QGlyphLayoutInfo
743{
744 QGlyphLayout *glyphs;
745 int *numGlyphs;
746 bool callbackCalled;
747 int *mappedFonts;
748 QTextEngine::ShaperFlags flags;
749 QFontEngineMacMulti::ShaperItem *shaperItem;
750};
751
752static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon,
753 void *operationExtraParameter, ATSULayoutOperationCallbackStatus *callbackStatus)
754{
755 Q_UNUSED(selector);
756 Q_UNUSED(operationExtraParameter);
757
758 QGlyphLayoutInfo *nfo = reinterpret_cast<QGlyphLayoutInfo *>(refCon);
759 nfo->callbackCalled = true;
760
761 ATSLayoutRecord *layoutData = 0;
762 ItemCount itemCount = 0;
763
764 OSStatus e = noErr;
765 e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
766 /*iCreate =*/ false,
767 (void **) &layoutData,
768 &itemCount);
769 if (e != noErr)
770 return e;
771
772 *nfo->numGlyphs = itemCount - 1;
773
774 Fixed *baselineDeltas = 0;
775
776 e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
777 /*iCreate =*/ true,
778 (void **) &baselineDeltas,
779 &itemCount);
780 if (e != noErr)
781 return e;
782
783 int nextCharStop = -1;
784 int currentClusterGlyph = -1; // first glyph in log cluster
785 QFontEngineMacMulti::ShaperItem *item = nfo->shaperItem;
786 if (item->charAttributes) {
787 item = nfo->shaperItem;
788#if !defined(QT_NO_DEBUG)
789 int surrogates = 0;
790 const QChar *str = item->string;
791 for (int i = item->from; i < item->from + item->length - 1; ++i) {
792 surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00
793 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
794 }
795 Q_ASSERT(*nfo->numGlyphs == item->length - surrogates);
796#endif
797 for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop)
798 if (item->charAttributes[nextCharStop].charStop)
799 break;
800 nextCharStop -= item->from;
801 }
802
803 nfo->glyphs->attributes[0].clusterStart = true;
804 int glyphIdx = 0;
805 int glyphIncrement = 1;
806 if (nfo->flags & QTextEngine::RightToLeft) {
807 glyphIdx = itemCount - 2;
808 glyphIncrement = -1;
809 }
810 for (int i = 0; i < *nfo->numGlyphs; ++i, glyphIdx += glyphIncrement) {
811
812 int charOffset = layoutData[glyphIdx].originalOffset / sizeof(UniChar);
813 const int fontIdx = nfo->mappedFonts[charOffset];
814
815 ATSGlyphRef glyphId = layoutData[glyphIdx].glyphID;
816
817 QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]);
818 QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos);
819
820 if (glyphId != 0xffff || i == 0) {
821 nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24);
822
823 nfo->glyphs->advances_y[i] = yAdvance;
824 nfo->glyphs->advances_x[i] = xAdvance;
825 } else {
826 // ATSUI gives us 0xffff as glyph id at the index in the glyph array for
827 // a character position that maps to a ligtature. Such a glyph id does not
828 // result in any visual glyph, but it may have an advance, which is why we
829 // sum up the glyph advances.
830 --i;
831 nfo->glyphs->advances_y[i] += yAdvance;
832 nfo->glyphs->advances_x[i] += xAdvance;
833 *nfo->numGlyphs -= 1;
834 }
835
836 if (item->log_clusters) {
837 if (charOffset >= nextCharStop) {
838 nfo->glyphs->attributes[i].clusterStart = true;
839 currentClusterGlyph = i;
840
841 ++nextCharStop;
842 for (; nextCharStop < item->length; ++nextCharStop)
843 if (item->charAttributes[item->from + nextCharStop].charStop)
844 break;
845 } else {
846 if (currentClusterGlyph == -1)
847 currentClusterGlyph = i;
848 }
849 item->log_clusters[charOffset] = currentClusterGlyph;
850
851 // surrogate handling
852 if (charOffset < item->length - 1) {
853 QChar current = item->string[item->from + charOffset];
854 QChar next = item->string[item->from + charOffset + 1];
855 if (current.unicode() >= 0xd800 && current.unicode() < 0xdc00
856 && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) {
857 item->log_clusters[charOffset + 1] = currentClusterGlyph;
858 }
859 }
860 }
861 }
862
863 /*
864 if (item) {
865 qDebug() << "resulting logclusters:";
866 for (int i = 0; i < item->length; ++i)
867 qDebug() << "logClusters[" << i << "] =" << item->log_clusters[i];
868 qDebug() << "clusterstarts:";
869 for (int i = 0; i < *nfo->numGlyphs; ++i)
870 qDebug() << "clusterStart[" << i << "] =" << nfo->glyphs[i].attributes.clusterStart;
871 }
872 */
873
874 ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
875 (void **) &baselineDeltas);
876
877 ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
878 (void **) &layoutData);
879
880 *callbackStatus = kATSULayoutOperationCallbackStatusHandled;
881 return noErr;
882}
883
884int QFontEngineMacMulti::fontIndexForFontID(ATSUFontID id) const
885{
886 for (int i = 0; i < engines.count(); ++i) {
887 if (engineAt(i)->fontID == id)
888 return i;
889 }
890
891 QFontEngineMacMulti *that = const_cast<QFontEngineMacMulti *>(this);
892 QFontEngineMac *fe = new QFontEngineMac(style, id, fontDef, that);
893 fe->ref.ref();
894 that->engines.append(fe);
895 return engines.count() - 1;
896}
897
898bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
899{
900 return stringToCMap(str, len, glyphs, nglyphs, flags, /*logClusters=*/0, /*charAttributes=*/0);
901}
902
903bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
904 unsigned short *logClusters, const HB_CharAttributes *charAttributes) const
905{
906 if (*nglyphs < len) {
907 *nglyphs = len;
908 return false;
909 }
910
911 ShaperItem shaperItem;
912 shaperItem.string = str;
913 shaperItem.from = 0;
914 shaperItem.length = len;
915 shaperItem.glyphs = *glyphs;
916 shaperItem.glyphs.numGlyphs = *nglyphs;
917 shaperItem.flags = flags;
918 shaperItem.log_clusters = logClusters;
919 shaperItem.charAttributes = charAttributes;
920
921 const int maxChars = qMax(1,
922 int(SHRT_MAX / maxCharWidth())
923 - 10 // subtract a few to be on the safe side
924 );
925 if (len < maxChars || !charAttributes)
926 return stringToCMapInternal(str, len, glyphs, nglyphs, flags, &shaperItem);
927
928 int charIdx = 0;
929 int glyphIdx = 0;
930 ShaperItem tmpItem = shaperItem;
931
932 do {
933 tmpItem.from = shaperItem.from + charIdx;
934
935 int charCount = qMin(maxChars, len - charIdx);
936
937 int lastWhitespace = tmpItem.from + charCount - 1;
938 int lastSoftBreak = lastWhitespace;
939 int lastCharStop = lastSoftBreak;
940 for (int i = lastCharStop; i >= tmpItem.from; --i) {
941 if (tmpItem.charAttributes[i].whiteSpace) {
942 lastWhitespace = i;
943 break;
944 } if (tmpItem.charAttributes[i].lineBreakType != HB_NoBreak) {
945 lastSoftBreak = i;
946 } if (tmpItem.charAttributes[i].charStop) {
947 lastCharStop = i;
948 }
949 }
950 charCount = qMin(lastWhitespace, qMin(lastSoftBreak, lastCharStop)) - tmpItem.from + 1;
951
952 int glyphCount = shaperItem.glyphs.numGlyphs - glyphIdx;
953 if (glyphCount <= 0)
954 return false;
955 tmpItem.length = charCount;
956 tmpItem.glyphs = shaperItem.glyphs.mid(glyphIdx, glyphCount);
957 tmpItem.log_clusters = shaperItem.log_clusters + charIdx;
958 if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length,
959 &tmpItem.glyphs, &glyphCount, flags,
960 &tmpItem)) {
961 *nglyphs = glyphIdx + glyphCount;
962 return false;
963 }
964 for (int i = 0; i < charCount; ++i)
965 tmpItem.log_clusters[i] += glyphIdx;
966 glyphIdx += glyphCount;
967 charIdx += charCount;
968 } while (charIdx < len);
969 *nglyphs = glyphIdx;
970 glyphs->numGlyphs = glyphIdx;
971
972 return true;
973}
974
975bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,ShaperItem *shaperItem) const
976{
977 //qDebug() << "stringToCMap" << QString(str, len);
978
979 OSStatus e = noErr;
980
981 e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len);
982 if (e != noErr) {
983 qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__);
984 return false;
985 }
986
987 QGlyphLayoutInfo nfo;
988 nfo.glyphs = glyphs;
989 nfo.numGlyphs = nglyphs;
990 nfo.callbackCalled = false;
991 nfo.flags = flags;
992 nfo.shaperItem = shaperItem;
993
994 QVarLengthArray<int> mappedFonts(len);
995 for (int i = 0; i < len; ++i)
996 mappedFonts[i] = 0;
997 nfo.mappedFonts = mappedFonts.data();
998
999 Q_ASSERT(sizeof(void *) <= sizeof(URefCon));
1000 e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo);
1001 if (e != noErr) {
1002 qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__);
1003 return false;
1004 }
1005
1006 {
1007 const int maxAttributeCount = 3;
1008 ATSUAttributeTag tags[maxAttributeCount + 1];
1009 ByteCount sizes[maxAttributeCount + 1];
1010 ATSUAttributeValuePtr values[maxAttributeCount + 1];
1011 int attributeCount = 0;
1012
1013 tags[attributeCount] = kATSULineLayoutOptionsTag;
1014 ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment
1015 | kATSLineIgnoreFontLeading
1016 | kATSLineNoSpecialJustification // we do kashidas ourselves
1017 | kATSLineDisableAllJustification
1018 ;
1019
1020 if (!(flags & QTextEngine::DesignMetrics)) {
1021 layopts |= kATSLineFractDisable | kATSLineUseDeviceMetrics
1022 | kATSLineDisableAutoAdjustDisplayPos;
1023 }
1024
1025 if (fontDef.styleStrategy & QFont::NoAntialias)
1026 layopts |= kATSLineNoAntiAliasing;
1027
1028 if (!kerning)
1029 layopts |= kATSLineDisableAllKerningAdjustments;
1030
1031 values[attributeCount] = &layopts;
1032 sizes[attributeCount] = sizeof(layopts);
1033 ++attributeCount;
1034
1035 tags[attributeCount] = kATSULayoutOperationOverrideTag;
1036 ATSULayoutOperationOverrideSpecifier spec;
1037 spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
1038 spec.overrideUPP = atsuPostLayoutCallback;
1039 values[attributeCount] = &spec;
1040 sizes[attributeCount] = sizeof(spec);
1041 ++attributeCount;
1042
1043 // CTWritingDirection
1044 Boolean direction;
1045 if (flags & QTextEngine::RightToLeft)
1046 direction = kATSURightToLeftBaseDirection;
1047 else
1048 direction = kATSULeftToRightBaseDirection;
1049 tags[attributeCount] = kATSULineDirectionTag;
1050 values[attributeCount] = &direction;
1051 sizes[attributeCount] = sizeof(direction);
1052 ++attributeCount;
1053
1054 Q_ASSERT(attributeCount < maxAttributeCount + 1);
1055 e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values);
1056 if (e != noErr) {
1057 qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__);
1058 return false;
1059 }
1060
1061 }
1062
1063 e = ATSUSetRunStyle(textLayout, style, 0, len);
1064 if (e != noErr) {
1065 qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__);
1066 return false;
1067 }
1068
1069 if (!(fontDef.styleStrategy & QFont::NoFontMerging)) {
1070 int pos = 0;
1071 do {
1072 ATSUFontID substFont = 0;
1073 UniCharArrayOffset changedOffset = 0;
1074 UniCharCount changeCount = 0;
1075
1076 e = ATSUMatchFontsToText(textLayout, pos, len - pos,
1077 &substFont, &changedOffset,
1078 &changeCount);
1079 if (e == kATSUFontsMatched) {
1080 int fontIdx = fontIndexForFontID(substFont);
1081 for (uint i = 0; i < changeCount; ++i)
1082 mappedFonts[changedOffset + i] = fontIdx;
1083 pos = changedOffset + changeCount;
1084 ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount);
1085 } else if (e == kATSUFontsNotMatched) {
1086 pos = changedOffset + changeCount;
1087 }
1088 } while (pos < len && e != noErr);
1089 }
1090 { // trigger the a layout
1091 // CFAttributedStringCreate, CTFramesetterCreateWithAttributedString (or perhaps Typesetter)
1092 Rect rect;
1093 e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
1094 /*iLocationX =*/ 0, /*iLocationY =*/ 0,
1095 &rect);
1096 if (e != noErr) {