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