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

Last change on this file since 605 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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