191void QCoreTextFontEngine::init()
197 QCFString name = CTFontCopyName(ctfont, kCTFontUniqueNameKey);
198 face_id.filename = QString::fromCFString(name).toUtf8();
199 face_id.variableAxes = fontDef.variableAxisValues;
201 QCFString family = CTFontCopyFamilyName(ctfont);
202 fontDef.families = QStringList(family);
204 QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute);
205 fontDef.styleName = styleName;
208 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
210 if (traits & kCTFontColorGlyphsTrait)
211 glyphFormat = QFontEngine::Format_ARGB;
212 else if (shouldSmoothFont() && fontSmoothing() == FontSmoothing::Subpixel)
213 glyphFormat = QFontEngine::Format_A32;
215 glyphFormat = QFontEngine::Format_A8;
217 if (traits & kCTFontItalicTrait)
218 fontDef.style = QFont::StyleItalic;
220 static const auto getTraitValue = [](CFDictionaryRef allTraits, CFStringRef trait) ->
float {
221 if (CFDictionaryContainsKey(allTraits, trait)) {
222 CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait);
224 CFNumberGetValue(traitNum, kCFNumberFloatType, &v);
230 QCFType<CFDictionaryRef> allTraits = CTFontCopyTraits(ctfont);
231 int slant =
static_cast<
int>(getTraitValue(allTraits, kCTFontSlantTrait) * 500 + 500);
232 if (slant > 500 && !(traits & kCTFontItalicTrait))
233 fontDef.style = QFont::StyleOblique;
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 (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait) && !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;
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 QImage::Format imageFormat = hasColorGlyphs() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
776 QImage im(br.width.ceil().toInt(), br.height.ceil().toInt(), imageFormat);
777 if (!im.width() || !im.height())
780 auto cgImageFormat = qt_mac_cgImageFormatForImage(im);
784 QCFType<CGContextRef> ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
785 cgImageFormat->bitsPerComponent, im.bytesPerLine(), cgImageFormat->colorSpace,
786 cgImageFormat->bitmapInfo);
789 CGContextSetShouldAntialias(ctx, shouldAntialias());
791 const bool shouldSmooth = shouldSmoothFont();
792 CGContextSetShouldSmoothFonts(ctx, shouldSmooth);
794#if defined(Q_OS_MACOS)
795 auto glyphColor = [&] {
796 if (shouldSmooth && fontSmoothing() == Grayscale) {
807 if (
auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
808 if (platformTheme->colorScheme() != Qt::ColorScheme::Dark)
809 return kCGColorBlack;
812 return kCGColorWhite;
815 const bool blackOnWhiteGlyphs = glyphColor == kCGColorBlack;
816 if (blackOnWhiteGlyphs)
822 CGContextSetFontSize(ctx, fontDef.pixelSize);
824 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
826 if (synthesisFlags & QFontEngine::SynthesizedItalic)
827 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
829 if (!hasColorGlyphs())
830 cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
832 if (matrix.isScaling())
833 cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMakeScale(matrix.m11(), matrix.m22()));
835 CGGlyph cgGlyph = glyph;
837 qreal pos_x = -br.x.truncate() + subPixelPosition.x.toReal();
838 qreal pos_y = im.height() + br.y.toReal() - subPixelPosition.y.toReal();
840 if (!hasColorGlyphs()) {
841 CGContextSetTextMatrix(ctx, cgMatrix);
842#if defined(Q_OS_MACOS)
843 CGContextSetFillColorWithColor(ctx, CGColorGetConstantColor(glyphColor));
845 CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
847 CGContextSetTextDrawingMode(ctx, kCGTextFill);
848 CGContextSetTextPosition(ctx, pos_x, pos_y);
850 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
852 if (synthesisFlags & QFontEngine::SynthesizedBold) {
853 qreal boldOffset = 0.5 * lineThickness().toReal();
856 qt_scaleForTransform(matrix, &scale);
859 CGContextSetTextPosition(ctx, pos_x + boldOffset, pos_y);
860 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
863 CGContextSetRGBFillColor(ctx, color.redF(), color.greenF(), color.blueF(), color.alphaF());
868 CGContextTranslateCTM(ctx, pos_x, pos_y);
869 CGContextConcatCTM(ctx, cgMatrix);
873 CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
876 if (expectsGammaCorrectedBlending())
877 qGamma_correct_back_to_linear_cs(&im);
879#if defined(Q_OS_MACOS)
880 if (blackOnWhiteGlyphs)
892QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph,
const QFixedPoint &subPixelPosition,
const QTransform &x)
894 if (x.type() > QTransform::TxScale)
895 return QFontEngine::alphaMapForGlyph(glyph, subPixelPosition, x);
897 QImage im = imageForGlyph(glyph, subPixelPosition, x);
899 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
901 for (
int y=0; y<im.height(); ++y) {
902 uint *src = (uint*) im.scanLine(y);
903 uchar *dst = alphaMap.scanLine(y);
904 for (
int x=0; x<im.width(); ++x) {
972void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metric)
974 CGAffineTransform cgMatrix = CGAffineTransformIdentity;
976 qreal emSquare = CTFontGetUnitsPerEm(ctfont);
977 qreal scale = emSquare / CTFontGetSize(ctfont);
978 cgMatrix = CGAffineTransformScale(cgMatrix, scale, -scale);
980 QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, (CGGlyph) glyph, &cgMatrix);
981 ConvertPathInfo info(path, QPointF(0,0));
982 CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
984 *metric = boundingBox(glyph);
986 metric->width = QFixed::fromReal(metric->width.toReal() * scale);
987 metric->height = QFixed::fromReal(metric->height.toReal() * scale);
988 metric->x = QFixed::fromReal(metric->x.toReal() * scale);
989 metric->y = QFixed::fromReal(metric->y.toReal() * scale);
990 metric->xoff = QFixed::fromReal(metric->xoff.toReal() * scale);
991 metric->yoff = QFixed::fromReal(metric->yoff.toReal() * scale);
1034QFontEngine::Properties QCoreTextFontEngine::properties()
const
1038 QCFString psName, copyright;
1039 psName = CTFontCopyPostScriptName(ctfont);
1040 copyright = CTFontCopyName(ctfont, kCTFontCopyrightNameKey);
1041 result.postscriptName = QString::fromCFString(psName).toUtf8();
1042 result.copyright = QString::fromCFString(copyright).toUtf8();
1044 qreal emSquare = CTFontGetUnitsPerEm(ctfont);
1045 qreal scale = emSquare / CTFontGetSize(ctfont);
1047 CGRect cgRect = CTFontGetBoundingBox(ctfont);
1048 result.boundingBox = QRectF(cgRect.origin.x * scale,
1049 -CTFontGetAscent(ctfont) * scale,
1050 cgRect.size.width * scale,
1051 cgRect.size.height * scale);
1053 result.emSquare = emSquareSize();
1054 result.ascent = QFixed::fromReal(CTFontGetAscent(ctfont) * scale);
1055 result.descent = QFixed::fromReal(CTFontGetDescent(ctfont) * scale);
1056 result.leading = QFixed::fromReal(CTFontGetLeading(ctfont) * scale);
1057 result.italicAngle = QFixed::fromReal(CTFontGetSlantAngle(ctfont));
1058 result.capHeight = QFixed::fromReal(CTFontGetCapHeight(ctfont) * scale);
1059 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)