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

Last change on this file since 1168 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 67.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qapplication_p.h>
43#include <private/qfontengine_p.h>
44#include <private/qpainter_p.h>
45#include <private/qtextengine_p.h>
46#include <qbitmap.h>
47#include <private/qpaintengine_mac_p.h>
48#include <private/qprintengine_mac_p.h>
49#include <qglobal.h>
50#include <qpixmap.h>
51#include <qpixmapcache.h>
52#include <qvarlengtharray.h>
53#include <qdebug.h>
54#include <qendian.h>
55#include <qmath.h>
56
57#include <ApplicationServices/ApplicationServices.h>
58#include <AppKit/AppKit.h>
59
60QT_BEGIN_NAMESPACE
61
62/*****************************************************************************
63 QFontEngine debug facilities
64 *****************************************************************************/
65//#define DEBUG_ADVANCES
66
67extern int qt_antialiasing_threshold; // QApplication.cpp
68
69#ifndef FixedToQFixed
70#define FixedToQFixed(a) QFixed::fromFixed((a) >> 10)
71#define QFixedToFixed(x) ((x).value() << 10)
72#endif
73
74class QMacFontPath
75{
76 float x, y;
77 QPainterPath *path;
78public:
79 inline QMacFontPath(float _x, float _y, QPainterPath *_path) : x(_x), y(_y), path(_path) { }
80 inline void setPosition(float _x, float _y) { x = _x; y = _y; }
81 inline void advance(float _x) { x += _x; }
82 static OSStatus lineTo(const Float32Point *, void *);
83 static OSStatus cubicTo(const Float32Point *, const Float32Point *,
84 const Float32Point *, void *);
85 static OSStatus moveTo(const Float32Point *, void *);
86 static OSStatus closePath(void *);
87};
88
89OSStatus QMacFontPath::lineTo(const Float32Point *pt, void *data)
90
91{
92 QMacFontPath *p = static_cast<QMacFontPath*>(data);
93 p->path->lineTo(p->x + pt->x, p->y + pt->y);
94 return noErr;
95}
96
97OSStatus QMacFontPath::cubicTo(const Float32Point *cp1, const Float32Point *cp2,
98 const Float32Point *ep, void *data)
99
100{
101 QMacFontPath *p = static_cast<QMacFontPath*>(data);
102 p->path->cubicTo(p->x + cp1->x, p->y + cp1->y,
103 p->x + cp2->x, p->y + cp2->y,
104 p->x + ep->x, p->y + ep->y);
105 return noErr;
106}
107
108OSStatus QMacFontPath::moveTo(const Float32Point *pt, void *data)
109{
110 QMacFontPath *p = static_cast<QMacFontPath*>(data);
111 p->path->moveTo(p->x + pt->x, p->y + pt->y);
112 return noErr;
113}
114
115OSStatus QMacFontPath::closePath(void *data)
116{
117 static_cast<QMacFontPath*>(data)->path->closeSubpath();
118 return noErr;
119}
120
121
122
123void qmacfontengine_gamma_correct(QImage *image)
124{
125 extern uchar qt_pow_rgb_gamma[256];
126
127 // gamma correct the pixels back to linear color space...
128 int h = image->height();
129 int w = image->width();
130
131 for (int y=0; y<h; ++y) {
132 uint *pixels = (uint *) image->scanLine(y);
133 for (int x=0; x<w; ++x) {
134 uint p = pixels[x];
135 uint r = qt_pow_rgb_gamma[qRed(p)];
136 uint g = qt_pow_rgb_gamma[qGreen(p)];
137 uint b = qt_pow_rgb_gamma[qBlue(p)];
138 pixels[x] = (r << 16) | (g << 8) | b | 0xff000000;
139 }
140 }
141}
142
143
144#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
145QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
146 : QFontEngineMulti(0)
147{
148 this->fontDef = fontDef;
149 CTFontSymbolicTraits symbolicTraits = 0;
150 if (fontDef.weight >= QFont::Bold)
151 symbolicTraits |= kCTFontBoldTrait;
152 switch (fontDef.style) {
153 case QFont::StyleNormal:
154 break;
155 case QFont::StyleItalic:
156 case QFont::StyleOblique:
157 symbolicTraits |= kCTFontItalicTrait;
158 break;
159 }
160
161 QCFString name;
162 ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name);
163
164 transform = CGAffineTransformIdentity;
165 if (fontDef.stretch != 100) {
166 transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
167 }
168
169 QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize);
170 QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform);
171 ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, symbolicTraits, symbolicTraits);
172
173 // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does
174 // not exist for the given font. (for example italic)
175 if (ctfont == 0) {
176 ctfont = baseFont;
177 CFRetain(ctfont);
178 }
179
180 attributeDict = CFDictionaryCreateMutable(0, 2,
181 &kCFTypeDictionaryKeyCallBacks,
182 &kCFTypeDictionaryValueCallBacks);
183 CFDictionaryAddValue(attributeDict, NSFontAttributeName, ctfont);
184 if (!kerning) {
185 float zero = 0.0;
186 QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero);
187 CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern);
188 }
189
190 QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef, this);
191 fe->ref.ref();
192 engines.append(fe);
193
194}
195
196QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti()
197{
198 CFRelease(ctfont);
199}
200
201uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef id) const
202{
203 for (int i = 0; i < engines.count(); ++i) {
204 if (CFEqual(engineAt(i)->ctfont, id))
205 return i;
206 }
207
208 QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this);
209 QCoreTextFontEngine *fe = new QCoreTextFontEngine(id, fontDef, that);
210 fe->ref.ref();
211 that->engines.append(fe);
212 return engines.count() - 1;
213}
214
215bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags,
216 unsigned short *logClusters, const HB_CharAttributes *) const
217{
218 QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0,
219 reinterpret_cast<const UniChar *>(str),
220 len, kCFAllocatorNull);
221 QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict);
222 QCFType<CTTypesetterRef> typeSetter = CTTypesetterCreateWithAttributedString(attributedString);
223 CFRange range = {0, 0};
224 QCFType<CTLineRef> line = CTTypesetterCreateLine(typeSetter, range);
225 CFArrayRef array = CTLineGetGlyphRuns(line);
226 uint arraySize = CFArrayGetCount(array);
227 glyph_t *outGlyphs = glyphs->glyphs;
228 HB_GlyphAttributes *outAttributes = glyphs->attributes;
229 QFixed *outAdvances_x = glyphs->advances_x;
230 QFixed *outAdvances_y = glyphs->advances_y;
231 glyph_t *initialGlyph = outGlyphs;
232
233 if (arraySize == 0) {
234 // CoreText failed to shape the text we gave it, so we assume one glyph
235 // per character and build a list of invalid glyphs with zero advance
236 *nglyphs = len;
237 for (int i = 0; i < len; ++i) {
238 outGlyphs[i] = 0;
239 if (logClusters)
240 logClusters[i] = i;
241 outAdvances_x[i] = QFixed();
242 outAdvances_y[i] = QFixed();
243 outAttributes[i].clusterStart = true;
244 }
245 return true;
246 }
247
248 const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft);
249
250 bool outOBounds = false;
251 for (uint i = 0; i < arraySize; ++i) {
252 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, rtl ? (arraySize - 1 - i) : i));
253 CFIndex glyphCount = CTRunGetGlyphCount(run);
254 if (glyphCount == 0)
255 continue;
256
257 Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl);
258
259 if (!outOBounds && outGlyphs + glyphCount - initialGlyph > *nglyphs) {
260 outOBounds = true;
261 }
262 if (!outOBounds) {
263 CFDictionaryRef runAttribs = CTRunGetAttributes(run);
264 //NSLog(@"Dictionary %@", runAttribs);
265 if (!runAttribs)
266 runAttribs = attributeDict;
267 CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName));
268 const uint fontIndex = (fontIndexForFont(runFont) << 24);
269 //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont));
270 QVarLengthArray<CGGlyph, 512> cgglyphs(0);
271 const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run);
272 if (!tmpGlyphs) {
273 cgglyphs.resize(glyphCount);
274 CTRunGetGlyphs(run, range, cgglyphs.data());
275 tmpGlyphs = cgglyphs.constData();
276 }
277 QVarLengthArray<CGPoint, 512> cgpoints(0);
278 const CGPoint *tmpPoints = CTRunGetPositionsPtr(run);
279 if (!tmpPoints) {
280 cgpoints.resize(glyphCount);
281 CTRunGetPositions(run, range, cgpoints.data());
282 tmpPoints = cgpoints.constData();
283 }
284
285 const int rtlOffset = rtl ? (glyphCount - 1) : 0;
286 const int rtlSign = rtl ? -1 : 1;
287
288 if (logClusters) {
289 CFRange stringRange = CTRunGetStringRange(run);
290 QVarLengthArray<CFIndex, 512> stringIndices(0);
291 const CFIndex *tmpIndices = CTRunGetStringIndicesPtr(run);
292 if (!tmpIndices) {
293 stringIndices.resize(glyphCount);
294 CTRunGetStringIndices(run, range, stringIndices.data());
295 tmpIndices = stringIndices.constData();
296 }
297
298 const int firstGlyphIndex = outGlyphs - initialGlyph;
299 outAttributes[0].clusterStart = true;
300
301 CFIndex k = 0;
302 CFIndex i = 0;
303 for (i = stringRange.location;
304 (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) {
305 if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location) {
306 logClusters[i] = k + firstGlyphIndex;
307 outAttributes[k].clusterStart = true;
308 ++k;
309 } else {
310 logClusters[i] = k + firstGlyphIndex - 1;
311 }
312 }
313 // in case of a ligature at the end, fill the remaining logcluster entries
314 for (;i < stringRange.location + stringRange.length; i++) {
315 logClusters[i] = k + firstGlyphIndex - 1;
316 }
317 }
318 for (CFIndex i = 0; i < glyphCount - 1; ++i) {
319 int idx = rtlOffset + rtlSign * i;
320 outGlyphs[idx] = tmpGlyphs[i] | fontIndex;
321 outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x);
322 outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y);
323
324 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
325 outAdvances_x[idx] = outAdvances_x[idx].round();
326 outAdvances_y[idx] = outAdvances_y[idx].round();
327 }
328 }
329 CGSize lastGlyphAdvance;
330 CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1);
331
332 outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex;
333 outAdvances_x[rtl ? 0 : (glyphCount - 1)] =
334 (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
335 ? QFixed::fromReal(lastGlyphAdvance.width).round()
336 : QFixed::fromReal(lastGlyphAdvance.width);
337 }
338 outGlyphs += glyphCount;
339 outAttributes += glyphCount;
340 outAdvances_x += glyphCount;
341 outAdvances_y += glyphCount;
342 }
343 *nglyphs = (outGlyphs - initialGlyph);
344 return !outOBounds;
345}
346
347bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
348 int *nglyphs, QTextEngine::ShaperFlags flags) const
349{
350 *nglyphs = len;
351 QCFType<CFStringRef> cfstring;
352
353 QVarLengthArray<CGGlyph> cgGlyphs(len);
354 CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len);
355
356 for (int i = 0; i < len; ++i) {
357 if (cgGlyphs[i]) {
358 glyphs->glyphs[i] = cgGlyphs[i];
359 } else {
360 if (!cfstring)
361 cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull);
362 QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1));
363 CGGlyph substituteGlyph = 0;
364 CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1);
365 if (substituteGlyph) {
366 const uint fontIndex = (fontIndexForFont(substituteFont) << 24);
367 glyphs->glyphs[i] = substituteGlyph | fontIndex;
368 if (!(flags & QTextEngine::GlyphIndicesOnly)) {
369 CGSize advance;
370 CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1);
371 glyphs->advances_x[i] = QFixed::fromReal(advance.width);
372 glyphs->advances_y[i] = QFixed::fromReal(advance.height);
373 }
374 }
375 }
376 }
377
378 if (flags & QTextEngine::GlyphIndicesOnly)
379 return true;
380
381 QVarLengthArray<CGSize> advances(len);
382 CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len);
383
384 for (int i = 0; i < len; ++i) {
385 if (glyphs->glyphs[i] & 0xff000000)
386 continue;
387 glyphs->advances_x[i] = QFixed::fromReal(advances[i].width);
388 glyphs->advances_y[i] = QFixed::fromReal(advances[i].height);
389 }
390
391 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
392 for (int i = 0; i < len; ++i) {
393 glyphs->advances_x[i] = glyphs->advances_x[i].round();
394 glyphs->advances_y[i] = glyphs->advances_y[i].round();
395 }
396 }
397
398 return true;
399}
400
401void QCoreTextFontEngineMulti::recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const
402{
403}
404void QCoreTextFontEngineMulti::doKerning(int , QGlyphLayout *, QTextEngine::ShaperFlags) const
405{
406}
407
408void QCoreTextFontEngineMulti::loadEngine(int)
409{
410 // Do nothing
411 Q_ASSERT(false);
412}
413
414
415
416QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def,
417 QCoreTextFontEngineMulti *multiEngine)
418{
419 fontDef = def;
420 parentEngine = multiEngine;
421 synthesisFlags = 0;
422 ctfont = font;
423 CFRetain(ctfont);
424 ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0);
425 cgFont = CGFontCreateWithPlatformFont(&atsfont);
426 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
427 if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) {
428 synthesisFlags |= SynthesizedBold;
429 }
430
431 if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) {
432 synthesisFlags |= SynthesizedItalic;
433 }
434 transform = CGAffineTransformIdentity;
435 if (fontDef.stretch != 100) {
436 transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
437 }
438 QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
439 if (os2Table.size() >= 10)
440 fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8));
441}
442
443QCoreTextFontEngine::~QCoreTextFontEngine()
444{
445 CFRelease(ctfont);
446 CFRelease(cgFont);
447}
448
449bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int *, QTextEngine::ShaperFlags) const
450{
451 return false;
452}
453
454glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
455{
456 QFixed w;
457 bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
458
459 for (int i = 0; i < glyphs.numGlyphs; ++i) {
460 w += round ? glyphs.effectiveAdvance(i).round()
461 : glyphs.effectiveAdvance(i);
462 }
463 return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0);
464}
465glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
466{
467 glyph_metrics_t ret;
468 CGGlyph g = glyph;
469 CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1);
470 ret.width = QFixed::fromReal(rect.size.width);
471 ret.height = QFixed::fromReal(rect.size.height);
472 ret.x = QFixed::fromReal(rect.origin.x);
473 ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
474 CGSize advances[1];
475 CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1);
476 ret.xoff = QFixed::fromReal(advances[0].width);
477 ret.yoff = QFixed::fromReal(advances[0].height);
478
479 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
480 ret.xoff = ret.xoff.round();
481 ret.yoff = ret.yoff.round();
482 }
483
484 return ret;
485}
486
487QFixed QCoreTextFontEngine::ascent() const
488{
489 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
490 ? QFixed::fromReal(CTFontGetAscent(ctfont)).round()
491 : QFixed::fromReal(CTFontGetAscent(ctfont));
492}
493QFixed QCoreTextFontEngine::descent() const
494{
495 QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont));
496 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
497 d = d.round();
498
499 // subtract a pixel to even out the historical +1 in QFontMetrics::height().
500 // Fix in Qt 5.
501 return d - 1;
502}
503QFixed QCoreTextFontEngine::leading() const
504{
505 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
506 ? QFixed::fromReal(CTFontGetLeading(ctfont)).round()
507 : QFixed::fromReal(CTFontGetLeading(ctfont));
508}
509QFixed QCoreTextFontEngine::xHeight() const
510{
511 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
512 ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round()
513 : QFixed::fromReal(CTFontGetXHeight(ctfont));
514}
515QFixed QCoreTextFontEngine::averageCharWidth() const
516{
517 // ### Need to implement properly and get the information from the OS/2 Table.
518 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
519 ? QFontEngine::averageCharWidth().round()
520 : QFontEngine::averageCharWidth();
521}
522
523qreal QCoreTextFontEngine::maxCharWidth() const
524{
525 // ### Max Help!
526 return 0;
527
528}
529qreal QCoreTextFontEngine::minLeftBearing() const
530{
531 // ### Min Help!
532 return 0;
533
534}
535qreal QCoreTextFontEngine::minRightBearing() const
536{
537 // ### Max Help! (even thought it's right)
538 return 0;
539
540}
541
542void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
543{
544 QVarLengthArray<QFixedPoint> positions;
545 QVarLengthArray<glyph_t> glyphs;
546 QTransform matrix;
547 matrix.translate(x, y);
548 getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
549 if (glyphs.size() == 0)
550 return;
551
552 CGContextSetFontSize(ctx, fontDef.pixelSize);
553
554 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
555
556 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
557
558 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
559
560 if (synthesisFlags & QFontEngine::SynthesizedItalic)
561 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
562
563 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
564
565 CGContextSetTextMatrix(ctx, cgMatrix);
566
567 CGContextSetTextDrawingMode(ctx, kCGTextFill);
568
569
570 QVarLengthArray<CGSize> advances(glyphs.size());
571 QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
572
573 for (int i = 0; i < glyphs.size() - 1; ++i) {
574 advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
575 advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
576 cgGlyphs[i] = glyphs[i];
577 }
578 advances[glyphs.size() - 1].width = 0;
579 advances[glyphs.size() - 1].height = 0;
580 cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
581
582 CGContextSetFont(ctx, cgFont);
583 //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont));
584
585 CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
586
587 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
588
589 if (synthesisFlags & QFontEngine::SynthesizedBold) {
590 CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
591 positions[0].y.toReal());
592
593 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
594 }
595
596 CGContextSetTextMatrix(ctx, oldTextMatrix);
597}
598
599struct ConvertPathInfo
600{
601 ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {}
602 QPainterPath *path;
603 QPointF pos;
604};
605
606static void convertCGPathToQPainterPath(void *info, const CGPathElement *element)
607{
608 ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info);
609 switch(element->type) {
610 case kCGPathElementMoveToPoint:
611 myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(),
612 element->points[0].y + myInfo->pos.y());
613 break;
614 case kCGPathElementAddLineToPoint:
615 myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(),
616 element->points[0].y + myInfo->pos.y());
617 break;
618 case kCGPathElementAddQuadCurveToPoint:
619 myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(),
620 element->points[0].y + myInfo->pos.y(),
621 element->points[1].x + myInfo->pos.x(),
622 element->points[1].y + myInfo->pos.y());
623 break;
624 case kCGPathElementAddCurveToPoint:
625 myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(),
626 element->points[0].y + myInfo->pos.y(),
627 element->points[1].x + myInfo->pos.x(),
628 element->points[1].y + myInfo->pos.y(),
629 element->points[2].x + myInfo->pos.x(),
630 element->points[2].y + myInfo->pos.y());
631 break;
632 case kCGPathElementCloseSubpath:
633 myInfo->path->closeSubpath();
634 break;
635 default:
636 qDebug() << "Unhandled path transform type: " << element->type;
637 }
638
639}
640
641void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
642 QPainterPath *path, QTextItem::RenderFlags)
643{
644
645 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
646 cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
647
648 if (synthesisFlags & QFontEngine::SynthesizedItalic)
649 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
650
651
652 for (int i = 0; i < nGlyphs; ++i) {
653 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
654 ConvertPathInfo info(path, positions[i].toPointF());
655 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
656 }
657}
658
659QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, int margin, bool aa)
660{
661 const glyph_metrics_t br = boundingBox(glyph);
662 QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32);
663 im.fill(0);
664
665 CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
666 uint cgflags = kCGImageAlphaNoneSkipFirst;
667#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
668 cgflags |= kCGBitmapByteOrder32Host;
669#endif
670 CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
671 8, im.bytesPerLine(), colorspace,
672 cgflags);
673 CGContextSetFontSize(ctx, fontDef.pixelSize);
674 CGContextSetShouldAntialias(ctx, aa ||
675 (fontDef.pointSize > qt_antialiasing_threshold
676 && !(fontDef.styleStrategy & QFont::NoAntialias)));
677 CGContextSetShouldSmoothFonts(ctx, aa);
678 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
679 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
680
681 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
682
683 if (synthesisFlags & QFontEngine::SynthesizedItalic)
684 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
685
686 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
687
688 CGContextSetTextMatrix(ctx, cgMatrix);
689 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
690 CGContextSetTextDrawingMode(ctx, kCGTextFill);
691
692 ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0);
693 QCFType<CGFontRef> cgFont = CGFontCreateWithPlatformFont(&atsfont);
694 CGContextSetFont(ctx, cgFont);
695
696 qreal pos_x = -br.x.toReal()+1, pos_y = im.height()+br.y.toReal();
697 CGContextSetTextPosition(ctx, pos_x, pos_y);
698
699 CGSize advance;
700 advance.width = 0;
701 advance.height = 0;
702 CGGlyph cgGlyph = glyph;
703 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
704
705 if (synthesisFlags & QFontEngine::SynthesizedBold) {
706 CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
707 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
708 }
709
710 CGContextRelease(ctx);
711
712 return im;
713}
714
715QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph)
716{
717 QImage im = imageForGlyph(glyph, 0, false);
718
719 QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
720 QVector<QRgb> colors(256);
721 for (int i=0; i<256; ++i)
722 colors[i] = qRgba(0, 0, 0, i);
723 indexed.setColorTable(colors);
724
725 for (int y=0; y<im.height(); ++y) {
726 uint *src = (uint*) im.scanLine(y);
727 uchar *dst = indexed.scanLine(y);
728 for (int x=0; x<im.width(); ++x) {
729 *dst = qGray(*src);
730 ++dst;
731 ++src;
732 }
733 }
734
735 return indexed;
736}
737
738QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &x)
739{
740 if (x.type() >= QTransform::TxScale)
741 return QFontEngine::alphaRGBMapForGlyph(glyph, margin, x);
742
743 QImage im = imageForGlyph(glyph, margin, true);
744 qmacfontengine_gamma_correct(&im);
745 return im;
746}
747
748void QCoreTextFontEngine::recalcAdvances(int numGlyphs, QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
749{
750 Q_ASSERT(false);
751 Q_UNUSED(numGlyphs);
752 Q_UNUSED(glyphs);
753 Q_UNUSED(flags);
754}
755
756QFontEngine::FaceId QCoreTextFontEngine::faceId() const
757{
758 return QFontEngine::FaceId();
759}
760
761bool QCoreTextFontEngine::canRender(const QChar *string, int len)
762{
763 QCFType<CTFontRef> retFont = CTFontCreateForString(ctfont,
764 QCFType<CFStringRef>(CFStringCreateWithCharactersNoCopy(0,
765 reinterpret_cast<const UniChar *>(string),
766 len, kCFAllocatorNull)),
767 CFRangeMake(0, len));
768 return retFont != 0;
769 return false;
770}
771
772 bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
773 {
774 QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0);
775 if (!table || !length)
776 return false;
777 CFIndex tableLength = CFDataGetLength(table);
778 int availableLength = *length;
779 *length = tableLength;
780 if (buffer) {
781 if (tableLength > availableLength)
782 return false;
783 CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer);
784 }
785 return true;
786 }
787
788void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *)
789{
790 // ###
791}
792
793#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
794
795#ifndef QT_MAC_USE_COCOA
796QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
797 : QFontEngineMulti(0)
798{
799 this->fontDef = fontDef;
800 this->kerning = kerning;
801
802 // hopefully (CTFontCreateWithName or CTFontCreateWithFontDescriptor) + CTFontCreateCopyWithSymbolicTraits
803 // (or CTFontCreateWithQuickdrawInstance)
804 FMFontFamily fmFamily;
805 FMFontStyle fntStyle = 0;
806 fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
807 if (fmFamily == kInvalidFontFamily) {
808 // Use the ATSFont then...
809 fontID = FMGetFontFromATSFontRef(atsFontRef);
810 } else {
811 if (fontDef.weight >= QFont::Bold)
812 fntStyle |= ::bold;
813 if (fontDef.style != QFont::StyleNormal)
814 fntStyle |= ::italic;
815
816 FMFontStyle intrinsicStyle;
817 FMFont fnt = 0;
818 if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
819 fontID = FMGetATSFontRefFromFont(fnt);
820 }
821
822 // CFDictionaryRef, <CTStringAttributes.h>
823 OSStatus status;
824
825 status = ATSUCreateTextLayout(&textLayout);
826 Q_ASSERT(status == noErr);
827
828 const int maxAttributeCount = 5;
829 ATSUAttributeTag tags[maxAttributeCount + 1];
830 ByteCount sizes[maxAttributeCount + 1];
831 ATSUAttributeValuePtr values[maxAttributeCount + 1];
832 int attributeCount = 0;
833
834 Fixed size = FixRatio(fontDef.pixelSize, 1);
835 tags[attributeCount] = kATSUSizeTag;
836 sizes[attributeCount] = sizeof(size);
837 values[attributeCount] = &size;
838 ++attributeCount;
839
840 tags[attributeCount] = kATSUFontTag;
841 sizes[attributeCount] = sizeof(fontID);
842 values[attributeCount] = &this->fontID;
843 ++attributeCount;
844
845 transform = CGAffineTransformIdentity;
846 if (fontDef.stretch != 100) {
847 transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
848 tags[attributeCount] = kATSUFontMatrixTag;
849 sizes[attributeCount] = sizeof(transform);
850 values[attributeCount] = &transform;
851 ++attributeCount;
852 }
853
854 status = ATSUCreateStyle(&style);
855 Q_ASSERT(status == noErr);
856
857 Q_ASSERT(attributeCount < maxAttributeCount + 1);
858 status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
859 Q_ASSERT(status == noErr);
860
861 QFontEngineMac *fe = new QFontEngineMac(style, fontID, fontDef, this);
862 fe->ref.ref();
863 engines.append(fe);
864}
865
866QFontEngineMacMulti::~QFontEngineMacMulti()
867{
868 ATSUDisposeTextLayout(textLayout);
869 ATSUDisposeStyle(style);
870
871 for (int i = 0; i < engines.count(); ++i) {
872 QFontEngineMac *fe = const_cast<QFontEngineMac *>(static_cast<const QFontEngineMac *>(engines.at(i)));
873 fe->multiEngine = 0;
874 if (!fe->ref.deref())
875 delete fe;
876 }
877 engines.clear();
878}
879
880struct QGlyphLayoutInfo
881{
882 QGlyphLayout *glyphs;
883 int *numGlyphs;
884 bool callbackCalled;
885 int *mappedFonts;
886 QTextEngine::ShaperFlags flags;
887 QFontEngineMacMulti::ShaperItem *shaperItem;
888 unsigned int styleStrategy;
889};
890
891static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon,
892 void *operationExtraParameter, ATSULayoutOperationCallbackStatus *callbackStatus)
893{
894 Q_UNUSED(selector);
895 Q_UNUSED(operationExtraParameter);
896
897 QGlyphLayoutInfo *nfo = reinterpret_cast<QGlyphLayoutInfo *>(refCon);
898 nfo->callbackCalled = true;
899
900 ATSLayoutRecord *layoutData = 0;
901 ItemCount itemCount = 0;
902
903 OSStatus e = noErr;
904 e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
905 /*iCreate =*/ false,
906 (void **) &layoutData,
907 &itemCount);
908 if (e != noErr)
909 return e;
910
911 *nfo->numGlyphs = itemCount - 1;
912
913 Fixed *baselineDeltas = 0;
914
915 e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
916 /*iCreate =*/ true,
917 (void **) &baselineDeltas,
918 &itemCount);
919 if (e != noErr)
920 return e;
921
922 int nextCharStop = -1;
923 int currentClusterGlyph = -1; // first glyph in log cluster
924 QFontEngineMacMulti::ShaperItem *item = nfo->shaperItem;
925 if (item->charAttributes) {
926 item = nfo->shaperItem;
927#if !defined(QT_NO_DEBUG)
928 int surrogates = 0;
929 const QChar *str = item->string;
930 for (int i = item->from; i < item->from + item->length - 1; ++i) {
931 surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00
932 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
933 }
934#endif
935 for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop)
936 if (item->charAttributes[nextCharStop].charStop)
937 break;
938 nextCharStop -= item->from;
939 }
940
941 nfo->glyphs->attributes[0].clusterStart = true;
942 int glyphIdx = 0;
943 int glyphIncrement = 1;
944 if (nfo->flags & QTextEngine::RightToLeft) {
945 glyphIdx = itemCount - 2;
946 glyphIncrement = -1;
947 }
948 for (int i = 0; i < *nfo->numGlyphs; ++i, glyphIdx += glyphIncrement) {
949
950 int charOffset = layoutData[glyphIdx].originalOffset / sizeof(UniChar);
951 const int fontIdx = nfo->mappedFonts[charOffset];
952
953 ATSGlyphRef glyphId = layoutData[glyphIdx].glyphID;
954
955 QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]);
956 QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos);
957
958 if (nfo->styleStrategy & QFont::ForceIntegerMetrics) {
959 yAdvance = yAdvance.round();
960 xAdvance = xAdvance.round();
961 }
962
963 if (glyphId != 0xffff || i == 0) {
964 if (i < nfo->glyphs->numGlyphs)
965 {
966 nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24);
967
968 nfo->glyphs->advances_y[i] = yAdvance;
969 nfo->glyphs->advances_x[i] = xAdvance;
970 }
971 } else {
972 // ATSUI gives us 0xffff as glyph id at the index in the glyph array for
973 // a character position that maps to a ligtature. Such a glyph id does not
974 // result in any visual glyph, but it may have an advance, which is why we
975 // sum up the glyph advances.
976 --i;
977 nfo->glyphs->advances_y[i] += yAdvance;
978 nfo->glyphs->advances_x[i] += xAdvance;
979 *nfo->numGlyphs -= 1;
980 }
981
982 if (item->log_clusters) {
983 if (charOffset >= nextCharStop) {
984 nfo->glyphs->attributes[i].clusterStart = true;
985 currentClusterGlyph = i;
986
987 ++nextCharStop;
988 for (; nextCharStop < item->length; ++nextCharStop)
989 if (item->charAttributes[item->from + nextCharStop].charStop)
990 break;
991 } else {
992 if (currentClusterGlyph == -1)
993 currentClusterGlyph = i;
994 }
995 item->log_clusters[charOffset] = currentClusterGlyph;
996
997 // surrogate handling
998 if (charOffset < item->length - 1) {
999 QChar current = item->string[item->from + charOffset];
1000 QChar next = item->string[item->from + charOffset + 1];
1001 if (current.unicode() >= 0xd800 && current.unicode() < 0xdc00
1002 && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) {
1003 item->log_clusters[charOffset + 1] = currentClusterGlyph;
1004 }
1005 }
1006 }
1007 }
1008
1009 /*
1010 if (item) {
1011 qDebug() << "resulting logclusters:";
1012 for (int i = 0; i < item->length; ++i)
1013 qDebug() << "logClusters[" << i << "] =" << item->log_clusters[i];
1014 qDebug() << "clusterstarts:";
1015 for (int i = 0; i < *nfo->numGlyphs; ++i)
1016 qDebug() << "clusterStart[" << i << "] =" << nfo->glyphs[i].attributes.clusterStart;
1017 }
1018 */
1019
1020 ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
1021 (void **) &baselineDeltas);
1022
1023 ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
1024 (void **) &layoutData);
1025
1026 *callbackStatus = kATSULayoutOperationCallbackStatusHandled;
1027 return noErr;
1028}
1029
1030int QFontEngineMacMulti::fontIndexForFontID(ATSUFontID id) const
1031{
1032 for (int i = 0; i < engines.count(); ++i) {
1033 if (engineAt(i)->fontID == id)
1034 return i;
1035 }
1036
1037 QFontEngineMacMulti *that = const_cast<QFontEngineMacMulti *>(this);
1038 QFontEngineMac *fe = new QFontEngineMac(style, id, fontDef, that);
1039 fe->ref.ref();
1040 that->engines.append(fe);
1041 return engines.count() - 1;
1042}
1043
1044bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
1045{
1046 return stringToCMap(str, len, glyphs, nglyphs, flags, /*logClusters=*/0, /*charAttributes=*/0);
1047}
1048
1049bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
1050 unsigned short *logClusters, const HB_CharAttributes *charAttributes) const
1051{
1052 if (*nglyphs < len) {
1053 *nglyphs = len;
1054 return false;
1055 }
1056
1057 ShaperItem shaperItem;
1058 shaperItem.string = str;
1059 shaperItem.from = 0;
1060 shaperItem.length = len;
1061 shaperItem.glyphs = *glyphs;
1062 shaperItem.glyphs.numGlyphs = *nglyphs;
1063 shaperItem.flags = flags;
1064 shaperItem.log_clusters = logClusters;
1065 shaperItem.charAttributes = charAttributes;
1066
1067 const int maxChars = qMax(1,
1068 int(SHRT_MAX / maxCharWidth())
1069 - 10 // subtract a few to be on the safe side
1070 );
1071 if (len < maxChars || !charAttributes)
1072 return stringToCMapInternal(str, len, glyphs, nglyphs, flags, &shaperItem);
1073
1074 int charIdx = 0;
1075 int glyphIdx = 0;
1076 ShaperItem tmpItem = shaperItem;
1077
1078 do {
1079 tmpItem.from = shaperItem.from + charIdx;
1080
1081 int charCount = qMin(maxChars, len - charIdx);
1082
1083 int lastWhitespace = tmpItem.from + charCount - 1;
1084 int lastSoftBreak = lastWhitespace;
1085 int lastCharStop = lastSoftBreak;
1086 for (int i = lastCharStop; i >= tmpItem.from; --i) {
1087 if (tmpItem.charAttributes[i].whiteSpace) {
1088 lastWhitespace = i;
1089 break;
1090 } if (tmpItem.charAttributes[i].lineBreakType != HB_NoBreak) {
1091 lastSoftBreak = i;
1092 } if (tmpItem.charAttributes[i].charStop) {
1093 lastCharStop = i;
1094 }
1095 }
1096 charCount = qMin(lastWhitespace, qMin(lastSoftBreak, lastCharStop)) - tmpItem.from + 1;
1097
1098 int glyphCount = shaperItem.glyphs.numGlyphs - glyphIdx;
1099 if (glyphCount <= 0)
1100 return false;
1101 tmpItem.length = charCount;
1102 tmpItem.glyphs = shaperItem.glyphs.mid(glyphIdx, glyphCount);
1103 tmpItem.log_clusters = shaperItem.log_clusters + charIdx;
1104 if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length,
1105 &tmpItem.glyphs, &glyphCount, flags,
1106 &tmpItem)) {
1107 *nglyphs = glyphIdx + glyphCount;
1108 return false;
1109 }
1110 for (int i = 0; i < charCount; ++i)
1111 tmpItem.log_clusters[i] += glyphIdx;
1112 glyphIdx += glyphCount;
1113 charIdx += charCount;
1114 } while (charIdx < len);
1115 *nglyphs = glyphIdx;
1116 glyphs->numGlyphs = glyphIdx;
1117
1118 return true;
1119}
1120
1121bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,ShaperItem *shaperItem) const
1122{
1123 //qDebug() << "stringToCMap" << QString(str, len);
1124
1125 OSStatus e = noErr;
1126
1127 e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len);
1128 if (e != noErr) {
1129 qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__);
1130 return false;
1131 }
1132
1133 QGlyphLayoutInfo nfo;
1134 nfo.glyphs = glyphs;
1135 nfo.numGlyphs = nglyphs;
1136 nfo.callbackCalled = false;
1137 nfo.flags = flags;
1138 nfo.shaperItem = shaperItem;
1139 nfo.styleStrategy = fontDef.styleStrategy;
1140
1141 int prevNumGlyphs = *nglyphs;
1142
1143 QVarLengthArray<int> mappedFonts(len);
1144 for (int i = 0; i < len; ++i)
1145 mappedFonts[i] = 0;
1146 nfo.mappedFonts = mappedFonts.data();
1147
1148 Q_ASSERT(sizeof(void *) <= sizeof(URefCon));
1149 e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo);
1150 if (e != noErr) {
1151 qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__);
1152 return false;
1153 }
1154
1155 {
1156 const int maxAttributeCount = 3;
1157 ATSUAttributeTag tags[maxAttributeCount + 1];
1158 ByteCount sizes[maxAttributeCount + 1];
1159 ATSUAttributeValuePtr values[maxAttributeCount + 1];
1160 int attributeCount = 0;
1161
1162 tags[attributeCount] = kATSULineLayoutOptionsTag;
1163 ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment
1164 | kATSLineIgnoreFontLeading
1165 | kATSLineNoSpecialJustification // we do kashidas ourselves
1166 | kATSLineDisableAllJustification
1167 ;
1168
1169 if (fontDef.styleStrategy & QFont::NoAntialias)
1170 layopts |= kATSLineNoAntiAliasing;
1171
1172 if (!kerning)
1173 layopts |= kATSLineDisableAllKerningAdjustments;
1174
1175 values[attributeCount] = &layopts;
1176 sizes[attributeCount] = sizeof(layopts);
1177 ++attributeCount;
1178
1179 tags[attributeCount] = kATSULayoutOperationOverrideTag;
1180 ATSULayoutOperationOverrideSpecifier spec;
1181 spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
1182 spec.overrideUPP = atsuPostLayoutCallback;
1183 values[attributeCount] = &spec;
1184 sizes[attributeCount] = sizeof(spec);
1185 ++attributeCount;
1186
1187 // CTWritingDirection
1188 Boolean direction;
1189 if (flags & QTextEngine::RightToLeft)
1190 direction = kATSURightToLeftBaseDirection;
1191 else
1192 direction = kATSULeftToRightBaseDirection;
1193 tags[attributeCount] = kATSULineDirectionTag;
1194 values[attributeCount] = &direction;
1195 sizes[attributeCount] = sizeof(direction);
1196 ++attributeCount;
1197
1198 Q_ASSERT(attributeCount < maxAttributeCount + 1);
1199 e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values);
1200 if (e != noErr) {
1201 qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__);
1202 return false;
1203 }
1204
1205 }
1206
1207 e = ATSUSetRunStyle(textLayout, style, 0, len);
1208 if (e != noErr) {
1209 qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__);
1210 return false;
1211 }
1212
1213 if (!(fontDef.styleStrategy & QFont::NoFontMerging)) {
1214 int pos = 0;
1215 do {
1216 ATSUFontID substFont = 0;
1217 UniCharArrayOffset changedOffset = 0;
1218 UniCharCount changeCount = 0;
1219
1220 e = ATSUMatchFontsToText(textLayout, pos, len - pos,
1221 &substFont, &changedOffset,
1222 &changeCount);
1223 if (e == kATSUFontsMatched) {
1224 int fontIdx = fontIndexForFontID(substFont);
1225 for (uint i = 0; i < changeCount; ++i)
1226 mappedFonts[changedOffset + i] = fontIdx;
1227 pos = changedOffset + changeCount;
1228 ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount);
1229 } else if (e == kATSUFontsNotMatched) {
1230 pos = changedOffset + changeCount;
1231 }
1232 } while (pos < len && e != noErr);
1233 }
1234 { // trigger the a layout
1235 // CFAttributedStringCreate, CTFramesetterCreateWithAttributedString (or perhaps Typesetter)
1236 Rect rect;
1237 e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
1238 /*iLocationX =*/ 0, /*iLocationY =*/ 0,
1239 &rect);
1240 if (e != noErr) {
1241 qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__);
1242 return false;
1243 }
1244 }
1245
1246 if (!nfo.callbackCalled) {
1247 qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__);
1248 return false;
1249 }
1250
1251 ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning);
1252 if (prevNumGlyphs < *nfo.numGlyphs)
1253 return false;
1254 return true;
1255}
1256
1257void QFontEngineMacMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1258{
1259 Q_ASSERT(false);
1260 Q_UNUSED(glyphs);
1261 Q_UNUSED(flags);
1262}
1263
1264void QFontEngineMacMulti::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
1265{
1266 //Q_ASSERT(false);
1267}
1268
1269void QFontEngineMacMulti::loadEngine(int /*at*/)
1270{
1271 // should never be called!
1272 Q_ASSERT(false);
1273}
1274
1275bool QFontEngineMacMulti::canRender(const QChar *string, int len)
1276{
1277 ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len);
1278 ATSUSetRunStyle(textLayout, style, 0, len);
1279
1280 OSStatus e = noErr;
1281 int pos = 0;
1282 do {
1283 FMFont substFont = 0;
1284 UniCharArrayOffset changedOffset = 0;
1285 UniCharCount changeCount = 0;
1286
1287 // CTFontCreateForString
1288 e = ATSUMatchFontsToText(textLayout, pos, len - pos,
1289 &substFont, &changedOffset,
1290 &changeCount);
1291 if (e == kATSUFontsMatched) {
1292 pos = changedOffset + changeCount;
1293 } else if (e == kATSUFontsNotMatched) {
1294 break;
1295 }
1296 } while (pos < len && e != noErr);
1297
1298 return e == noErr || e == kATSUFontsMatched;
1299}
1300
1301QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine)
1302 : fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false)
1303{
1304 fontDef = def;
1305 ATSUCreateAndCopyStyle(baseStyle, &style);
1306 ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1307 cgFont = CGFontCreateWithPlatformFont(&atsFont);
1308
1309 const int maxAttributeCount = 4;
1310 ATSUAttributeTag tags[maxAttributeCount + 1];
1311 ByteCount sizes[maxAttributeCount + 1];
1312 ATSUAttributeValuePtr values[maxAttributeCount + 1];
1313 int attributeCount = 0;
1314
1315 synthesisFlags = 0;
1316
1317 // synthesizing using CG is not recommended
1318 quint16 macStyle = 0;
1319 {
1320 uchar data[4];
1321 ByteCount len = 4;
1322 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
1323 macStyle = qFromBigEndian<quint16>(data);
1324 }
1325
1326 Boolean atsuBold = false;
1327 Boolean atsuItalic = false;
1328 if (fontDef.weight >= QFont::Bold) {
1329 if (!(macStyle & 1)) {
1330 synthesisFlags |= SynthesizedBold;
1331 atsuBold = true;
1332 tags[attributeCount] = kATSUQDBoldfaceTag;
1333 sizes[attributeCount] = sizeof(atsuBold);
1334 values[attributeCount] = &atsuBold;
1335 ++attributeCount;
1336 }
1337 }
1338 if (fontDef.style != QFont::StyleNormal) {
1339 if (!(macStyle & 2)) {
1340 synthesisFlags |= SynthesizedItalic;
1341 atsuItalic = true;
1342 tags[attributeCount] = kATSUQDItalicTag;
1343 sizes[attributeCount] = sizeof(atsuItalic);
1344 values[attributeCount] = &atsuItalic;
1345 ++attributeCount;
1346 }
1347 }
1348
1349 tags[attributeCount] = kATSUFontTag;
1350 values[attributeCount] = &fontID;
1351 sizes[attributeCount] = sizeof(fontID);
1352 ++attributeCount;
1353
1354 Q_ASSERT(attributeCount < maxAttributeCount + 1);
1355 OSStatus err = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
1356 Q_ASSERT(err == noErr);
1357 Q_UNUSED(err);
1358
1359 // CTFontCopyTable
1360 quint16 tmpFsType;
1361 if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 8, 2, &tmpFsType, 0) == noErr)
1362 fsType = qFromBigEndian<quint16>(tmpFsType);
1363 else
1364 fsType = 0;
1365
1366 if (multiEngine)
1367 transform = multiEngine->transform;
1368 else
1369 transform = CGAffineTransformIdentity;
1370
1371 ATSUTextMeasurement metric;
1372
1373 ATSUGetAttribute(style, kATSUAscentTag, sizeof(metric), &metric, 0);
1374 m_ascent = FixRound(metric);
1375
1376 ATSUGetAttribute(style, kATSUDescentTag, sizeof(metric), &metric, 0);
1377 m_descent = FixRound(metric);
1378
1379 ATSUGetAttribute(style, kATSULeadingTag, sizeof(metric), &metric, 0);
1380 m_leading = FixRound(metric);
1381
1382 ATSFontMetrics metrics;
1383
1384 ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
1385 m_maxCharWidth = metrics.maxAdvanceWidth * fontDef.pointSize;
1386
1387 ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
1388 m_xHeight = QFixed::fromReal(metrics.xHeight * fontDef.pointSize);
1389
1390 ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
1391 m_averageCharWidth = QFixed::fromReal(metrics.avgAdvanceWidth * fontDef.pointSize);
1392
1393 // Use width of 'X' if ATSFontGetHorizontalMetrics returns 0 for avgAdvanceWidth.
1394 if (m_averageCharWidth == QFixed(0)) {
1395 QChar c('X');
1396 QGlyphLayoutArray<1> glyphs;
1397 int nglyphs = 1;
1398 stringToCMap(&c, 1, &glyphs, &nglyphs, 0);
1399 glyph_metrics_t metrics = boundingBox(glyphs);
1400 m_averageCharWidth = metrics.width;
1401 }
1402}
1403
1404QFontEngineMac::~QFontEngineMac()
1405{
1406 ATSUDisposeStyle(style);
1407}
1408
1409static inline unsigned int getChar(const QChar *str, int &i, const int len)
1410{
1411 unsigned int uc = str[i].unicode();
1412 if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
1413 uint low = str[i+1].unicode();
1414 if (low >= 0xdc00 && low < 0xe000) {
1415 uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
1416 ++i;
1417 }
1418 }
1419 return uc;
1420}
1421
1422bool QFontEngineMac::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
1423{
1424 if (!cmap) {
1425 cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
1426 int size = 0;
1427 cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &size);
1428 if (!cmap)
1429 return false;
1430 }
1431 if (symbolCMap) {
1432 for (int i = 0; i < len; ++i) {
1433 unsigned int uc = getChar(str, i, len);
1434 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
1435 if(!glyphs->glyphs[i] && uc < 0x100)
1436 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
1437 }
1438 } else {
1439 for (int i = 0; i < len; ++i) {
1440 unsigned int uc = getChar(str, i, len);
1441 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
1442 }
1443 }
1444
1445 *nglyphs = len;
1446 glyphs->numGlyphs = *nglyphs;
1447
1448 if (!(flags & QTextEngine::GlyphIndicesOnly))
1449 recalcAdvances(glyphs, flags);
1450
1451 return true;
1452}
1453
1454void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1455{
1456 Q_UNUSED(flags)
1457
1458 QVarLengthArray<GlyphID> atsuGlyphs(glyphs->numGlyphs);
1459 for (int i = 0; i < glyphs->numGlyphs; ++i)
1460 atsuGlyphs[i] = glyphs->glyphs[i];
1461
1462 QVarLengthArray<ATSGlyphScreenMetrics> metrics(glyphs->numGlyphs);
1463
1464 ATSUGlyphGetScreenMetrics(style, glyphs->numGlyphs, atsuGlyphs.data(), sizeof(GlyphID),
1465 /* iForcingAntiAlias =*/ false,
1466 /* iAntiAliasSwitch =*/true,
1467 metrics.data());
1468
1469 for (int i = 0; i < glyphs->numGlyphs; ++i) {
1470 glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x);
1471 glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y);
1472
1473 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
1474 glyphs->advances_x[i] = glyphs->advances_x[i].round();
1475 glyphs->advances_y[i] = glyphs->advances_y[i].round();
1476 }
1477 }
1478}
1479
1480glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs)
1481{
1482 QFixed w;
1483 bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
1484 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1485 w += round ? glyphs.effectiveAdvance(i).round()
1486 : glyphs.effectiveAdvance(i);
1487 }
1488 return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0);
1489}
1490
1491glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph)
1492{
1493 GlyphID atsuGlyph = glyph;
1494
1495 ATSGlyphScreenMetrics metrics;
1496
1497 ATSUGlyphGetScreenMetrics(style, 1, &atsuGlyph, 0,
1498 /* iForcingAntiAlias =*/ false,
1499 /* iAntiAliasSwitch =*/true,
1500 &metrics);
1501
1502 // ### check again
1503
1504 glyph_metrics_t gm;
1505 gm.width = int(metrics.width);
1506 gm.height = int(metrics.height);
1507 gm.x = QFixed::fromReal(metrics.topLeft.x);
1508 gm.y = -QFixed::fromReal(metrics.topLeft.y);
1509 gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x);
1510 gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y);
1511
1512 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
1513 gm.x = gm.x.floor();
1514 gm.y = gm.y.floor();
1515 gm.xoff = gm.xoff.round();
1516 gm.yoff = gm.yoff.round();
1517 }
1518
1519 return gm;
1520}
1521
1522QFixed QFontEngineMac::ascent() const
1523{
1524 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1525 ? m_ascent.round()
1526 : m_ascent;
1527}
1528
1529QFixed QFontEngineMac::descent() const
1530{
1531 // subtract a pixel to even out the historical +1 in QFontMetrics::height().
1532 // Fix in Qt 5.
1533 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1534 ? m_descent.round() - 1
1535 : m_descent;
1536}
1537
1538QFixed QFontEngineMac::leading() const
1539{
1540 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1541 ? m_leading.round()
1542 : m_leading;
1543}
1544
1545qreal QFontEngineMac::maxCharWidth() const
1546{
1547 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1548 ? qRound(m_maxCharWidth)
1549 : m_maxCharWidth;
1550}
1551
1552QFixed QFontEngineMac::xHeight() const
1553{
1554 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1555 ? m_xHeight.round()
1556 : m_xHeight;
1557}
1558
1559QFixed QFontEngineMac::averageCharWidth() const
1560{
1561 return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1562 ? m_averageCharWidth.round()
1563 : m_averageCharWidth;
1564}
1565
1566static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path)
1567{
1568 if (!numGlyphs)
1569 return;
1570
1571 OSStatus e;
1572
1573 QMacFontPath fontpath(0, 0, path);
1574 ATSCubicMoveToUPP moveTo = NewATSCubicMoveToUPP(QMacFontPath::moveTo);
1575 ATSCubicLineToUPP lineTo = NewATSCubicLineToUPP(QMacFontPath::lineTo);
1576 ATSCubicCurveToUPP cubicTo = NewATSCubicCurveToUPP(QMacFontPath::cubicTo);
1577 ATSCubicClosePathUPP closePath = NewATSCubicClosePathUPP(QMacFontPath::closePath);
1578
1579 // CTFontCreatePathForGlyph
1580 for (int i = 0; i < numGlyphs; ++i) {
1581 GlyphID glyph = glyphs[i];
1582
1583 fontpath.setPosition(positions[i].x.toReal(), positions[i].y.toReal());
1584 ATSUGlyphGetCubicPaths(style, glyph, moveTo, lineTo,
1585 cubicTo, closePath, &fontpath, &e);
1586 }
1587
1588 DisposeATSCubicMoveToUPP(moveTo);
1589 DisposeATSCubicLineToUPP(lineTo);
1590 DisposeATSCubicCurveToUPP(cubicTo);
1591 DisposeATSCubicClosePathUPP(closePath);
1592}
1593
1594void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path,
1595 QTextItem::RenderFlags)
1596{
1597 addGlyphsToPathHelper(style, glyphs, positions, numGlyphs, path);
1598}
1599
1600
1601/*!
1602 Helper function for alphaMapForGlyph and alphaRGBMapForGlyph. The two are identical, except for
1603 the subpixel antialiasing...
1604*/
1605QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful)
1606{
1607 const glyph_metrics_t br = boundingBox(glyph);
1608 QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32);
1609 im.fill(0xff000000);
1610
1611 CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
1612 uint cgflags = kCGImageAlphaNoneSkipFirst;
1613#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
1614 cgflags |= kCGBitmapByteOrder32Host;
1615#endif
1616 CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
1617 8, im.bytesPerLine(), colorspace,
1618 cgflags);
1619 CGContextSetFontSize(ctx, fontDef.pixelSize);
1620 CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
1621 // turn off sub-pixel hinting - no support for that in OpenGL
1622 CGContextSetShouldSmoothFonts(ctx, colorful);
1623 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
1624 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
1625 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
1626
1627 if (synthesisFlags & QFontEngine::SynthesizedItalic)
1628 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
1629
1630 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
1631
1632 CGContextSetTextMatrix(ctx, cgMatrix);
1633 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
1634 CGContextSetTextDrawingMode(ctx, kCGTextFill);
1635 CGContextSetFont(ctx, cgFont);
1636
1637 qreal pos_x = -br.x.toReal() + 1;
1638 qreal pos_y = im.height() + br.y.toReal() - 2;
1639 CGContextSetTextPosition(ctx, pos_x, pos_y);
1640
1641 CGSize advance;
1642 advance.width = 0;
1643 advance.height = 0;
1644 CGGlyph cgGlyph = glyph;
1645 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
1646
1647 if (synthesisFlags & QFontEngine::SynthesizedBold) {
1648 CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
1649 CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
1650 }
1651
1652 CGContextRelease(ctx);
1653
1654 return im;
1655}
1656
1657QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph)
1658{
1659 QImage im = imageForGlyph(glyph, 2, false);
1660
1661 QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
1662 QVector<QRgb> colors(256);
1663 for (int i=0; i<256; ++i)
1664 colors[i] = qRgba(0, 0, 0, i);
1665 indexed.setColorTable(colors);
1666
1667 for (int y=0; y<im.height(); ++y) {
1668 uint *src = (uint*) im.scanLine(y);
1669 uchar *dst = indexed.scanLine(y);
1670 for (int x=0; x<im.width(); ++x) {
1671 *dst = qGray(*src);
1672 ++dst;
1673 ++src;
1674 }
1675 }
1676
1677 return indexed;
1678}
1679
1680QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t)
1681{
1682 QImage im = imageForGlyph(glyph, margin, true);
1683
1684 if (t.type() >= QTransform::TxScale) {
1685 im = im.transformed(t);
1686 }
1687
1688 qmacfontengine_gamma_correct(&im);
1689
1690 return im;
1691}
1692
1693
1694bool QFontEngineMac::canRender(const QChar *string, int len)
1695{
1696 Q_ASSERT(false);
1697 Q_UNUSED(string);
1698 Q_UNUSED(len);
1699 return false;
1700}
1701
1702void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
1703{
1704 QVarLengthArray<QFixedPoint> positions;
1705 QVarLengthArray<glyph_t> glyphs;
1706 QTransform matrix;
1707 matrix.translate(x, y);
1708 getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1709 if (glyphs.size() == 0)
1710 return;
1711
1712 CGContextSetFontSize(ctx, fontDef.pixelSize);
1713
1714 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
1715
1716 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
1717
1718 CGAffineTransformConcat(cgMatrix, oldTextMatrix);
1719
1720 if (synthesisFlags & QFontEngine::SynthesizedItalic)
1721 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
1722
1723 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
1724
1725 CGContextSetTextMatrix(ctx, cgMatrix);
1726
1727 CGContextSetTextDrawingMode(ctx, kCGTextFill);
1728
1729
1730 QVarLengthArray<CGSize> advances(glyphs.size());
1731 QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
1732
1733 for (int i = 0; i < glyphs.size() - 1; ++i) {
1734 advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
1735 advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
1736 cgGlyphs[i] = glyphs[i];
1737 }
1738 advances[glyphs.size() - 1].width = 0;
1739 advances[glyphs.size() - 1].height = 0;
1740 cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
1741
1742 CGContextSetFont(ctx, cgFont);
1743
1744 CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
1745
1746 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
1747
1748 if (synthesisFlags & QFontEngine::SynthesizedBold) {
1749 CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
1750 positions[0].y.toReal());
1751
1752 CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
1753 }
1754
1755 CGContextSetTextMatrix(ctx, oldTextMatrix);
1756}
1757
1758QFontEngine::FaceId QFontEngineMac::faceId() const
1759{
1760 FaceId ret;
1761#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
1762if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
1763 // CTFontGetPlatformFont
1764 FSRef ref;
1765 if (ATSFontGetFileReference(FMGetATSFontRefFromFont(fontID), &ref) != noErr)
1766 return ret;
1767 ret.filename = QByteArray(128, 0);
1768 ret.index = fontID;
1769 FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
1770}else
1771#endif
1772{
1773 FSSpec spec;
1774 if (ATSFontGetFileSpecification(FMGetATSFontRefFromFont(fontID), &spec) != noErr)
1775 return ret;
1776
1777 FSRef ref;
1778 FSpMakeFSRef(&spec, &ref);
1779 ret.filename = QByteArray(128, 0);
1780 ret.index = fontID;
1781 FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
1782}
1783 return ret;
1784}
1785
1786QByteArray QFontEngineMac::getSfntTable(uint tag) const
1787{
1788 ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1789
1790 ByteCount length;
1791 OSStatus status = ATSFontGetTable(atsFont, tag, 0, 0, 0, &length);
1792 if (status != noErr)
1793 return QByteArray();
1794 QByteArray table(length, 0);
1795 // CTFontCopyTable
1796 status = ATSFontGetTable(atsFont, tag, 0, table.length(), table.data(), &length);
1797 if (status != noErr)
1798 return QByteArray();
1799 return table;
1800}
1801
1802QFontEngine::Properties QFontEngineMac::properties() const
1803{
1804 QFontEngine::Properties props;
1805 ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
1806 quint16 tmp;
1807 // CTFontGetUnitsPerEm
1808 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 18, 2, &tmp, 0) == noErr)
1809 props.emSquare = qFromBigEndian<quint16>(tmp);
1810 struct {
1811 qint16 xMin;
1812 qint16 yMin;
1813 qint16 xMax;
1814 qint16 yMax;
1815 } bbox;
1816 bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
1817 // CTFontGetBoundingBox
1818 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 36, 8, &bbox, 0) == noErr) {
1819 bbox.xMin = qFromBigEndian<quint16>(bbox.xMin);
1820 bbox.yMin = qFromBigEndian<quint16>(bbox.yMin);
1821 bbox.xMax = qFromBigEndian<quint16>(bbox.xMax);
1822 bbox.yMax = qFromBigEndian<quint16>(bbox.yMax);
1823 }
1824 struct {
1825 qint16 ascender;
1826 qint16 descender;
1827 qint16 linegap;
1828 } metrics;
1829 metrics.ascender = metrics.descender = metrics.linegap = 0;
1830 // CTFontGetAscent, etc.
1831 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'h', 'e', 'a'), 4, 6, &metrics, 0) == noErr) {
1832 metrics.ascender = qFromBigEndian<quint16>(metrics.ascender);
1833 metrics.descender = qFromBigEndian<quint16>(metrics.descender);
1834 metrics.linegap = qFromBigEndian<quint16>(metrics.linegap);
1835 }
1836 props.ascent = metrics.ascender;
1837 props.descent = -metrics.descender;
1838 props.leading = metrics.linegap;
1839 props.boundingBox = QRectF(bbox.xMin, -bbox.yMax,
1840 bbox.xMax - bbox.xMin,
1841 bbox.yMax - bbox.yMin);
1842 props.italicAngle = 0;
1843 props.capHeight = props.ascent;
1844
1845 qint16 lw = 0;
1846 // fonts lie
1847 if (ATSFontGetTable(atsFont, MAKE_TAG('p', 'o', 's', 't'), 10, 2, &lw, 0) == noErr)
1848 lw = qFromBigEndian<quint16>(lw);
1849 props.lineWidth = lw;
1850
1851 // CTFontCopyPostScriptName
1852 QCFString psName;
1853 if (ATSFontGetPostScriptName(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &psName) == noErr)
1854 props.postscriptName = QString(psName).toUtf8();
1855 props.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(props.postscriptName);
1856 return props;
1857}
1858
1859void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1860{
1861 ATSUStyle unscaledStyle;
1862 ATSUCreateAndCopyStyle(style, &unscaledStyle);
1863
1864 int emSquare = properties().emSquare.toInt();
1865
1866 const int maxAttributeCount = 4;
1867 ATSUAttributeTag tags[maxAttributeCount + 1];
1868 ByteCount sizes[maxAttributeCount + 1];
1869 ATSUAttributeValuePtr values[maxAttributeCount + 1];
1870 int attributeCount = 0;
1871
1872 Fixed size = FixRatio(emSquare, 1);
1873 tags[attributeCount] = kATSUSizeTag;
1874 sizes[attributeCount] = sizeof(size);
1875 values[attributeCount] = &size;
1876 ++attributeCount;
1877
1878 Q_ASSERT(attributeCount < maxAttributeCount + 1);
1879 OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values);
1880 Q_ASSERT(err == noErr);
1881 Q_UNUSED(err);
1882
1883 // various CTFont metrics functions: CTFontGetBoundingRectsForGlyphs, CTFontGetAdvancesForGlyphs
1884 GlyphID atsuGlyph = glyph;
1885 ATSGlyphScreenMetrics atsuMetrics;
1886 ATSUGlyphGetScreenMetrics(unscaledStyle, 1, &atsuGlyph, 0,
1887 /* iForcingAntiAlias =*/ false,
1888 /* iAntiAliasSwitch =*/true,
1889 &atsuMetrics);
1890
1891 metrics->width = int(atsuMetrics.width);
1892 metrics->height = int(atsuMetrics.height);
1893 metrics->x = QFixed::fromReal(atsuMetrics.topLeft.x);
1894 metrics->y = -QFixed::fromReal(atsuMetrics.topLeft.y);
1895 metrics->xoff = QFixed::fromReal(atsuMetrics.deviceAdvance.x);
1896 metrics->yoff = QFixed::fromReal(atsuMetrics.deviceAdvance.y);
1897
1898 QFixedPoint p;
1899 addGlyphsToPathHelper(unscaledStyle, &glyph, &p, 1, path);
1900
1901 ATSUDisposeStyle(unscaledStyle);
1902}
1903#endif // !QT_MAC_USE_COCOA
1904
1905QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.