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