188void QCoreTextFontEngine::init()
194 QCFString name = CTFontCopyName(ctfont, kCTFontUniqueNameKey);
195 face_id.filename = QString::fromCFString(name).toUtf8();
196 face_id.variableAxes = fontDef.variableAxisValues;
198 QCFString family = CTFontCopyFamilyName(ctfont);
199 fontDef.families = QStringList(family);
201 QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute);
202 fontDef.styleName = styleName;
205 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
207 const auto requestedStyle = fontDef.style;
209 if (traits & kCTFontColorGlyphsTrait)
210 glyphFormat = QFontEngine::Format_ARGB;
211 else if (shouldSmoothFont() && fontSmoothing() == FontSmoothing::Subpixel)
212 glyphFormat = QFontEngine::Format_A32;
214 glyphFormat = QFontEngine::Format_A8;
216 static const auto getTraitValue = [](CFDictionaryRef allTraits, CFStringRef trait) ->
float {
217 if (CFDictionaryContainsKey(allTraits, trait)) {
218 CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait);
220 CFNumberGetValue(traitNum, kCFNumberFloatType, &v);
226 QCFType<CFDictionaryRef> allTraits = CTFontCopyTraits(ctfont);
227 int slant =
static_cast<
int>(getTraitValue(allTraits, kCTFontSlantTrait) * 500 + 500);
228 if (traits & kCTFontItalicTrait)
229 fontDef.style = QFont::StyleItalic;
230 else if (slant > 500)
231 fontDef.style = QFont::StyleOblique;
233 fontDef.style = QFont::StyleNormal;
235 if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait) && !qEnvironmentVariableIsSet(
"QT_NO_SYNTHESIZED_BOLD"))
236 synthesisFlags |= SynthesizedBold;
238 fontDef.weight = QCoreTextFontEngine::qtWeightFromCFWeight(getTraitValue(allTraits, kCTFontWeightTrait));
240 if (requestedStyle != QFont::StyleNormal && fontDef.style == QFont::StyleNormal && !qEnvironmentVariableIsSet(
"QT_NO_SYNTHESIZED_ITALIC"))
241 synthesisFlags |= SynthesizedItalic;
244 QByteArray os2Table = getSfntTable(QFont::Tag(
"OS/2").value());
245 unsigned emSize = CTFontGetUnitsPerEm(ctfont);
246 if (os2Table.size() >= 10) {
247 fsType = qFromBigEndian<quint16>(os2Table.constData() + 8);
249 qint16 width = qAbs(qFromBigEndian<qint16>(os2Table.constData() + 2));
250 avgCharWidth = QFixed::fromReal(width * fontDef.pixelSize / emSize);
252 avgCharWidth = QFontEngine::averageCharWidth();
254 underlineThickness = QFixed::fromReal(CTFontGetUnderlineThickness(ctfont));
255 underlinePos = -QFixed::fromReal(CTFontGetUnderlinePosition(ctfont));
257 cache_cost = (CTFontGetAscent(ctfont) + CTFontGetDescent(ctfont)) * avgCharWidth.toInt() * 2000;
259 kerningPairsLoaded =
false;
261 if (QCFType<CFArrayRef> variationAxes = CTFontCopyVariationAxes(ctfont)) {
262 CFIndex count = CFArrayGetCount(variationAxes);
263 for (CFIndex i = 0; i < count; ++i) {
264 CFDictionaryRef variationAxis = CFDictionaryRef(CFArrayGetValueAtIndex(variationAxes, i));
266 QFontVariableAxis fontVariableAxis;
267 if (CFNumberRef tagRef = (CFNumberRef) CFDictionaryGetValue(variationAxis,
268 kCTFontVariationAxisIdentifierKey)) {
270 CFNumberGetValue(tagRef, kCFNumberIntType, &tag);
271 if (
auto maybeTag = QFont::Tag::fromValue(tag))
272 fontVariableAxis.setTag(*maybeTag);
275 if (CFNumberRef minimumValueRef = (CFNumberRef) CFDictionaryGetValue(variationAxis,
276 kCTFontVariationAxisMinimumValueKey)) {
278 CFNumberGetValue(minimumValueRef, kCFNumberFloatType, &minimumValue);
279 fontVariableAxis.setMinimumValue(minimumValue);
282 if (CFNumberRef maximumValueRef = (CFNumberRef) CFDictionaryGetValue(variationAxis,
283 kCTFontVariationAxisMaximumValueKey)) {
285 CFNumberGetValue(maximumValueRef, kCFNumberFloatType, &maximumValue);
286 fontVariableAxis.setMaximumValue(maximumValue);
289 if (CFNumberRef defaultValueRef = (CFNumberRef) CFDictionaryGetValue(variationAxis,
290 kCTFontVariationAxisDefaultValueKey)) {
292 CFNumberGetValue(defaultValueRef, kCFNumberFloatType, &defaultValue);
293 fontVariableAxis.setDefaultValue(defaultValue);
296 if (CFStringRef nameRef = (CFStringRef) CFDictionaryGetValue(variationAxis,
297 kCTFontVariationAxisNameKey)) {
298 fontVariableAxis.setName(QString::fromCFString(nameRef));
301 variableAxisList.append(fontVariableAxis);
347int QCoreTextFontEngine::stringToCMap(
const QChar *str,
int len, QGlyphLayout *glyphs,
348 int *nglyphs, QFontEngine::ShaperFlags flags)
const
350 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
351 if (*nglyphs < len) {
356 QVarLengthArray<CGGlyph> cgGlyphs(len, 0);
357 CTFontGetGlyphsForCharacters(ctfont, (
const UniChar*)str, cgGlyphs.data(), len);
360 int mappedGlyphs = 0;
361 QStringIterator it(str, str + len);
362 while (it.hasNext()) {
363 qsizetype idx = it.index();
364 char32_t ucs4 = it.next();
365 glyphs->glyphs[glyph_pos] = cgGlyphs[idx];
367 cgGlyphs[glyph_pos] = cgGlyphs[idx];
368 if (glyphs->glyphs[glyph_pos] != 0 || isIgnorableChar(ucs4))
373 *nglyphs = glyph_pos;
374 glyphs->numGlyphs = glyph_pos;
376 if (!(flags & GlyphIndicesOnly))
377 loadAdvancesForGlyphs(cgGlyphs, glyphs);
382glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
386 CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontOrientationHorizontal, &g, 0, 1);
387 if (synthesisFlags & QFontEngine::SynthesizedItalic) {
388 rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW;
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;
395 CTFontGetAdvancesForGlyphs(ctfont, kCTFontOrientationHorizontal, &g, advances, 1);
396 ret.xoff = QFixed::fromReal(advances[0].width);
397 ret.yoff = QFixed::fromReal(advances[0].height);
447void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y,
const QTextItemInt &ti,
int paintDeviceHeight)
449 QVarLengthArray<QFixedPoint> positions;
450 QVarLengthArray<glyph_t> glyphs;
452 matrix.translate(x, y);
453 getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
454 if (glyphs.size() == 0)
457 CGContextSetFontSize(ctx, fontDef.pixelSize);
459 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
461 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
464 Q_UNUSED(CGAffineTransformConcat(cgMatrix, oldTextMatrix));
466 if (synthesisFlags & QFontEngine::SynthesizedItalic)
467 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
469 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
471 CGContextSetTextMatrix(ctx, cgMatrix);
473 CGContextSetTextDrawingMode(ctx, kCGTextFill);
475 QVarLengthArray<CGPoint> cgPositions(glyphs.size());
476 QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
477 const qreal firstX = positions[0].x.toReal();
478 const qreal firstY = positions[0].y.toReal();
479 for (
int i = 0; i < glyphs.size(); ++i) {
480 cgPositions[i].x = positions[i].x.toReal() - firstX;
481 cgPositions[i].y = firstY - positions[i].y.toReal();
482 cgGlyphs[i] = glyphs[i];
487 CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
488 CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx);
490 if (synthesisFlags & QFontEngine::SynthesizedBold) {
491 QTransform matrix(cgMatrix.a, cgMatrix.b, cgMatrix.c, cgMatrix.d, cgMatrix.tx, cgMatrix.ty);
493 qreal boldOffset = 0.5 * lineThickness().toReal();
495 qt_scaleForTransform(matrix, &scale);
498 CGContextSetTextPosition(ctx,
499 positions[0].x.toReal() + boldOffset,
500 positions[0].y.toReal());
501 CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx);
504 CGContextSetTextMatrix(ctx, oldTextMatrix);
519 switch(element->type) {
520 case kCGPathElementMoveToPoint:
521 myInfo->path->moveTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
522 element->points[0].y + myInfo->pos.y());
524 case kCGPathElementAddLineToPoint:
525 myInfo->path->lineTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
526 element->points[0].y + myInfo->pos.y());
528 case kCGPathElementAddQuadCurveToPoint:
529 myInfo->path->quadTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
530 element->points[0].y + myInfo->pos.y(),
531 (element->points[1].x * myInfo->stretch) + myInfo->pos.x(),
532 element->points[1].y + myInfo->pos.y());
534 case kCGPathElementAddCurveToPoint:
535 myInfo->path->cubicTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
536 element->points[0].y + myInfo->pos.y(),
537 (element->points[1].x * myInfo->stretch) + myInfo->pos.x(),
538 element->points[1].y + myInfo->pos.y(),
539 (element->points[2].x * myInfo->stretch) + myInfo->pos.x(),
540 element->points[2].y + myInfo->pos.y());
542 case kCGPathElementCloseSubpath:
543 myInfo->path->closeSubpath();
546 qCWarning(lcQpaFonts) <<
"Unhandled path transform type: " << element->type;
551void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
int nGlyphs,
552 QPainterPath *path, QTextItem::RenderFlags)
554 if (hasColorGlyphs())
557 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
558 cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
560 if (synthesisFlags & QFontEngine::SynthesizedItalic)
561 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
563 qreal stretch = fontDef.stretch ? qreal(fontDef.stretch) / 100 : 1.0;
564 for (
int i = 0; i < nGlyphs; ++i) {
565 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
566 ConvertPathInfo info(path, positions[i].toPointF(), stretch);
567 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
583glyph_metrics_t QCoreTextFontEngine::alphaMapBoundingBox(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &matrix, GlyphFormat format)
585 if (matrix.type() > QTransform::TxScale)
586 return QFontEngine::alphaMapBoundingBox(glyph, subPixelPosition, matrix, format);
588 glyph_metrics_t br = boundingBox(glyph);
590 QTransform xform = matrix;
591 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
592 xform.scale(fontDef.stretch / 100.0, 1.0);
593 qcoretextfontengine_scaleMetrics(br, xform);
597 br.width = -br.width;
599 br.height = -br.height;
601 if (format == QFontEngine::Format_A8 || format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB) {
681QCoreTextFontEngine::FontSmoothing QCoreTextFontEngine::fontSmoothing()
683 static const FontSmoothing cachedFontSmoothing = [] {
684 static const int kSize = 10;
685 QCFType<CTFontRef> font = CTFontCreateWithName(CFSTR(
"Helvetica"), kSize,
nullptr);
687 UniChar character(
'X'); CGGlyph glyph;
688 CTFontGetGlyphsForCharacters(font, &character, &glyph, 1);
690 auto drawGlyph = [&](
bool smooth) -> QImage {
691 QImage image(kSize, kSize, QImage::Format_RGB32);
694 QMacCGContext ctx(&image);
695 CGContextSetTextDrawingMode(ctx, kCGTextFill);
696 CGContextSetGrayFillColor(ctx, 1, 1);
700 CGContextSetShouldSmoothFonts(ctx, smooth);
702 CTFontDrawGlyphs(font, &glyph, &CGPointZero, 1, ctx);
706 QImage nonSmoothed = drawGlyph(
false);
707 QImage smoothed = drawGlyph(
true);
709 FontSmoothing fontSmoothing = FontSmoothing::Disabled;
711 for (
int x = 0; x < kSize; ++x) {
712 for (
int y = 0; y < kSize; ++y) {
713 QRgb sp = smoothed.pixel(x, y);
714 if (qRed(sp) != qGreen(sp) || qRed(sp) != qBlue(sp)) {
715 fontSmoothing = FontSmoothing::Subpixel;
719 if (sp != nonSmoothed.pixel(x, y))
720 fontSmoothing = FontSmoothing::Grayscale;
725 auto defaults = [NSUserDefaults standardUserDefaults];
726 qCDebug(lcQpaFonts) <<
"Resolved font smoothing algorithm. Defaults ="
727 << [[defaults dictionaryRepresentation] dictionaryWithValuesForKeys:@[
728 @
"AppleFontSmoothing",
729 @
"CGFontRenderingFontSmoothingDisabled"
730 ]] <<
"Result =" << fontSmoothing;
732 return fontSmoothing;
735 return cachedFontSmoothing;
771QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &matrix,
const QColor &color)
773 glyph_metrics_t br = alphaMapBoundingBox(glyph, subPixelPosition, matrix, glyphFormat);
775 if (glyphBoundingBoxExceedsLimit(br.width.toReal(),
777 effectivePixelSizeSquared(matrix))) {
781 QImage::Format imageFormat = hasColorGlyphs() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
782 QImage im(br.width.ceil().toInt(), br.height.ceil().toInt(), imageFormat);
783 if (!im.width() || !im.height())
786 auto cgImageFormat = qt_mac_cgImageFormatForImage(im);
790 QCFType<CGContextRef> ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
791 cgImageFormat->bitsPerComponent, im.bytesPerLine(), cgImageFormat->colorSpace,
792 cgImageFormat->bitmapInfo);
795 CGContextSetShouldAntialias(ctx, shouldAntialias());
797 const bool shouldSmooth = shouldSmoothFont();
798 CGContextSetShouldSmoothFonts(ctx, shouldSmooth);
800#if defined(Q_OS_MACOS)
801 auto glyphColor = [&] {
802 if (shouldSmooth && fontSmoothing() == Grayscale) {
813 if (
auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
814 if (platformTheme->colorScheme() != Qt::ColorScheme::Dark)
815 return kCGColorBlack;
818 return kCGColorWhite;
821 const bool blackOnWhiteGlyphs = glyphColor == kCGColorBlack;
822 if (blackOnWhiteGlyphs)
828 CGContextSetFontSize(ctx, fontDef.pixelSize);
830 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
832 if (synthesisFlags & QFontEngine::SynthesizedItalic)
833 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
835 if (!hasColorGlyphs())
836 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
838 if (matrix.isScaling())
839 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMakeScale(matrix.m11(), matrix.m22()));
841 CGGlyph cgGlyph = glyph;
843 qreal pos_x = -br.x.truncate() + subPixelPosition.x.toReal();
844 qreal pos_y = im.height() + br.y.toReal() - subPixelPosition.y.toReal();
846 if (!hasColorGlyphs()) {
847 CGContextSetTextMatrix(ctx, cgMatrix);
848#if defined(Q_OS_MACOS)
849 CGContextSetFillColorWithColor(ctx, CGColorGetConstantColor(glyphColor));
851 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
853 CGContextSetTextDrawingMode(ctx, kCGTextFill);
854 CGContextSetTextPosition(ctx, pos_x, pos_y);
856 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
858 if (synthesisFlags & QFontEngine::SynthesizedBold) {
859 qreal boldOffset = 0.5 * lineThickness().toReal();
862 qt_scaleForTransform(matrix, &scale);
865 CGContextSetTextPosition(ctx, pos_x + boldOffset, pos_y);
866 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
869 CGContextSetRGBFillColor(ctx, color.redF(), color.greenF(), color.blueF(), color.alphaF());
874 CGContextTranslateCTM(ctx, pos_x, pos_y);
875 CGContextConcatCTM(ctx, cgMatrix);
879 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
882 if (expectsGammaCorrectedBlending())
883 qGamma_correct_back_to_linear_cs(&im);
885#if defined(Q_OS_MACOS)
886 if (blackOnWhiteGlyphs)
898QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &x)
900 if (x.type() > QTransform::TxScale)
901 return QFontEngine::alphaMapForGlyph(glyph, subPixelPosition, x);
903 QImage im = imageForGlyph(glyph, subPixelPosition, x);
905 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
907 for (
int y=0; y<im.height(); ++y) {
908 uint *src = (uint*) im.scanLine(y);
909 uchar *dst = alphaMap.scanLine(y);
910 for (
int x=0; x<im.width(); ++x) {
978void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metric)
980 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
982 qreal emSquare = CTFontGetUnitsPerEm(ctfont);
983 qreal scale = emSquare / CTFontGetSize(ctfont);
984 cgMatrix = CGAffineTransformScale(cgMatrix, scale, -scale);
986 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, (CGGlyph) glyph, &cgMatrix);
987 ConvertPathInfo info(path, QPointF(0,0));
988 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
990 *metric = boundingBox(glyph);
992 metric->width = QFixed::fromReal(metric->width.toReal() * scale);
993 metric->height = QFixed::fromReal(metric->height.toReal() * scale);
994 metric->x = QFixed::fromReal(metric->x.toReal() * scale);
995 metric->y = QFixed::fromReal(metric->y.toReal() * scale);
996 metric->xoff = QFixed::fromReal(metric->xoff.toReal() * scale);
997 metric->yoff = QFixed::fromReal(metric->yoff.toReal() * scale);
1040QFontEngine::Properties QCoreTextFontEngine::properties()
const
1044 QCFString psName, copyright;
1045 psName = CTFontCopyPostScriptName(ctfont);
1046 copyright = CTFontCopyName(ctfont, kCTFontCopyrightNameKey);
1047 result.postscriptName = QString::fromCFString(psName).toUtf8();
1048 result.copyright = QString::fromCFString(copyright).toUtf8();
1050 qreal emSquare = CTFontGetUnitsPerEm(ctfont);
1051 qreal scale = emSquare / CTFontGetSize(ctfont);
1053 CGRect cgRect = CTFontGetBoundingBox(ctfont);
1054 result.boundingBox = QRectF(cgRect.origin.x * scale,
1055 -CTFontGetAscent(ctfont) * scale,
1056 cgRect.size.width * scale,
1057 cgRect.size.height * scale);
1059 result.emSquare = emSquareSize();
1060 result.ascent = QFixed::fromReal(CTFontGetAscent(ctfont) * scale);
1061 result.descent = QFixed::fromReal(CTFontGetDescent(ctfont) * scale);
1062 result.leading = QFixed::fromReal(CTFontGetLeading(ctfont) * scale);
1063 result.italicAngle = QFixed::fromReal(CTFontGetSlantAngle(ctfont));
1064 result.capHeight = QFixed::fromReal(CTFontGetCapHeight(ctfont) * scale);
1065 result.lineWidth = QFixed::fromReal(CTFontGetUnderlineThickness(ctfont) * scale);
static void qcoretextfontengine_scaleMetrics(glyph_metrics_t &br, const QTransform &matrix)
static void convertCGPathToQPainterPath(void *info, const CGPathElement *element)
#define COMPARE_WEIGHT_DISTANCE(ct_weight, qt_weight)