src/gui/text/qfontengine_mac.mm
changeset 30 5dc02b23752f
parent 18 2f34d5167611
child 33 3e2da88830cd
--- a/src/gui/text/qfontengine_mac.mm	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/gui/text/qfontengine_mac.mm	Tue Jul 06 15:10:48 2010 +0300
@@ -53,6 +53,7 @@
 #include <qvarlengtharray.h>
 #include <qdebug.h>
 #include <qendian.h>
+#include <qmath.h>
 
 #include <ApplicationServices/ApplicationServices.h>
 #include <AppKit/AppKit.h>
@@ -161,9 +162,14 @@
     QCFString name;
     ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name);
 
+    transform = CGAffineTransformIdentity;
+    if (fontDef.stretch != 100) {
+        transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+    }
+
     QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize);
-    QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, 0);
-    ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, 0, symbolicTraits, symbolicTraits);
+    QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform);
+    ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, symbolicTraits, symbolicTraits);
 
     // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does
     // not exist for the given font. (for example italic)
@@ -225,8 +231,19 @@
     QFixed *outAdvances_y = glyphs->advances_y;
     glyph_t *initialGlyph = outGlyphs;
 
-    if (arraySize == 0)
-        return false;
+    if (arraySize == 0) {
+        // CoreText failed to shape the text we gave it, so we assume one glyph
+        // per character and build a list of invalid glyphs with zero advance
+        *nglyphs = len;
+        for (int i = 0; i < len; ++i) {
+            outGlyphs[i] = 0;
+            logClusters[i] = i;
+            outAdvances_x[i] = QFixed();
+            outAdvances_y[i] = QFixed();
+            outAttributes[i].clusterStart = true;
+        }
+        return true;
+    }
 
     const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft);
 
@@ -303,12 +320,20 @@
                 outGlyphs[idx] = tmpGlyphs[i] | fontIndex;
                 outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x);
                 outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y);
+                
+                if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+                    outAdvances_x[idx] = outAdvances_x[idx].round();
+                    outAdvances_y[idx] = outAdvances_y[idx].round();
+                }
             }
             CGSize lastGlyphAdvance;
             CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1);
 
             outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex;
-            outAdvances_x[rtl ? 0 : (glyphCount - 1)] = QFixed::fromReal(lastGlyphAdvance.width).ceil();
+            outAdvances_x[rtl ? 0 : (glyphCount - 1)] =
+                    (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+                    ? QFixed::fromReal(lastGlyphAdvance.width).round()
+                    : QFixed::fromReal(lastGlyphAdvance.width);
         }
         outGlyphs += glyphCount;
         outAttributes += glyphCount;
@@ -358,7 +383,10 @@
     if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) {
         synthesisFlags |= SynthesizedItalic;
     }
-
+    transform = CGAffineTransformIdentity;
+    if (fontDef.stretch != 100) {
+        transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+    }
     QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
     if (os2Table.size() >= 10)
         fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8));
@@ -378,8 +406,11 @@
 glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
 {
     QFixed w;
-    for (int i = 0; i < glyphs.numGlyphs; ++i)
-        w += glyphs.effectiveAdvance(i);
+    for (int i = 0; i < glyphs.numGlyphs; ++i) {
+        w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+             ? glyphs.effectiveAdvance(i).round()
+             : glyphs.effectiveAdvance(i);
+    }
     return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0);
 }
 glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
@@ -393,33 +424,51 @@
     ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
     CGSize advances[1];
     CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1);
-    ret.xoff = QFixed::fromReal(advances[0].width).ceil();
-    ret.yoff = QFixed::fromReal(advances[0].height).ceil();
+    ret.xoff = QFixed::fromReal(advances[0].width);
+    ret.yoff = QFixed::fromReal(advances[0].height);
+
+    if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+        ret.xoff = ret.xoff.round();
+        ret.yoff = ret.yoff.round();
+    }
+
     return ret;
 }
 
 QFixed QCoreTextFontEngine::ascent() const
 {
-    return QFixed::fromReal(CTFontGetAscent(ctfont)).ceil();
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? QFixed::fromReal(CTFontGetAscent(ctfont)).round()
+            : QFixed::fromReal(CTFontGetAscent(ctfont));
 }
 QFixed QCoreTextFontEngine::descent() const
 {
+    QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont));
+    if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+        d = d.round();
+
     // subtract a pixel to even out the historical +1 in QFontMetrics::height().
     // Fix in Qt 5.
-    return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil() - 1;
+    return d - 1;
 }
 QFixed QCoreTextFontEngine::leading() const
 {
-    return QFixed::fromReal(CTFontGetLeading(ctfont)).ceil();
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? QFixed::fromReal(CTFontGetLeading(ctfont)).round()
+            : QFixed::fromReal(CTFontGetLeading(ctfont));
 }
 QFixed QCoreTextFontEngine::xHeight() const
 {
-    return QFixed::fromReal(CTFontGetXHeight(ctfont)).ceil();
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round()
+            : QFixed::fromReal(CTFontGetXHeight(ctfont));
 }
 QFixed QCoreTextFontEngine::averageCharWidth() const
 {
     // ### Need to implement properly and get the information from the OS/2 Table.
-    return QFontEngine::averageCharWidth();
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? QFontEngine::averageCharWidth().round()
+            : QFontEngine::averageCharWidth();
 }
 
 qreal QCoreTextFontEngine::maxCharWidth() const
@@ -462,7 +511,7 @@
     if (synthesisFlags & QFontEngine::SynthesizedItalic)
         cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
 
-// ###    cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+    cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
 
     CGContextSetTextMatrix(ctx, cgMatrix);
 
@@ -585,7 +634,7 @@
     if (synthesisFlags & QFontEngine::SynthesizedItalic)
         cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
 
-// ###    cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+    cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
 
     CGContextSetTextMatrix(ctx, cgMatrix);
     CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
@@ -787,6 +836,7 @@
     int *mappedFonts;
     QTextEngine::ShaperFlags flags;
     QFontEngineMacMulti::ShaperItem *shaperItem;
+    unsigned int styleStrategy;
 };
 
 static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon,
@@ -856,6 +906,11 @@
         QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]);
         QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos);
 
+        if (nfo->styleStrategy & QFont::ForceIntegerMetrics) {
+            yAdvance = yAdvance.round();
+            xAdvance = xAdvance.round();
+        }
+
         if (glyphId != 0xffff || i == 0) {
             if (i < nfo->glyphs->numGlyphs)
             {
@@ -1032,6 +1087,7 @@
     nfo.callbackCalled = false;
     nfo.flags = flags;
     nfo.shaperItem = shaperItem;
+    nfo.styleStrategy = fontDef.styleStrategy;
 
     int prevNumGlyphs = *nglyphs;
 
@@ -1061,8 +1117,6 @@
                                        | kATSLineDisableAllJustification
                                        ;
 
-	layopts |= kATSLineUseDeviceMetrics;
-
         if (fontDef.styleStrategy & QFont::NoAntialias)
             layopts |= kATSLineNoAntiAliasing;
 
@@ -1366,14 +1420,22 @@
     for (int i = 0; i < glyphs->numGlyphs; ++i) {
         glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x);
         glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y);
+
+        if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+            glyphs->advances_x[i] = glyphs->advances_x[i].round();
+            glyphs->advances_y[i] = glyphs->advances_y[i].round();
+        }
     }
 }
 
 glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs)
 {
     QFixed w;
-    for (int i = 0; i < glyphs.numGlyphs; ++i)
-        w += glyphs.effectiveAdvance(i);
+    for (int i = 0; i < glyphs.numGlyphs; ++i) {
+        w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+             ? glyphs.effectiveAdvance(i).round()
+             : glyphs.effectiveAdvance(i);
+    }
     return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0);
 }
 
@@ -1398,39 +1460,58 @@
     gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x);
     gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y);
 
+    if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+        gm.x = gm.x.floor();
+        gm.y = gm.y.floor();
+        gm.xoff = gm.xoff.round();
+        gm.yoff = gm.yoff.round();
+    }
+
     return gm;
 }
 
 QFixed QFontEngineMac::ascent() const
 {
-    return m_ascent;
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? m_ascent.round()
+            : m_ascent;
 }
 
 QFixed QFontEngineMac::descent() const
 {
     // subtract a pixel to even out the historical +1 in QFontMetrics::height().
     // Fix in Qt 5.
-    return m_descent - 1;
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? m_descent.round() - 1
+            : m_descent;
 }
 
 QFixed QFontEngineMac::leading() const
 {
-    return m_leading;
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? m_leading.round()
+            : m_leading;
 }
 
 qreal QFontEngineMac::maxCharWidth() const
 {
-    return m_maxCharWidth;
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? qRound(m_maxCharWidth)
+            : m_maxCharWidth;
 }
 
 QFixed QFontEngineMac::xHeight() const
 {
-    return m_xHeight;
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? m_xHeight.round()
+            : m_xHeight;
 }
 
 QFixed QFontEngineMac::averageCharWidth() const
 {
-    return m_averageCharWidth;
+    return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+            ? m_averageCharWidth.round()
+            : m_averageCharWidth;
 }
 
 static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path)