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);
348int QCoreTextFontEngine::stringToCMap(
const QChar *str,
int len, QGlyphLayout *glyphs,
349 int *nglyphs, QFontEngine::ShaperFlags flags)
const
351 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
352 if (*nglyphs < len) {
357 QVarLengthArray<CGGlyph> cgGlyphs(len);
358 CTFontGetGlyphsForCharacters(ctfont, (
const UniChar*)str, cgGlyphs.data(), len);
361 int mappedGlyphs = 0;
362 QStringIterator it(str, str + len);
363 while (it.hasNext()) {
364 qsizetype idx = it.index();
365 char32_t ucs4 = it.next();
366 glyphs->glyphs[glyph_pos] = cgGlyphs[idx];
368 cgGlyphs[glyph_pos] = cgGlyphs[idx];
369 if (glyphs->glyphs[glyph_pos] != 0 || isIgnorableChar(ucs4))
374 *nglyphs = glyph_pos;
375 glyphs->numGlyphs = glyph_pos;
377 if (!(flags & GlyphIndicesOnly))
378 loadAdvancesForGlyphs(cgGlyphs, glyphs);
383glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
387 CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontOrientationHorizontal, &g, 0, 1);
388 if (synthesisFlags & QFontEngine::SynthesizedItalic) {
389 rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW;
391 ret.width = QFixed::fromReal(rect.size.width);
392 ret.height = QFixed::fromReal(rect.size.height);
393 ret.x = QFixed::fromReal(rect.origin.x);
394 ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
396 CTFontGetAdvancesForGlyphs(ctfont, kCTFontOrientationHorizontal, &g, advances, 1);
397 ret.xoff = QFixed::fromReal(advances[0].width);
398 ret.yoff = QFixed::fromReal(advances[0].height);
448void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y,
const QTextItemInt &ti,
int paintDeviceHeight)
450 QVarLengthArray<QFixedPoint> positions;
451 QVarLengthArray<glyph_t> glyphs;
453 matrix.translate(x, y);
454 getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
455 if (glyphs.size() == 0)
458 CGContextSetFontSize(ctx, fontDef.pixelSize);
460 CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
462 CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
465 Q_UNUSED(CGAffineTransformConcat(cgMatrix, oldTextMatrix));
467 if (synthesisFlags & QFontEngine::SynthesizedItalic)
468 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
470 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
472 CGContextSetTextMatrix(ctx, cgMatrix);
474 CGContextSetTextDrawingMode(ctx, kCGTextFill);
476 QVarLengthArray<CGPoint> cgPositions(glyphs.size());
477 QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
478 const qreal firstX = positions[0].x.toReal();
479 const qreal firstY = positions[0].y.toReal();
480 for (
int i = 0; i < glyphs.size(); ++i) {
481 cgPositions[i].x = positions[i].x.toReal() - firstX;
482 cgPositions[i].y = firstY - positions[i].y.toReal();
483 cgGlyphs[i] = glyphs[i];
488 CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
489 CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx);
491 if (synthesisFlags & QFontEngine::SynthesizedBold) {
492 QTransform matrix(cgMatrix.a, cgMatrix.b, cgMatrix.c, cgMatrix.d, cgMatrix.tx, cgMatrix.ty);
494 qreal boldOffset = 0.5 * lineThickness().toReal();
496 qt_scaleForTransform(matrix, &scale);
499 CGContextSetTextPosition(ctx,
500 positions[0].x.toReal() + boldOffset,
501 positions[0].y.toReal());
502 CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx);
505 CGContextSetTextMatrix(ctx, oldTextMatrix);
520 switch(element->type) {
521 case kCGPathElementMoveToPoint:
522 myInfo->path->moveTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
523 element->points[0].y + myInfo->pos.y());
525 case kCGPathElementAddLineToPoint:
526 myInfo->path->lineTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
527 element->points[0].y + myInfo->pos.y());
529 case kCGPathElementAddQuadCurveToPoint:
530 myInfo->path->quadTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
531 element->points[0].y + myInfo->pos.y(),
532 (element->points[1].x * myInfo->stretch) + myInfo->pos.x(),
533 element->points[1].y + myInfo->pos.y());
535 case kCGPathElementAddCurveToPoint:
536 myInfo->path->cubicTo((element->points[0].x * myInfo->stretch) + myInfo->pos.x(),
537 element->points[0].y + myInfo->pos.y(),
538 (element->points[1].x * myInfo->stretch) + myInfo->pos.x(),
539 element->points[1].y + myInfo->pos.y(),
540 (element->points[2].x * myInfo->stretch) + myInfo->pos.x(),
541 element->points[2].y + myInfo->pos.y());
543 case kCGPathElementCloseSubpath:
544 myInfo->path->closeSubpath();
547 qCWarning(lcQpaFonts) <<
"Unhandled path transform type: " << element->type;
552void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
int nGlyphs,
553 QPainterPath *path, QTextItem::RenderFlags)
555 if (hasColorGlyphs())
558 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
559 cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
561 if (synthesisFlags & QFontEngine::SynthesizedItalic)
562 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
564 qreal stretch = fontDef.stretch ? qreal(fontDef.stretch) / 100 : 1.0;
565 for (
int i = 0; i < nGlyphs; ++i) {
566 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
567 ConvertPathInfo info(path, positions[i].toPointF(), stretch);
568 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
584glyph_metrics_t QCoreTextFontEngine::alphaMapBoundingBox(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &matrix, GlyphFormat format)
586 if (matrix.type() > QTransform::TxScale)
587 return QFontEngine::alphaMapBoundingBox(glyph, subPixelPosition, matrix, format);
589 glyph_metrics_t br = boundingBox(glyph);
591 QTransform xform = matrix;
592 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
593 xform.scale(fontDef.stretch / 100.0, 1.0);
594 qcoretextfontengine_scaleMetrics(br, xform);
598 br.width = -br.width;
600 br.height = -br.height;
602 if (format == QFontEngine::Format_A8 || format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB) {
682QCoreTextFontEngine::FontSmoothing QCoreTextFontEngine::fontSmoothing()
684 static const FontSmoothing cachedFontSmoothing = [] {
685 static const int kSize = 10;
686 QCFType<CTFontRef> font = CTFontCreateWithName(CFSTR(
"Helvetica"), kSize,
nullptr);
688 UniChar character(
'X'); CGGlyph glyph;
689 CTFontGetGlyphsForCharacters(font, &character, &glyph, 1);
691 auto drawGlyph = [&](
bool smooth) -> QImage {
692 QImage image(kSize, kSize, QImage::Format_RGB32);
695 QMacCGContext ctx(&image);
696 CGContextSetTextDrawingMode(ctx, kCGTextFill);
697 CGContextSetGrayFillColor(ctx, 1, 1);
701 CGContextSetShouldSmoothFonts(ctx, smooth);
703 CTFontDrawGlyphs(font, &glyph, &CGPointZero, 1, ctx);
707 QImage nonSmoothed = drawGlyph(
false);
708 QImage smoothed = drawGlyph(
true);
710 FontSmoothing fontSmoothing = FontSmoothing::Disabled;
712 for (
int x = 0; x < kSize; ++x) {
713 for (
int y = 0; y < kSize; ++y) {
714 QRgb sp = smoothed.pixel(x, y);
715 if (qRed(sp) != qGreen(sp) || qRed(sp) != qBlue(sp)) {
716 fontSmoothing = FontSmoothing::Subpixel;
720 if (sp != nonSmoothed.pixel(x, y))
721 fontSmoothing = FontSmoothing::Grayscale;
726 auto defaults = [NSUserDefaults standardUserDefaults];
727 qCDebug(lcQpaFonts) <<
"Resolved font smoothing algorithm. Defaults ="
728 << [[defaults dictionaryRepresentation] dictionaryWithValuesForKeys:@[
729 @
"AppleFontSmoothing",
730 @
"CGFontRenderingFontSmoothingDisabled"
731 ]] <<
"Result =" << fontSmoothing;
733 return fontSmoothing;
736 return cachedFontSmoothing;
772QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &matrix,
const QColor &color)
774 glyph_metrics_t br = alphaMapBoundingBox(glyph, subPixelPosition, matrix, glyphFormat);
776 if (glyphBoundingBoxExceedsLimit(br.width.toReal(),
778 effectivePixelSizeSquared(matrix))) {
782 QImage::Format imageFormat = hasColorGlyphs() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
783 QImage im(br.width.ceil().toInt(), br.height.ceil().toInt(), imageFormat);
784 if (!im.width() || !im.height())
787 auto cgImageFormat = qt_mac_cgImageFormatForImage(im);
791 QCFType<CGContextRef> ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
792 cgImageFormat->bitsPerComponent, im.bytesPerLine(), cgImageFormat->colorSpace,
793 cgImageFormat->bitmapInfo);
796 CGContextSetShouldAntialias(ctx, shouldAntialias());
798 const bool shouldSmooth = shouldSmoothFont();
799 CGContextSetShouldSmoothFonts(ctx, shouldSmooth);
801#if defined(Q_OS_MACOS)
802 auto glyphColor = [&] {
803 if (shouldSmooth && fontSmoothing() == Grayscale) {
814 if (
auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
815 if (platformTheme->colorScheme() != Qt::ColorScheme::Dark)
816 return kCGColorBlack;
819 return kCGColorWhite;
822 const bool blackOnWhiteGlyphs = glyphColor == kCGColorBlack;
823 if (blackOnWhiteGlyphs)
829 CGContextSetFontSize(ctx, fontDef.pixelSize);
831 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
833 if (synthesisFlags & QFontEngine::SynthesizedItalic)
834 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
836 if (!hasColorGlyphs())
837 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
839 if (matrix.isScaling())
840 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMakeScale(matrix.m11(), matrix.m22()));
842 CGGlyph cgGlyph = glyph;
844 qreal pos_x = -br.x.truncate() + subPixelPosition.x.toReal();
845 qreal pos_y = im.height() + br.y.toReal() - subPixelPosition.y.toReal();
847 if (!hasColorGlyphs()) {
848 CGContextSetTextMatrix(ctx, cgMatrix);
849#if defined(Q_OS_MACOS)
850 CGContextSetFillColorWithColor(ctx, CGColorGetConstantColor(glyphColor));
852 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
854 CGContextSetTextDrawingMode(ctx, kCGTextFill);
855 CGContextSetTextPosition(ctx, pos_x, pos_y);
857 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
859 if (synthesisFlags & QFontEngine::SynthesizedBold) {
860 qreal boldOffset = 0.5 * lineThickness().toReal();
863 qt_scaleForTransform(matrix, &scale);
866 CGContextSetTextPosition(ctx, pos_x + boldOffset, pos_y);
867 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
870 CGContextSetRGBFillColor(ctx, color.redF(), color.greenF(), color.blueF(), color.alphaF());
875 CGContextTranslateCTM(ctx, pos_x, pos_y);
876 CGContextConcatCTM(ctx, cgMatrix);
880 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
883 if (expectsGammaCorrectedBlending())
884 qGamma_correct_back_to_linear_cs(&im);
886#if defined(Q_OS_MACOS)
887 if (blackOnWhiteGlyphs)
899QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &x)
901 if (x.type() > QTransform::TxScale)
902 return QFontEngine::alphaMapForGlyph(glyph, subPixelPosition, x);
904 QImage im = imageForGlyph(glyph, subPixelPosition, x);
906 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
908 for (
int y=0; y<im.height(); ++y) {
909 uint *src = (uint*) im.scanLine(y);
910 uchar *dst = alphaMap.scanLine(y);
911 for (
int x=0; x<im.width(); ++x) {
979void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metric)
981 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
983 qreal emSquare = CTFontGetUnitsPerEm(ctfont);
984 qreal scale = emSquare / CTFontGetSize(ctfont);
985 cgMatrix = CGAffineTransformScale(cgMatrix, scale, -scale);
987 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, (CGGlyph) glyph, &cgMatrix);
988 ConvertPathInfo info(path, QPointF(0,0));
989 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
991 *metric = boundingBox(glyph);
993 metric->width = QFixed::fromReal(metric->width.toReal() * scale);
994 metric->height = QFixed::fromReal(metric->height.toReal() * scale);
995 metric->x = QFixed::fromReal(metric->x.toReal() * scale);
996 metric->y = QFixed::fromReal(metric->y.toReal() * scale);
997 metric->xoff = QFixed::fromReal(metric->xoff.toReal() * scale);
998 metric->yoff = QFixed::fromReal(metric->yoff.toReal() * scale);
1041QFontEngine::Properties QCoreTextFontEngine::properties()
const
1045 QCFString psName, copyright;
1046 psName = CTFontCopyPostScriptName(ctfont);
1047 copyright = CTFontCopyName(ctfont, kCTFontCopyrightNameKey);
1048 result.postscriptName = QString::fromCFString(psName).toUtf8();
1049 result.copyright = QString::fromCFString(copyright).toUtf8();
1051 qreal emSquare = CTFontGetUnitsPerEm(ctfont);
1052 qreal scale = emSquare / CTFontGetSize(ctfont);
1054 CGRect cgRect = CTFontGetBoundingBox(ctfont);
1055 result.boundingBox = QRectF(cgRect.origin.x * scale,
1056 -CTFontGetAscent(ctfont) * scale,
1057 cgRect.size.width * scale,
1058 cgRect.size.height * scale);
1060 result.emSquare = emSquareSize();
1061 result.ascent = QFixed::fromReal(CTFontGetAscent(ctfont) * scale);
1062 result.descent = QFixed::fromReal(CTFontGetDescent(ctfont) * scale);
1063 result.leading = QFixed::fromReal(CTFontGetLeading(ctfont) * scale);
1064 result.italicAngle = QFixed::fromReal(CTFontGetSlantAngle(ctfont));
1065 result.capHeight = QFixed::fromReal(CTFontGetCapHeight(ctfont) * scale);
1066 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)