Fix uses of qRound on non-floating-point types.
[profile/ivi/qtbase.git] / src / gui / text / qfontengine_mac.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qfontengine_mac_p.h"
43
44 #include <private/qapplication_p.h>
45 #include <private/qfontengine_p.h>
46 #include <private/qpainter_p.h>
47 #include <private/qtextengine_p.h>
48 #include <qbitmap.h>
49 #include <private/qpaintengine_mac_p.h>
50 #include <private/qprintengine_mac_p.h>
51 #include <qglobal.h>
52 #include <qpixmap.h>
53 #include <qpixmapcache.h>
54 #include <qvarlengtharray.h>
55 #include <qdebug.h>
56 #include <qendian.h>
57 #include <qmath.h>
58 #include <private/qimage_p.h>
59
60 #include <ApplicationServices/ApplicationServices.h>
61 #include <AppKit/AppKit.h>
62
63 QT_BEGIN_NAMESPACE
64
65 /*****************************************************************************
66   QFontEngine debug facilities
67  *****************************************************************************/
68 //#define DEBUG_ADVANCES
69
70 extern int qt_antialiasing_threshold; // QApplication.cpp
71
72 #ifndef FixedToQFixed
73 #define FixedToQFixed(a) QFixed::fromFixed((a) >> 10)
74 #define QFixedToFixed(x) ((x).value() << 10)
75 #endif
76
77 class QMacFontPath
78 {
79     float x, y;
80     QPainterPath *path;
81 public:
82     inline QMacFontPath(float _x, float _y, QPainterPath *_path) : x(_x), y(_y), path(_path) { }
83     inline void setPosition(float _x, float _y) { x = _x; y = _y; }
84     inline void advance(float _x) { x += _x; }
85     static OSStatus lineTo(const Float32Point *, void *);
86     static OSStatus cubicTo(const Float32Point *, const Float32Point *,
87                             const Float32Point *, void *);
88     static OSStatus moveTo(const Float32Point *, void *);
89     static OSStatus closePath(void *);
90 };
91
92 OSStatus QMacFontPath::lineTo(const Float32Point *pt, void *data)
93
94 {
95     QMacFontPath *p = static_cast<QMacFontPath*>(data);
96     p->path->lineTo(p->x + pt->x, p->y + pt->y);
97     return noErr;
98 }
99
100 OSStatus QMacFontPath::cubicTo(const Float32Point *cp1, const Float32Point *cp2,
101                                const Float32Point *ep, void *data)
102
103 {
104     QMacFontPath *p = static_cast<QMacFontPath*>(data);
105     p->path->cubicTo(p->x + cp1->x, p->y + cp1->y,
106                      p->x + cp2->x, p->y + cp2->y,
107                      p->x + ep->x, p->y + ep->y);
108     return noErr;
109 }
110
111 OSStatus QMacFontPath::moveTo(const Float32Point *pt, void *data)
112 {
113     QMacFontPath *p = static_cast<QMacFontPath*>(data);
114     p->path->moveTo(p->x + pt->x, p->y + pt->y);
115     return noErr;
116 }
117
118 OSStatus QMacFontPath::closePath(void *data)
119 {
120     static_cast<QMacFontPath*>(data)->path->closeSubpath();
121     return noErr;
122 }
123
124
125 #ifndef QT_MAC_USE_COCOA
126 QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
127     : QFontEngineMulti(0)
128 {
129     this->fontDef = fontDef;
130     this->kerning = kerning;
131
132     // hopefully (CTFontCreateWithName or CTFontCreateWithFontDescriptor) + CTFontCreateCopyWithSymbolicTraits
133     // (or CTFontCreateWithQuickdrawInstance)
134     FMFontFamily fmFamily;
135     FMFontStyle fntStyle = 0;
136     fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
137     if (fmFamily == kInvalidFontFamily) {
138         // Use the ATSFont then...
139         fontID = FMGetFontFromATSFontRef(atsFontRef);
140     } else {
141         if (fontDef.weight >= QFont::Bold)
142             fntStyle |= ::bold;
143         if (fontDef.style != QFont::StyleNormal)
144             fntStyle |= ::italic;
145
146         FMFontStyle intrinsicStyle;
147         FMFont fnt = 0;
148         if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
149            fontID = FMGetATSFontRefFromFont(fnt);
150     }
151
152     // CFDictionaryRef, <CTStringAttributes.h>
153     OSStatus status;
154
155     status = ATSUCreateTextLayout(&textLayout);
156     Q_ASSERT(status == noErr);
157
158     const int maxAttributeCount = 5;
159     ATSUAttributeTag tags[maxAttributeCount + 1];
160     ByteCount sizes[maxAttributeCount + 1];
161     ATSUAttributeValuePtr values[maxAttributeCount + 1];
162     int attributeCount = 0;
163
164     Fixed size = FixRatio(fontDef.pixelSize, 1);
165     tags[attributeCount] = kATSUSizeTag;
166     sizes[attributeCount] = sizeof(size);
167     values[attributeCount] = &size;
168     ++attributeCount;
169
170     tags[attributeCount] = kATSUFontTag;
171     sizes[attributeCount] = sizeof(fontID);
172     values[attributeCount] = &this->fontID;
173     ++attributeCount;
174
175     transform = CGAffineTransformIdentity;
176     if (fontDef.stretch != 100) {
177         transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
178         tags[attributeCount] = kATSUFontMatrixTag;
179         sizes[attributeCount] = sizeof(transform);
180         values[attributeCount] = &transform;
181         ++attributeCount;
182     }
183
184     status = ATSUCreateStyle(&style);
185     Q_ASSERT(status == noErr);
186
187     Q_ASSERT(attributeCount < maxAttributeCount + 1);
188     status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
189     Q_ASSERT(status == noErr);
190
191     QFontEngineMac *fe = new QFontEngineMac(style, fontID, fontDef, this);
192     fe->ref.ref();
193     engines.append(fe);
194 }
195
196 QFontEngineMacMulti::~QFontEngineMacMulti()
197 {
198     ATSUDisposeTextLayout(textLayout);
199     ATSUDisposeStyle(style);
200
201     for (int i = 0; i < engines.count(); ++i) {
202         QFontEngineMac *fe = const_cast<QFontEngineMac *>(static_cast<const QFontEngineMac *>(engines.at(i)));
203         fe->multiEngine = 0;
204         if (!fe->ref.deref())
205             delete fe;
206     }
207     engines.clear();
208 }
209
210 struct QGlyphLayoutInfo
211 {
212     QGlyphLayout *glyphs;
213     int *numGlyphs;
214     bool callbackCalled;
215     int *mappedFonts;
216     QTextEngine::ShaperFlags flags;
217     QFontEngineMacMulti::ShaperItem *shaperItem;
218     unsigned int styleStrategy;
219 };
220
221 static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon,
222                                  void *operationExtraParameter, ATSULayoutOperationCallbackStatus *callbackStatus)
223 {
224     Q_UNUSED(selector);
225     Q_UNUSED(operationExtraParameter);
226
227     QGlyphLayoutInfo *nfo = reinterpret_cast<QGlyphLayoutInfo *>(refCon);
228     nfo->callbackCalled = true;
229
230     ATSLayoutRecord *layoutData = 0;
231     ItemCount itemCount = 0;
232
233     OSStatus e = noErr;
234     e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
235                                                    /*iCreate =*/ false,
236                                                    (void **) &layoutData,
237                                                    &itemCount);
238     if (e != noErr)
239         return e;
240
241     *nfo->numGlyphs = itemCount - 1;
242
243     Fixed *baselineDeltas = 0;
244
245     e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
246                                                    /*iCreate =*/ true,
247                                                    (void **) &baselineDeltas,
248                                                    &itemCount);
249     if (e != noErr)
250         return e;
251
252     int nextCharStop = -1;
253     int currentClusterGlyph = -1; // first glyph in log cluster
254     QFontEngineMacMulti::ShaperItem *item = nfo->shaperItem;
255     if (item->charAttributes) {
256         item = nfo->shaperItem;
257 #if !defined(QT_NO_DEBUG)
258         int surrogates = 0;
259         const QChar *str = item->string;
260         for (int i = item->from; i < item->from + item->length - 1; ++i) {
261             surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00
262                            && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
263         }
264 #endif
265         for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop)
266             if (item->charAttributes[nextCharStop].charStop)
267                 break;
268         nextCharStop -= item->from;
269     }
270
271     nfo->glyphs->attributes[0].clusterStart = true;
272     int glyphIdx = 0;
273     int glyphIncrement = 1;
274     if (nfo->flags & QTextEngine::RightToLeft) {
275         glyphIdx  = itemCount - 2;
276         glyphIncrement = -1;
277     }
278     for (int i = 0; i < *nfo->numGlyphs; ++i, glyphIdx += glyphIncrement) {
279
280         int charOffset = layoutData[glyphIdx].originalOffset / sizeof(UniChar);
281         const int fontIdx = nfo->mappedFonts[charOffset];
282
283         ATSGlyphRef glyphId = layoutData[glyphIdx].glyphID;
284
285         QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]);
286         QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos);
287
288         if (nfo->styleStrategy & QFont::ForceIntegerMetrics) {
289             yAdvance = yAdvance.round();
290             xAdvance = xAdvance.round();
291         }
292
293         if (glyphId != 0xffff || i == 0) {
294             if (i < nfo->glyphs->numGlyphs)
295             {
296                 nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24);
297
298                 nfo->glyphs->advances_y[i] = yAdvance;
299                 nfo->glyphs->advances_x[i] = xAdvance;
300             }
301         } else {
302             // ATSUI gives us 0xffff as glyph id at the index in the glyph array for
303             // a character position that maps to a ligtature. Such a glyph id does not
304             // result in any visual glyph, but it may have an advance, which is why we
305             // sum up the glyph advances.
306             --i;
307             nfo->glyphs->advances_y[i] += yAdvance;
308             nfo->glyphs->advances_x[i] += xAdvance;
309             *nfo->numGlyphs -= 1;
310         }
311
312         if (item->log_clusters) {
313             if (charOffset >= nextCharStop) {
314                 nfo->glyphs->attributes[i].clusterStart = true;
315                 currentClusterGlyph = i;
316
317                 ++nextCharStop;
318                 for (; nextCharStop < item->length; ++nextCharStop)
319                     if (item->charAttributes[item->from + nextCharStop].charStop)
320                         break;
321             } else {
322                 if (currentClusterGlyph == -1)
323                     currentClusterGlyph = i;
324             }
325             item->log_clusters[charOffset] = currentClusterGlyph;
326
327             // surrogate handling
328             if (charOffset < item->length - 1) {
329                 QChar current = item->string[item->from + charOffset];
330                 QChar next = item->string[item->from + charOffset + 1];
331                 if (current.unicode() >= 0xd800 && current.unicode() < 0xdc00
332                     && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) {
333                     item->log_clusters[charOffset + 1] = currentClusterGlyph;
334                 }
335             }
336         }
337     }
338
339     /*
340     if (item) {
341         qDebug() << "resulting logclusters:";
342         for (int i = 0; i < item->length; ++i)
343             qDebug() << "logClusters[" << i << "] =" << item->log_clusters[i];
344         qDebug() << "clusterstarts:";
345         for (int i = 0; i < *nfo->numGlyphs; ++i)
346             qDebug() << "clusterStart[" << i << "] =" << nfo->glyphs[i].attributes.clusterStart;
347     }
348     */
349
350     ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
351                                         (void **) &baselineDeltas);
352
353     ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
354                                         (void **) &layoutData);
355
356     *callbackStatus = kATSULayoutOperationCallbackStatusHandled;
357     return noErr;
358 }
359
360 int QFontEngineMacMulti::fontIndexForFontID(ATSUFontID id) const
361 {
362     for (int i = 0; i < engines.count(); ++i) {
363         if (engineAt(i)->fontID == id)
364             return i;
365     }
366
367     QFontEngineMacMulti *that = const_cast<QFontEngineMacMulti *>(this);
368     QFontEngineMac *fe = new QFontEngineMac(style, id, fontDef, that);
369     fe->ref.ref();
370     that->engines.append(fe);
371     return engines.count() - 1;
372 }
373
374 bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
375 {
376     return stringToCMap(str, len, glyphs, nglyphs, flags, /*logClusters=*/0, /*charAttributes=*/0);
377 }
378
379 bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
380                                        unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const
381 {
382     if (*nglyphs < len) {
383         *nglyphs = len;
384         return false;
385     }
386
387     ShaperItem shaperItem;
388     shaperItem.string = str;
389     shaperItem.from = 0;
390     shaperItem.length = len;
391     shaperItem.glyphs = *glyphs;
392     shaperItem.glyphs.numGlyphs = *nglyphs;
393     shaperItem.flags = flags;
394     shaperItem.log_clusters = logClusters;
395     shaperItem.charAttributes = charAttributes;
396
397     const int maxChars = qMax(1,
398                               int(SHRT_MAX / maxCharWidth())
399                               - 10 // subtract a few to be on the safe side
400                              );
401     if (len < maxChars || !charAttributes)
402         return stringToCMapInternal(str, len, glyphs, nglyphs, flags, &shaperItem);
403
404     int charIdx = 0;
405     int glyphIdx = 0;
406     ShaperItem tmpItem = shaperItem;
407
408     do {
409         tmpItem.from = shaperItem.from + charIdx;
410
411         int charCount = qMin(maxChars, len - charIdx);
412
413         int lastWhitespace = tmpItem.from + charCount - 1;
414         int lastSoftBreak = lastWhitespace;
415         int lastCharStop = lastSoftBreak;
416         for (int i = lastCharStop; i >= tmpItem.from; --i) {
417             if (tmpItem.charAttributes[i].whiteSpace) {
418                 lastWhitespace = i;
419                 break;
420             } if (tmpItem.charAttributes[i].lineBreakType != HB_NoBreak) {
421                 lastSoftBreak = i;
422             } if (tmpItem.charAttributes[i].charStop) {
423                 lastCharStop = i;
424             }
425         }
426         charCount = qMin(lastWhitespace, qMin(lastSoftBreak, lastCharStop)) - tmpItem.from + 1;
427
428         int glyphCount = shaperItem.glyphs.numGlyphs - glyphIdx;
429         if (glyphCount <= 0)
430             return false;
431         tmpItem.length = charCount;
432         tmpItem.glyphs = shaperItem.glyphs.mid(glyphIdx, glyphCount);
433         tmpItem.log_clusters = shaperItem.log_clusters + charIdx;
434         if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length,
435                                   &tmpItem.glyphs, &glyphCount, flags,
436                                   &tmpItem)) {
437             *nglyphs = glyphIdx + glyphCount;
438             return false;
439         }
440         for (int i = 0; i < charCount; ++i)
441             tmpItem.log_clusters[i] += glyphIdx;
442         glyphIdx += glyphCount;
443         charIdx += charCount;
444     } while (charIdx < len);
445     *nglyphs = glyphIdx;
446     glyphs->numGlyphs = glyphIdx;
447
448     return true;
449 }
450
451 bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,ShaperItem *shaperItem) const
452 {
453     //qDebug() << "stringToCMap" << QString(str, len);
454
455     OSStatus e = noErr;
456
457     e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len);
458     if (e != noErr) {
459         qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__);
460         return false;
461     }
462
463     QGlyphLayoutInfo nfo;
464     nfo.glyphs = glyphs;
465     nfo.numGlyphs = nglyphs;
466     nfo.callbackCalled = false;
467     nfo.flags = flags;
468     nfo.shaperItem = shaperItem;
469     nfo.styleStrategy = fontDef.styleStrategy;
470
471     int prevNumGlyphs = *nglyphs;
472
473     QVarLengthArray<int> mappedFonts(len);
474     for (int i = 0; i < len; ++i)
475         mappedFonts[i] = 0;
476     nfo.mappedFonts = mappedFonts.data();
477
478     Q_ASSERT(sizeof(void *) <= sizeof(URefCon));
479     e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo);
480     if (e != noErr) {
481         qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__);
482         return false;
483     }
484
485     {
486         const int maxAttributeCount = 3;
487         ATSUAttributeTag tags[maxAttributeCount + 1];
488         ByteCount sizes[maxAttributeCount + 1];
489         ATSUAttributeValuePtr values[maxAttributeCount + 1];
490         int attributeCount = 0;
491
492         tags[attributeCount] = kATSULineLayoutOptionsTag;
493         ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment
494                                        | kATSLineIgnoreFontLeading
495                                        | kATSLineNoSpecialJustification // we do kashidas ourselves
496                                        | kATSLineDisableAllJustification
497                                        ;
498
499         if (fontDef.styleStrategy & QFont::NoAntialias)
500             layopts |= kATSLineNoAntiAliasing;
501
502         if (!kerning)
503             layopts |= kATSLineDisableAllKerningAdjustments;
504
505         values[attributeCount] = &layopts;
506         sizes[attributeCount] = sizeof(layopts);
507         ++attributeCount;
508
509         tags[attributeCount] = kATSULayoutOperationOverrideTag;
510         ATSULayoutOperationOverrideSpecifier spec;
511         spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
512         spec.overrideUPP = atsuPostLayoutCallback;
513         values[attributeCount] = &spec;
514         sizes[attributeCount] = sizeof(spec);
515         ++attributeCount;
516
517         // CTWritingDirection
518         Boolean direction;
519         if (flags & QTextEngine::RightToLeft)
520             direction = kATSURightToLeftBaseDirection;
521         else
522             direction = kATSULeftToRightBaseDirection;
523         tags[attributeCount] = kATSULineDirectionTag;
524         values[attributeCount] = &direction;
525         sizes[attributeCount] = sizeof(direction);
526         ++attributeCount;
527
528         Q_ASSERT(attributeCount < maxAttributeCount + 1);
529         e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values);
530         if (e != noErr) {
531             qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__);
532             return false;
533         }
534
535     }
536
537     e = ATSUSetRunStyle(textLayout, style, 0, len);
538     if (e != noErr) {
539         qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__);
540         return false;
541     }
542
543     if (!(fontDef.styleStrategy & QFont::NoFontMerging)) {
544         int pos = 0;
545         do {
546             ATSUFontID substFont = 0;
547             UniCharArrayOffset changedOffset = 0;
548             UniCharCount changeCount = 0;
549
550             e = ATSUMatchFontsToText(textLayout, pos, len - pos,
551                                      &substFont, &changedOffset,
552                                      &changeCount);
553             if (e == kATSUFontsMatched) {
554                 int fontIdx = fontIndexForFontID(substFont);
555                 for (uint i = 0; i < changeCount; ++i)
556                     mappedFonts[changedOffset + i] = fontIdx;
557                 pos = changedOffset + changeCount;
558                 ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount);
559             } else if (e == kATSUFontsNotMatched) {
560                 pos = changedOffset + changeCount;
561             }
562         } while (pos < len && e != noErr);
563     }
564     {    // trigger the a layout
565         // CFAttributedStringCreate, CTFramesetterCreateWithAttributedString (or perhaps Typesetter)
566         Rect rect;
567         e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
568                                  /*iLocationX =*/ 0, /*iLocationY =*/ 0,
569                                  &rect);
570         if (e != noErr) {
571             qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__);
572             return false;
573         }
574     }
575
576     if (!nfo.callbackCalled) {
577             qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__);
578             return false;
579     }
580
581     ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning);
582     if (prevNumGlyphs < *nfo.numGlyphs)
583         return false;
584     return true;
585 }
586
587 void QFontEngineMacMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
588 {
589     Q_ASSERT(false);
590     Q_UNUSED(glyphs);
591     Q_UNUSED(flags);
592 }
593
594 void QFontEngineMacMulti::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
595 {
596     //Q_ASSERT(false);
597 }
598
599 void QFontEngineMacMulti::loadEngine(int /*at*/)
600 {
601     // should never be called!
602     Q_ASSERT(false);
603 }
604
605 bool QFontEngineMacMulti::canRender(const QChar *string, int len)
606 {
607     ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len);
608     ATSUSetRunStyle(textLayout, style, 0, len);
609
610     OSStatus e = noErr;
611     int pos = 0;
612     do {
613         FMFont substFont = 0;
614         UniCharArrayOffset changedOffset = 0;
615         UniCharCount changeCount = 0;
616
617         // CTFontCreateForString
618         e = ATSUMatchFontsToText(textLayout, pos, len - pos,
619                                  &substFont, &changedOffset,
620                                  &changeCount);
621         if (e == kATSUFontsMatched) {
622             pos = changedOffset + changeCount;
623         } else if (e == kATSUFontsNotMatched) {
624             break;
625         }
626     } while (pos < len && e != noErr);
627
628     return e == noErr || e == kATSUFontsMatched;
629 }
630
631 QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine)
632     : fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false)
633 {
634     fontDef = def;
635     ATSUCreateAndCopyStyle(baseStyle, &style);
636     ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
637     cgFont = CGFontCreateWithPlatformFont(&atsFont);
638
639     const int maxAttributeCount = 4;
640     ATSUAttributeTag tags[maxAttributeCount + 1];
641     ByteCount sizes[maxAttributeCount + 1];
642     ATSUAttributeValuePtr values[maxAttributeCount + 1];
643     int attributeCount = 0;
644
645     synthesisFlags = 0;
646
647     // synthesizing using CG is not recommended
648     quint16 macStyle = 0;
649     {
650         uchar data[4];
651         ByteCount len = 4;
652         if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
653             macStyle = qFromBigEndian<quint16>(data);
654     }
655
656     Boolean atsuBold = false;
657     Boolean atsuItalic = false;
658     if (fontDef.weight >= QFont::Bold) {
659         if (!(macStyle & 1)) {
660             synthesisFlags |= SynthesizedBold;
661             atsuBold = true;
662             tags[attributeCount] = kATSUQDBoldfaceTag;
663             sizes[attributeCount] = sizeof(atsuBold);
664             values[attributeCount] = &atsuBold;
665             ++attributeCount;
666         }
667     }
668     if (fontDef.style != QFont::StyleNormal) {
669         if (!(macStyle & 2)) {
670             synthesisFlags |= SynthesizedItalic;
671             atsuItalic = true;
672             tags[attributeCount] = kATSUQDItalicTag;
673             sizes[attributeCount] = sizeof(atsuItalic);
674             values[attributeCount] = &atsuItalic;
675             ++attributeCount;
676         }
677     }
678
679     tags[attributeCount] = kATSUFontTag;
680     values[attributeCount] = &fontID;
681     sizes[attributeCount] = sizeof(fontID);
682     ++attributeCount;
683
684     Q_ASSERT(attributeCount < maxAttributeCount + 1);
685     OSStatus err = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
686     Q_ASSERT(err == noErr);
687     Q_UNUSED(err);
688
689     // CTFontCopyTable
690     quint16 tmpFsType;
691     if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 8, 2, &tmpFsType, 0) == noErr)
692        fsType = qFromBigEndian<quint16>(tmpFsType);
693     else
694         fsType = 0;
695
696     if (multiEngine)
697         transform = multiEngine->transform;
698     else
699         transform = CGAffineTransformIdentity;
700
701     ATSUTextMeasurement metric;
702
703     ATSUGetAttribute(style, kATSUAscentTag, sizeof(metric), &metric, 0);
704     m_ascent = FixRound(metric);
705
706     ATSUGetAttribute(style, kATSUDescentTag, sizeof(metric), &metric, 0);
707     m_descent = FixRound(metric);
708
709     ATSUGetAttribute(style, kATSULeadingTag, sizeof(metric), &metric, 0);
710     m_leading = FixRound(metric);
711
712     ATSFontMetrics metrics;
713
714     ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
715     m_maxCharWidth = metrics.maxAdvanceWidth * fontDef.pointSize;
716
717     ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
718     m_xHeight = QFixed::fromReal(metrics.xHeight * fontDef.pointSize);
719
720     ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
721     m_averageCharWidth = QFixed::fromReal(metrics.avgAdvanceWidth * fontDef.pointSize);
722
723     // Use width of 'X' if ATSFontGetHorizontalMetrics returns 0 for avgAdvanceWidth.
724     if (m_averageCharWidth == QFixed(0)) {
725         QChar c('X');
726         QGlyphLayoutArray<1> glyphs;
727         int nglyphs = 1;
728         stringToCMap(&c, 1, &glyphs, &nglyphs, 0);
729         glyph_metrics_t metrics = boundingBox(glyphs);
730         m_averageCharWidth =  metrics.width;
731     }
732 }
733
734 QFontEngineMac::~QFontEngineMac()
735 {
736     ATSUDisposeStyle(style);
737 }
738
739 static inline unsigned int getChar(const QChar *str, int &i, const int len)
740 {
741     unsigned int uc = str[i].unicode();
742     if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
743         uint low = str[i+1].unicode();
744        if (low >= 0xdc00 && low < 0xe000) {
745             uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
746             ++i;
747         }
748     }
749     return uc;
750 }
751
752 // Not used directly for shaping, only used to calculate m_averageCharWidth
753 bool QFontEngineMac::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
754 {
755     if (!cmap) {
756         cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
757         int size = 0;
758         cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &size);
759         if (!cmap)
760             return false;
761     }
762     if (symbolCMap) {
763         for (int i = 0; i < len; ++i) {
764             unsigned int uc = getChar(str, i, len);
765             glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
766             if(!glyphs->glyphs[i] && uc < 0x100)
767                 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
768         }
769     } else {
770         for (int i = 0; i < len; ++i) {
771             unsigned int uc = getChar(str, i, len);
772             glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
773         }
774     }
775
776     *nglyphs = len;
777     glyphs->numGlyphs = *nglyphs;
778
779     if (!(flags & QTextEngine::GlyphIndicesOnly))
780         recalcAdvances(glyphs, flags);
781
782     return true;
783 }
784
785 void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
786 {
787     Q_UNUSED(flags)
788
789     QVarLengthArray<GlyphID> atsuGlyphs(glyphs->numGlyphs);
790     for (int i = 0; i < glyphs->numGlyphs; ++i)
791         atsuGlyphs[i] = glyphs->glyphs[i];
792
793     QVarLengthArray<ATSGlyphScreenMetrics> metrics(glyphs->numGlyphs);
794
795     ATSUGlyphGetScreenMetrics(style, glyphs->numGlyphs, atsuGlyphs.data(), sizeof(GlyphID),
796                               /* iForcingAntiAlias =*/ false,
797                               /* iAntiAliasSwitch =*/true,
798                               metrics.data());
799
800     for (int i = 0; i < glyphs->numGlyphs; ++i) {
801         glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x);
802         glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y);
803
804         if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
805             glyphs->advances_x[i] = glyphs->advances_x[i].round();
806             glyphs->advances_y[i] = glyphs->advances_y[i].round();
807         }
808     }
809 }
810
811 glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs)
812 {
813     QFixed w;
814     bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
815     for (int i = 0; i < glyphs.numGlyphs; ++i) {
816         w += round ? glyphs.effectiveAdvance(i).round()
817                    : glyphs.effectiveAdvance(i);
818     }
819     return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0);
820 }
821
822 glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph)
823 {
824     GlyphID atsuGlyph = glyph;
825
826     ATSGlyphScreenMetrics metrics;
827
828     ATSUGlyphGetScreenMetrics(style, 1, &atsuGlyph, 0,
829                               /* iForcingAntiAlias =*/ false,
830                               /* iAntiAliasSwitch =*/true,
831                               &metrics);
832
833     // ### check again
834
835     glyph_metrics_t gm;
836     gm.width = int(metrics.width);
837     gm.height = int(metrics.height);
838     gm.x = QFixed::fromReal(metrics.topLeft.x);
839     gm.y = -QFixed::fromReal(metrics.topLeft.y);
840     gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x);
841     gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y);
842
843     if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
844         gm.x = gm.x.floor();
845         gm.y = gm.y.floor();
846         gm.xoff = gm.xoff.round();
847         gm.yoff = gm.yoff.round();
848     }
849
850     return gm;
851 }
852
853 QFixed QFontEngineMac::ascent() const
854 {
855     return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
856             ? m_ascent.round()
857             : m_ascent;
858 }
859
860 QFixed QFontEngineMac::descent() const
861 {
862     // subtract a pixel to even out the historical +1 in QFontMetrics::height().
863     // Fix in Qt 5.
864     return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
865             ? m_descent.round() - 1
866             : m_descent;
867 }
868
869 QFixed QFontEngineMac::leading() const
870 {
871     return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
872             ? m_leading.round()
873             : m_leading;
874 }
875
876 qreal QFontEngineMac::maxCharWidth() const
877 {
878     return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
879             ? qRound(m_maxCharWidth)
880             : m_maxCharWidth;
881 }
882
883 QFixed QFontEngineMac::xHeight() const
884 {
885     return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
886             ? m_xHeight.round()
887             : m_xHeight;
888 }
889
890 QFixed QFontEngineMac::averageCharWidth() const
891 {
892     return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
893             ? m_averageCharWidth.round()
894             : m_averageCharWidth;
895 }
896
897 static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path)
898 {
899     if (!numGlyphs)
900         return;
901
902     OSStatus e;
903
904     QMacFontPath fontpath(0, 0, path);
905     ATSCubicMoveToUPP moveTo = NewATSCubicMoveToUPP(QMacFontPath::moveTo);
906     ATSCubicLineToUPP lineTo = NewATSCubicLineToUPP(QMacFontPath::lineTo);
907     ATSCubicCurveToUPP cubicTo = NewATSCubicCurveToUPP(QMacFontPath::cubicTo);
908     ATSCubicClosePathUPP closePath = NewATSCubicClosePathUPP(QMacFontPath::closePath);
909
910     // CTFontCreatePathForGlyph
911     for (int i = 0; i < numGlyphs; ++i) {
912         GlyphID glyph = glyphs[i];
913
914         fontpath.setPosition(positions[i].x.toReal(), positions[i].y.toReal());
915         ATSUGlyphGetCubicPaths(style, glyph, moveTo, lineTo,
916                                cubicTo, closePath, &fontpath, &e);
917     }
918
919     DisposeATSCubicMoveToUPP(moveTo);
920     DisposeATSCubicLineToUPP(lineTo);
921     DisposeATSCubicCurveToUPP(cubicTo);
922     DisposeATSCubicClosePathUPP(closePath);
923 }
924
925 void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path,
926                                            QTextItem::RenderFlags)
927 {
928     addGlyphsToPathHelper(style, glyphs, positions, numGlyphs, path);
929 }
930
931
932 /*!
933   Helper function for alphaMapForGlyph and alphaRGBMapForGlyph. The two are identical, except for
934   the subpixel antialiasing...
935 */
936 QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful)
937 {
938     const glyph_metrics_t br = boundingBox(glyph);
939     QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32);
940     im.fill(0xff000000);
941
942     CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
943     uint cgflags = kCGImageAlphaNoneSkipFirst;
944 #ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
945     cgflags |= kCGBitmapByteOrder32Host;
946 #endif
947     CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
948                                              8, im.bytesPerLine(), colorspace,
949                                              cgflags);
950     CGContextSetFontSize(ctx, fontDef.pixelSize);
951     CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
952     // turn off sub-pixel hinting - no support for that in OpenGL
953     CGContextSetShouldSmoothFonts(ctx, colorful);
954     CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
955     CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
956     CGAffineTransformConcat(cgMatrix, oldTextMatrix);
957
958     if (synthesisFlags & QFontEngine::SynthesizedItalic)
959         cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
960
961     cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
962
963     CGContextSetTextMatrix(ctx, cgMatrix);
964     CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
965     CGContextSetTextDrawingMode(ctx, kCGTextFill);
966     CGContextSetFont(ctx, cgFont);
967
968     qreal pos_x = -br.x.toReal() + 1;
969     qreal pos_y = im.height() + br.y.toReal() - 2;
970     CGContextSetTextPosition(ctx, pos_x, pos_y);
971
972     CGSize advance;
973     advance.width = 0;
974     advance.height = 0;
975     CGGlyph cgGlyph = glyph;
976     CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
977
978     if (synthesisFlags & QFontEngine::SynthesizedBold) {
979         CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
980         CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
981     }
982
983     CGContextRelease(ctx);
984
985     return im;
986 }
987
988 QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph)
989 {
990     QImage im = imageForGlyph(glyph, 2, false);
991
992     QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
993     QVector<QRgb> colors(256);
994     for (int i=0; i<256; ++i)
995         colors[i] = qRgba(0, 0, 0, i);
996     indexed.setColorTable(colors);
997
998     for (int y=0; y<im.height(); ++y) {
999         uint *src = (uint*) im.scanLine(y);
1000         uchar *dst = indexed.scanLine(y);
1001         for (int x=0; x<im.width(); ++x) {
1002             *dst = qGray(*src);
1003             ++dst;
1004             ++src;
1005         }
1006     }
1007
1008     return indexed;
1009 }
1010
1011 QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t)
1012 {
1013     QImage im = imageForGlyph(glyph, margin, true);
1014
1015     if (t.type() >= QTransform::TxScale) {
1016         im = im.transformed(t);
1017     }
1018
1019     qGamma_correct_back_to_linear_cs(&im);
1020
1021     return im;
1022 }
1023
1024
1025 bool QFontEngineMac::canRender(const QChar *string, int len)
1026 {
1027     Q_ASSERT(false);
1028     Q_UNUSED(string);
1029     Q_UNUSED(len);
1030     return false;
1031 }
1032
1033 void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
1034 {
1035     QVarLengthArray<QFixedPoint> positions;
1036     QVarLengthArray<glyph_t> glyphs;
1037     QTransform matrix;
1038     matrix.translate(x, y);
1039     getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1040     if (glyphs.size() == 0)
1041         return;
1042
1043     CGContextSetFontSize(ctx, fontDef.pixelSize);
1044
1045     CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
1046
1047     CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
1048
1049     CGAffineTransformConcat(cgMatrix, oldTextMatrix);
1050
1051     if (synthesisFlags & QFontEngine::SynthesizedItalic)
1052         cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
1053
1054     cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
1055
1056     CGContextSetTextMatrix(ctx, cgMatrix);
1057
1058     CGContextSetTextDrawingMode(ctx, kCGTextFill);
1059
1060
1061     QVarLengthArray<CGSize> advances(glyphs.size());
1062     QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
1063
1064     for (int i = 0; i < glyphs.size() - 1; ++i) {
1065         advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
1066         advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
1067         cgGlyphs[i] = glyphs[i];
1068     }
1069     advances[glyphs.size() - 1].width = 0;
1070     advances[glyphs.size() - 1].height = 0;
1071     cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
1072
1073     CGContextSetFont(ctx, cgFont);
1074
1075     CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
1076
1077     CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
1078
1079     if (synthesisFlags & QFontEngine::SynthesizedBold) {
1080         CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
1081                                       positions[0].y.toReal());
1082
1083         CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
1084     }
1085
1086     CGContextSetTextMatrix(ctx, oldTextMatrix);
1087 }
1088
1089 QFontEngine::FaceId QFontEngineMac::faceId() const
1090 {
1091     FaceId ret;
1092 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
1093 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
1094     // CTFontGetPlatformFont
1095     FSRef ref;
1096     if (ATSFontGetFileReference(FMGetATSFontRefFromFont(fontID), &ref) != noErr)
1097         return ret;
1098     ret.filename = QByteArray(128, 0);
1099     ret.index = fontID;
1100     FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
1101 }else
1102 #endif
1103 {
1104     FSSpec spec;
1105     if (ATSFontGetFileSpecification(FMGetATSFontRefFromFont(fontID), &spec) != noErr)
1106         return ret;
1107
1108     FSRef ref;
1109     FSpMakeFSRef(&spec, &ref);
1110     ret.filename = QByteArray(128, 0);
1111     ret.index = fontID;
1112     FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
1113 }
1114     return ret;
1115 }
1116
1117 QByteArray QFontEngineMac::getSfntTable(uint tag) const
1118 {
1119     ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1120
1121     ByteCount length;
1122     OSStatus status = ATSFontGetTable(atsFont, tag, 0, 0, 0, &length);
1123     if (status != noErr)
1124         return QByteArray();
1125     QByteArray table(length, 0);
1126     // CTFontCopyTable
1127     status = ATSFontGetTable(atsFont, tag, 0, table.length(), table.data(), &length);
1128     if (status != noErr)
1129         return QByteArray();
1130     return table;
1131 }
1132
1133 QFontEngine::Properties QFontEngineMac::properties() const
1134 {
1135     QFontEngine::Properties props;
1136     ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1137     quint16 tmp;
1138     // CTFontGetUnitsPerEm
1139     if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 18, 2, &tmp, 0) == noErr)
1140        props.emSquare = qFromBigEndian<quint16>(tmp);
1141     struct {
1142         qint16 xMin;
1143         qint16 yMin;
1144         qint16 xMax;
1145         qint16 yMax;
1146     } bbox;
1147     bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
1148     // CTFontGetBoundingBox
1149     if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 36, 8, &bbox, 0) == noErr) {
1150         bbox.xMin = qFromBigEndian<quint16>(bbox.xMin);
1151         bbox.yMin = qFromBigEndian<quint16>(bbox.yMin);
1152         bbox.xMax = qFromBigEndian<quint16>(bbox.xMax);
1153         bbox.yMax = qFromBigEndian<quint16>(bbox.yMax);
1154     }
1155     struct {
1156         qint16 ascender;
1157         qint16 descender;
1158         qint16 linegap;
1159     } metrics;
1160     metrics.ascender = metrics.descender = metrics.linegap = 0;
1161     // CTFontGetAscent, etc.
1162     if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'h', 'e', 'a'), 4, 6, &metrics, 0) == noErr) {
1163         metrics.ascender = qFromBigEndian<quint16>(metrics.ascender);
1164         metrics.descender = qFromBigEndian<quint16>(metrics.descender);
1165         metrics.linegap = qFromBigEndian<quint16>(metrics.linegap);
1166     }
1167     props.ascent = metrics.ascender;
1168     props.descent = -metrics.descender;
1169     props.leading = metrics.linegap;
1170     props.boundingBox = QRectF(bbox.xMin, -bbox.yMax,
1171                            bbox.xMax - bbox.xMin,
1172                            bbox.yMax - bbox.yMin);
1173     props.italicAngle = 0;
1174     props.capHeight = props.ascent;
1175
1176     qint16 lw = 0;
1177     // fonts lie
1178     if (ATSFontGetTable(atsFont, MAKE_TAG('p', 'o', 's', 't'), 10, 2, &lw, 0) == noErr)
1179        lw = qFromBigEndian<quint16>(lw);
1180     props.lineWidth = lw;
1181
1182     // CTFontCopyPostScriptName
1183     QCFString psName;
1184     if (ATSFontGetPostScriptName(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &psName) == noErr)
1185         props.postscriptName = QString(psName).toUtf8();
1186     props.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(props.postscriptName);
1187     return props;
1188 }
1189
1190 void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1191 {
1192     ATSUStyle unscaledStyle;
1193     ATSUCreateAndCopyStyle(style, &unscaledStyle);
1194
1195     int emSquare = properties().emSquare.toInt();
1196
1197     const int maxAttributeCount = 4;
1198     ATSUAttributeTag tags[maxAttributeCount + 1];
1199     ByteCount sizes[maxAttributeCount + 1];
1200     ATSUAttributeValuePtr values[maxAttributeCount + 1];
1201     int attributeCount = 0;
1202
1203     Fixed size = FixRatio(emSquare, 1);
1204     tags[attributeCount] = kATSUSizeTag;
1205     sizes[attributeCount] = sizeof(size);
1206     values[attributeCount] = &size;
1207     ++attributeCount;
1208
1209     Q_ASSERT(attributeCount < maxAttributeCount + 1);
1210     OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values);
1211     Q_ASSERT(err == noErr);
1212     Q_UNUSED(err);
1213
1214     // various CTFont metrics functions: CTFontGetBoundingRectsForGlyphs, CTFontGetAdvancesForGlyphs
1215     GlyphID atsuGlyph = glyph;
1216     ATSGlyphScreenMetrics atsuMetrics;
1217     ATSUGlyphGetScreenMetrics(unscaledStyle, 1, &atsuGlyph, 0,
1218                               /* iForcingAntiAlias =*/ false,
1219                               /* iAntiAliasSwitch =*/true,
1220                               &atsuMetrics);
1221
1222     metrics->width = int(atsuMetrics.width);
1223     metrics->height = int(atsuMetrics.height);
1224     metrics->x = QFixed::fromReal(atsuMetrics.topLeft.x);
1225     metrics->y = -QFixed::fromReal(atsuMetrics.topLeft.y);
1226     metrics->xoff = QFixed::fromReal(atsuMetrics.deviceAdvance.x);
1227     metrics->yoff = QFixed::fromReal(atsuMetrics.deviceAdvance.y);
1228
1229     QFixedPoint p;
1230     addGlyphsToPathHelper(unscaledStyle, &glyph, &p, 1, path);
1231
1232     ATSUDisposeStyle(unscaledStyle);
1233 }
1234 #endif // !QT_MAC_USE_COCOA
1235
1236 QT_END_NAMESPACE