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

Last change on this file since 219 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) {
1097 qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__);
1098 return false;
1099 }
1100 }
1101
1102 if (!nfo.callbackCalled) {
1103 qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__);
1104 return false;
1105 }
1106
1107 ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning);
1108 return true;
1109}
1110
1111void QFontEngineMacMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1112{
1113 Q_ASSERT(false);
1114 Q_UNUSED(glyphs);
1115 Q_UNUSED(flags);
1116}
1117
1118void QFontEngineMacMulti::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
1119{
1120 //Q_ASSERT(false);
1121}
1122
1123void QFontEngineMacMulti::loadEngine(int /*at*/)
1124{
1125 // should never be called!
1126 Q_ASSERT(false);
1127}
1128
1129bool QFontEngineMacMulti::canRender(const QChar *string, int len)
1130{
1131 ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len);
1132 ATSUSetRunStyle(textLayout, style, 0, len);
1133
1134 OSStatus e = noErr;
1135 int pos = 0;
1136 do {
1137 FMFont substFont = 0;
1138 UniCharArrayOffset changedOffset = 0;
1139 UniCharCount changeCount = 0;
1140
1141 // CTFontCreateForString
1142 e = ATSUMatchFontsToText(textLayout, pos, len - pos,
1143 &substFont, &changedOffset,
1144 &changeCount);
1145 if (e == kATSUFontsMatched) {
1146 pos = changedOffset + changeCount;
1147 } else if (e == kATSUFontsNotMatched) {
1148 break;
1149 }
1150 } while (pos < len && e != noErr);
1151
1152 return e == noErr || e == kATSUFontsMatched;
1153}
1154
1155QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine)
1156 : fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false)
1157{
1158 fontDef = def;
1159 ATSUCreateAndCopyStyle(baseStyle, &style);
1160 ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1161 cgFont = CGFontCreateWithPlatformFont(&atsFont);
1162
1163 const int maxAttributeCount = 4;
1164 ATSUAttributeTag tags[maxAttributeCount + 1];
1165 ByteCount sizes[maxAttributeCount + 1];
1166 ATSUAttributeValuePtr values[maxAttributeCount + 1];
1167 int attributeCount = 0;
1168
1169 synthesisFlags = 0;
1170
1171 // synthesizing using CG is not recommended
1172 quint16 macStyle = 0;
1173 {
1174 uchar data[4];
1175 ByteCount len = 4;
1176 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
1177 macStyle = qFromBigEndian<quint16>(data);
1178 }
1179
1180 Boolean atsuBold = false;
1181 Boolean atsuItalic = false;
1182 if (fontDef.weight >= QFont::Bold) {
1183 if (!(macStyle & 1)) {
1184 synthesisFlags |= SynthesizedBold;
1185 atsuBold = true;
1186 tags[attributeCount] = kATSUQDBoldfaceTag;
1187 sizes[attributeCount] = sizeof(atsuBold);
1188 values[attributeCount] = &atsuBold;
1189 ++attributeCount;
1190 }
1191 }
1192 if (fontDef.style != QFont::StyleNormal) {
1193 if (!(macStyle & 2)) {
1194 synthesisFlags |= SynthesizedItalic;
1195 atsuItalic = true;
1196 tags[attributeCount] = kATSUQDItalicTag;
1197 sizes[attributeCount] = sizeof(atsuItalic);
1198 values[attributeCount] = &atsuItalic;
1199 ++attributeCount;
1200 }
1201 }
1202
1203 tags[attributeCount] = kATSUFontTag;
1204 values[attributeCount] = &fontID;
1205 sizes[attributeCount] = sizeof(fontID);
1206 ++attributeCount;
1207
1208 Q_ASSERT(attributeCount < maxAttributeCount + 1);
1209 OSStatus err = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
1210 Q_ASSERT(err == noErr);
1211 Q_UNUSED(err);
1212
1213 // CTFontCopyTable
1214 quint16 tmpFsType;
1215 if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 8, 2, &tmpFsType, 0) == noErr)
1216 fsType = qFromBigEndian<quint16>(tmpFsType);
1217 else
1218 fsType = 0;
1219
1220 if (multiEngine)
1221 transform = multiEngine->transform;
1222 else
1223 transform = CGAffineTransformIdentity;
1224
1225 ATSUTextMeasurement metric;
1226
1227 ATSUGetAttribute(style, kATSUAscentTag, sizeof(metric), &metric, 0);
1228 m_ascent = FixRound(metric);
1229
1230 ATSUGetAttribute(style, kATSUDescentTag, sizeof(metric), &metric, 0);
1231 m_descent = FixRound(metric);
1232
1233 ATSUGetAttribute(style, kATSULeadingTag, sizeof(metric), &metric, 0);
1234 m_leading = FixRound(metric);
1235
1236 ATSFontMetrics metrics;
1237
1238 ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
1239 m_maxCharWidth = metrics.maxAdvanceWidth * fontDef.pointSize;
1240
1241 ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
1242 m_xHeight = QFixed::fromReal(metrics.xHeight * fontDef.pointSize);
1243
1244 ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
1245 m_averageCharWidth = QFixed::fromReal(metrics.avgAdvanceWidth * fontDef.pointSize);
1246
1247 // Use width of 'X' if ATSFontGetHorizontalMetrics returns 0 for avgAdvanceWidth.
1248 if (m_averageCharWidth == QFixed(0)) {
1249 QChar c('X');
1250 QGlyphLayoutArray<1> glyphs;
1251 int nglyphs = 1;
1252 stringToCMap(&c, 1, &glyphs, &nglyphs, 0);
1253 glyph_metrics_t metrics = boundingBox(glyphs);
1254 m_averageCharWidth = metrics.width;
1255 }
1256}
1257
1258QFontEngineMac::~QFontEngineMac()
1259{
1260 ATSUDisposeStyle(style);
1261}
1262
1263static inline unsigned int getChar(const QChar *str, int &i, const int len)
1264{
1265 unsigned int uc = str[i].unicode();
1266 if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
1267 uint low = str[i+1].unicode();
1268 if (low >= 0xdc00 && low < 0xe000) {
1269 uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
1270 ++i;
1271 }
1272 }
1273 return uc;
1274}
1275
1276bool QFontEngineMac::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
1277{
1278 if (!cmap) {
1279 cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
1280 int size = 0;
1281 cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &size);
1282 if (!cmap)
1283 return false;
1284 }
1285 if (symbolCMap) {
1286 for (int i = 0; i < len; ++i) {
1287 unsigned int uc = getChar(str, i, len);
1288 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
1289 if(!glyphs->glyphs[i] && uc < 0x100)
1290 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
1291 }
1292 } else {
1293 for (int i = 0; i < len; ++i) {
1294 unsigned int uc = getChar(str, i, len);
1295 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
1296 }
1297 }
1298
1299 *nglyphs = len;
1300 glyphs->numGlyphs = *nglyphs;
1301
1302 if (!(flags & QTextEngine::GlyphIndicesOnly))
1303 recalcAdvances(glyphs, flags);
1304
1305 return true;
1306}
1307
1308void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1309{
1310 Q_UNUSED(flags)
1311
1312 QVarLengthArray<GlyphID> atsuGlyphs(glyphs->numGlyphs);
1313 for (int i = 0; i < glyphs->numGlyphs; ++i)
1314 atsuGlyphs[i] = glyphs->glyphs[i];
1315
1316 QVarLengthArray<ATSGlyphScreenMetrics> metrics(glyphs->numGlyphs);
1317
1318 ATSUGlyphGetScreenMetrics(style, glyphs->numGlyphs, atsuGlyphs.data(), sizeof(GlyphID),
1319 /* iForcingAntiAlias =*/ false,
1320 /* iAntiAliasSwitch =*/true,
1321 metrics.data());
1322
1323 for (int i = 0; i < glyphs->numGlyphs; ++i) {
1324 glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x);
1325 glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y);
1326 }
1327}
1328
1329glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs)
1330{
1331 QFixed w;
1332 for (int i = 0; i < glyphs.numGlyphs; ++i)
1333 w += glyphs.effectiveAdvance(i);
1334 return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0);
1335}
1336
1337glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph)
1338{
1339 GlyphID atsuGlyph = glyph;
1340
1341 ATSGlyphScreenMetrics metrics;
1342
1343 ATSUGlyphGetScreenMetrics(style, 1, &atsuGlyph, 0,
1344 /* iForcingAntiAlias =*/ false,
1345 /* iAntiAliasSwitch =*/true,
1346 &metrics);
1347
1348 // ### check again
1349
1350 glyph_metrics_t gm;
1351 gm.width = int(metrics.width);
1352 gm.height = int(metrics.height);
1353 gm.x = QFixed::fromReal(metrics.topLeft.x);
1354 gm.y = -QFixed::fromReal(metrics.topLeft.y);
1355 gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x);
1356 gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y);
1357
1358 return gm;
1359}
1360
1361QFixed QFontEngineMac::ascent() const
1362{
1363 return m_ascent;
1364}
1365
1366QFixed QFontEngineMac::descent() const
1367{
1368 return m_descent;
1369}
1370
1371QFixed QFontEngineMac::leading() const
1372{
1373 return m_leading;
1374}
1375
1376qreal QFontEngineMac::maxCharWidth() const
1377{
1378 return m_maxCharWidth;
1379}
1380
1381QFixed QFontEngineMac::xHeight() const
1382{
1383 return m_xHeight;
1384}
1385
1386QFixed QFontEngineMac::averageCharWidth() const
1387{
1388 return m_averageCharWidth;
1389}
1390
1391static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path)
1392{
1393 if (!numGlyphs)
1394 return;
1395
1396 OSStatus e;
1397
1398 QMacFontPath fontpath(0, 0, path);
1399 ATSCubicMoveToUPP moveTo = NewATSCubicMoveToUPP(QMacFontPath::moveTo);
1400 ATSCubicLineToUPP lineTo = NewATSCubicLineToUPP(QMacFontPath::lineTo);
1401 ATSCubicCurveToUPP cubicTo = NewATSCubicCurveToUPP(QMacFontPath::cubicTo);
1402 ATSCubicClosePathUPP closePath = NewATSCubicClosePathUPP(QMacFontPath::closePath);
1403
1404 // CTFontCreatePathForGlyph
1405 for (int i = 0; i < numGlyphs; ++i) {
1406 GlyphID glyph = glyphs[i];
1407
1408 fontpath.setPosition(positions[i].x.toReal(), positions[i].y.toReal());
1409 ATSUGlyphGetCubicPaths(style, glyph, moveTo, lineTo,
1410 cubicTo, closePath, &fontpath, &e);
1411 }
1412
1413 DisposeATSCubicMoveToUPP(moveTo);
1414 DisposeATSCubicLineToUPP(lineTo);
1415 DisposeATSCubicCurveToUPP(cubicTo);
1416 DisposeATSCubicClosePathUPP(closePath);
1417}
1418
1419void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path,
1420 QTextItem::RenderFlags)
1421{
1422 addGlyphsToPathHelper(style, glyphs, positions, numGlyphs, path);
1423}
1424
1425QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph)
1426{
1427 const glyph_metrics_t br = boundingBox(glyph);
1428 QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32);
1429 im.fill(0);
1430
1431 CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
1432#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
1433 uint cgflags = kCGImageAlphaNoneSkipFirst;
1434#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
1435 if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
1436 cgflags |= kCGBitmapByteOrder32Host;
1437#endif
1438#else
1439 CGImageAlphaInfo cgflags = kCGImageAlphaNoneSkipFirst;
1440#endif
1441 CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
1442 8, im.bytesPerLine(), colorspace,
1443 cgflags);
1444 CGContextSetFontSize(ctx, fontDef.pixelSize);
1445 CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
1446 // turn off sub-pixel hinting - no support for that in OpenGL
1447 CGContextSetShouldSmoothFonts(ctx, false);
1448 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
1449 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
1450 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
1451
1452 if (synthesisFlags & QFontEngine::SynthesizedItalic)
1453 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
1454
1455 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
1456
1457 CGContextSetTextMatrix(ctx, cgMatrix);
1458 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
1459 CGContextSetTextDrawingMode(ctx, kCGTextFill);
1460 CGContextSetFont(ctx, cgFont);
1461
1462 qreal pos_x = -br.x.toReal() + 1;
1463 qreal pos_y = im.height() + br.y.toReal() - 2;
1464 CGContextSetTextPosition(ctx, pos_x, pos_y);
1465
1466 CGSize advance;
1467 advance.width = 0;
1468 advance.height = 0;
1469 CGGlyph cgGlyph = glyph;
1470 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
1471
1472 if (synthesisFlags & QFontEngine::SynthesizedBold) {
1473 CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
1474 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
1475 }
1476
1477 CGContextRelease(ctx);
1478
1479 QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
1480 QVector<QRgb> colors(256);
1481 for (int i=0; i<256; ++i)
1482 colors[i] = qRgba(0, 0, 0, i);
1483 indexed.setColorTable(colors);
1484
1485 for (int y=0; y<im.height(); ++y) {
1486 uint *src = (uint*) im.scanLine(y);
1487 uchar *dst = indexed.scanLine(y);
1488 for (int x=0; x<im.width(); ++x) {
1489 *dst = qGray(*src);
1490 ++dst;
1491 ++src;
1492 }
1493 }
1494
1495 return indexed;
1496}
1497
1498bool QFontEngineMac::canRender(const QChar *string, int len)
1499{
1500 Q_ASSERT(false);
1501 Q_UNUSED(string);
1502 Q_UNUSED(len);
1503 return false;
1504}
1505
1506void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
1507{
1508 QVarLengthArray<QFixedPoint> positions;
1509 QVarLengthArray<glyph_t> glyphs;
1510 QTransform matrix;
1511 matrix.translate(x, y);
1512 getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1513 if (glyphs.size() == 0)
1514 return;
1515
1516 CGContextSetFontSize(ctx, fontDef.pixelSize);
1517
1518 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
1519
1520 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
1521
1522 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
1523
1524 if (synthesisFlags & QFontEngine::SynthesizedItalic)
1525 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
1526
1527 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
1528
1529 CGContextSetTextMatrix(ctx, cgMatrix);
1530
1531 CGContextSetTextDrawingMode(ctx, kCGTextFill);
1532
1533
1534 QVarLengthArray<CGSize> advances(glyphs.size());
1535 QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
1536
1537 for (int i = 0; i < glyphs.size() - 1; ++i) {
1538 advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
1539 advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
1540 cgGlyphs[i] = glyphs[i];
1541 }
1542 advances[glyphs.size() - 1].width = 0;
1543 advances[glyphs.size() - 1].height = 0;
1544 cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
1545
1546 CGContextSetFont(ctx, cgFont);
1547
1548 CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
1549
1550 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
1551
1552 if (synthesisFlags & QFontEngine::SynthesizedBold) {
1553 CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
1554 positions[0].y.toReal());
1555
1556 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
1557 }
1558
1559 CGContextSetTextMatrix(ctx, oldTextMatrix);
1560}
1561
1562QFontEngine::FaceId QFontEngineMac::faceId() const
1563{
1564 FaceId ret;
1565#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
1566 // CTFontGetPlatformFont
1567 FSRef ref;
1568 if (ATSFontGetFileReference(FMGetATSFontRefFromFont(fontID), &ref) != noErr)
1569 return ret;
1570 ret.filename = QByteArray(128, 0);
1571 ret.index = fontID;
1572 FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
1573#else
1574 FSSpec spec;
1575 if (ATSFontGetFileSpecification(FMGetATSFontRefFromFont(fontID), &spec) != noErr)
1576 return ret;
1577
1578 FSRef ref;
1579 FSpMakeFSRef(&spec, &ref);
1580 ret.filename = QByteArray(128, 0);
1581 ret.index = fontID;
1582 FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
1583#endif
1584 return ret;
1585}
1586
1587QByteArray QFontEngineMac::getSfntTable(uint tag) const
1588{
1589 ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1590
1591 ByteCount length;
1592 OSStatus status = ATSFontGetTable(atsFont, tag, 0, 0, 0, &length);
1593 if (status != noErr)
1594 return QByteArray();
1595 QByteArray table(length, 0);
1596 // CTFontCopyTable
1597 status = ATSFontGetTable(atsFont, tag, 0, table.length(), table.data(), &length);
1598 if (status != noErr)
1599 return QByteArray();
1600 return table;
1601}
1602
1603QFontEngine::Properties QFontEngineMac::properties() const
1604{
1605 QFontEngine::Properties props;
1606 ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1607 quint16 tmp;
1608 // CTFontGetUnitsPerEm
1609 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 18, 2, &tmp, 0) == noErr)
1610 props.emSquare = qFromBigEndian<quint16>(tmp);
1611 struct {
1612 qint16 xMin;
1613 qint16 yMin;
1614 qint16 xMax;
1615 qint16 yMax;
1616 } bbox;
1617 bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
1618 // CTFontGetBoundingBox
1619 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 36, 8, &bbox, 0) == noErr) {
1620 bbox.xMin = qFromBigEndian<quint16>(bbox.xMin);
1621 bbox.yMin = qFromBigEndian<quint16>(bbox.yMin);
1622 bbox.xMax = qFromBigEndian<quint16>(bbox.xMax);
1623 bbox.yMax = qFromBigEndian<quint16>(bbox.yMax);
1624 }
1625 struct {
1626 qint16 ascender;
1627 qint16 descender;
1628 qint16 linegap;
1629 } metrics;
1630 metrics.ascender = metrics.descender = metrics.linegap = 0;
1631 // CTFontGetAscent, etc.
1632 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'h', 'e', 'a'), 4, 6, &metrics, 0) == noErr) {
1633 metrics.ascender = qFromBigEndian<quint16>(metrics.ascender);
1634 metrics.descender = qFromBigEndian<quint16>(metrics.descender);
1635 metrics.linegap = qFromBigEndian<quint16>(metrics.linegap);
1636 }
1637 props.ascent = metrics.ascender;
1638 props.descent = -metrics.descender;
1639 props.leading = metrics.linegap;
1640 props.boundingBox = QRectF(bbox.xMin, -bbox.yMax,
1641 bbox.xMax - bbox.xMin,
1642 bbox.yMax - bbox.yMin);
1643 props.italicAngle = 0;
1644 props.capHeight = props.ascent;
1645
1646 qint16 lw = 0;
1647 // fonts lie
1648 if (ATSFontGetTable(atsFont, MAKE_TAG('p', 'o', 's', 't'), 10, 2, &lw, 0) == noErr)
1649 lw = qFromBigEndian<quint16>(lw);
1650 props.lineWidth = lw;
1651
1652 // CTFontCopyPostScriptName
1653 QCFString psName;
1654 if (ATSFontGetPostScriptName(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &psName) == noErr)
1655 props.postscriptName = QString(psName).toUtf8();
1656 props.postscriptName = QPdf::stripSpecialCharacters(props.postscriptName);
1657 return props;
1658}
1659
1660void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1661{
1662 ATSUStyle unscaledStyle;
1663 ATSUCreateAndCopyStyle(style, &unscaledStyle);
1664
1665 int emSquare = properties().emSquare.toInt();
1666
1667 const int maxAttributeCount = 4;
1668 ATSUAttributeTag tags[maxAttributeCount + 1];
1669 ByteCount sizes[maxAttributeCount + 1];
1670 ATSUAttributeValuePtr values[maxAttributeCount + 1];
1671 int attributeCount = 0;
1672
1673 Fixed size = FixRatio(emSquare, 1);
1674 tags[attributeCount] = kATSUSizeTag;
1675 sizes[attributeCount] = sizeof(size);
1676 values[attributeCount] = &size;
1677 ++attributeCount;
1678
1679 Q_ASSERT(attributeCount < maxAttributeCount + 1);
1680 OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values);
1681 Q_ASSERT(err == noErr);
1682 Q_UNUSED(err);
1683
1684 // various CTFont metrics functions: CTFontGetBoundingRectsForGlyphs, CTFontGetAdvancesForGlyphs
1685 GlyphID atsuGlyph = glyph;
1686 ATSGlyphScreenMetrics atsuMetrics;
1687 ATSUGlyphGetScreenMetrics(unscaledStyle, 1, &atsuGlyph, 0,
1688 /* iForcingAntiAlias =*/ false,
1689 /* iAntiAliasSwitch =*/true,
1690 &atsuMetrics);
1691
1692 metrics->width = int(atsuMetrics.width);
1693 metrics->height = int(atsuMetrics.height);
1694 metrics->x = QFixed::fromReal(atsuMetrics.topLeft.x);
1695 metrics->y = -QFixed::fromReal(atsuMetrics.topLeft.y);
1696 metrics->xoff = QFixed::fromReal(atsuMetrics.deviceAdvance.x);
1697 metrics->yoff = QFixed::fromReal(atsuMetrics.deviceAdvance.y);
1698
1699 QFixedPoint p;
1700 addGlyphsToPathHelper(unscaledStyle, &glyph, &p, 1, path);
1701
1702 ATSUDisposeStyle(unscaledStyle);
1703}
1704#endif // !QT_MAC_USE_COCOA
1705
1706QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.