Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2000 Dirk Mueller (mueller (at) kde.org)
      5  * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #import "config.h"
     24 #import "Font.h"
     25 
     26 #import "GlyphBuffer.h"
     27 #import "GraphicsContext.h"
     28 #import "Logging.h"
     29 #import "SimpleFontData.h"
     30 #import "WebCoreSystemInterface.h"
     31 #import <AppKit/AppKit.h>
     32 
     33 #define SYNTHETIC_OBLIQUE_ANGLE 14
     34 
     35 #ifdef __LP64__
     36 #define URefCon void*
     37 #else
     38 #define URefCon UInt32
     39 #endif
     40 
     41 using namespace std;
     42 
     43 namespace WebCore {
     44 
     45 bool Font::canReturnFallbackFontsForComplexText()
     46 {
     47     return true;
     48 }
     49 
     50 bool Font::canExpandAroundIdeographsInComplexText()
     51 {
     52     return true;
     53 }
     54 
     55 // CTFontGetVerticalTranslationsForGlyphs is different on Snow Leopard.  It returns values for a font-size of 1
     56 // without unitsPerEm applied.  We have to apply a transform that scales up to the point size and that also
     57 // divides by unitsPerEm.
     58 static bool hasBrokenCTFontGetVerticalTranslationsForGlyphs()
     59 {
     60 // Chromium runs the same binary on both Leopard and Snow Leopard, so the check has to happen at runtime.
     61 #if PLATFORM(CHROMIUM)
     62     static bool isCached = false;
     63     static bool result;
     64 
     65     if (!isCached) {
     66         SInt32 majorVersion = 0;
     67         SInt32 minorVersion = 0;
     68         Gestalt(gestaltSystemVersionMajor, &majorVersion);
     69         Gestalt(gestaltSystemVersionMinor, &minorVersion);
     70         result = majorVersion == 10 && minorVersion == 6;
     71         isCached = true;
     72     }
     73     return result;
     74 #elif defined(BUILDING_ON_SNOW_LEOPARD)
     75     return true;
     76 #else
     77     return false;
     78 #endif
     79 }
     80 
     81 static void showGlyphsWithAdvances(const FloatPoint& point, const SimpleFontData* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count)
     82 {
     83     CGContextSetTextPosition(context, point.x(), point.y());
     84 
     85     const FontPlatformData& platformData = font->platformData();
     86     if (!platformData.isColorBitmapFont()) {
     87         CGAffineTransform savedMatrix;
     88         bool isVertical = font->platformData().orientation() == Vertical;
     89         if (isVertical) {
     90             CGAffineTransform rotateLeftTransform = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
     91             savedMatrix = CGContextGetTextMatrix(context);
     92             CGAffineTransform runMatrix = CGAffineTransformConcat(savedMatrix, rotateLeftTransform);
     93             CGContextSetTextMatrix(context, runMatrix);
     94 
     95             CGAffineTransform translationsTransform;
     96             if (hasBrokenCTFontGetVerticalTranslationsForGlyphs()) {
     97                 translationsTransform = CGAffineTransformMake(platformData.m_size, 0, 0, platformData.m_size, 0, 0);
     98                 translationsTransform = CGAffineTransformConcat(translationsTransform, rotateLeftTransform);
     99                 CGFloat unitsPerEm = CGFontGetUnitsPerEm(platformData.cgFont());
    100                 translationsTransform = CGAffineTransformConcat(translationsTransform, CGAffineTransformMakeScale(1 / unitsPerEm, 1 / unitsPerEm));
    101             } else {
    102                 translationsTransform = rotateLeftTransform;
    103             }
    104             Vector<CGSize, 256> translations(count);
    105             CTFontGetVerticalTranslationsForGlyphs(platformData.ctFont(), glyphs, translations.data(), count);
    106 
    107             CGAffineTransform transform = CGAffineTransformInvert(CGContextGetTextMatrix(context));
    108 
    109             CGPoint position = FloatPoint(point.x(), point.y() + font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent());
    110             Vector<CGPoint, 256> positions(count);
    111             for (size_t i = 0; i < count; ++i) {
    112                 CGSize translation = CGSizeApplyAffineTransform(translations[i], translationsTransform);
    113                 positions[i] = CGPointApplyAffineTransform(CGPointMake(position.x - translation.width, position.y + translation.height), transform);
    114                 position.x += advances[i].width;
    115                 position.y += advances[i].height;
    116             }
    117             CGContextShowGlyphsAtPositions(context, glyphs, positions.data(), count);
    118             CGContextSetTextMatrix(context, savedMatrix);
    119         } else
    120             CGContextShowGlyphsWithAdvances(context, glyphs, advances, count);
    121     }
    122 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
    123     else {
    124         if (!count)
    125             return;
    126 
    127         Vector<CGPoint, 256> positions(count);
    128         CGAffineTransform matrix = CGAffineTransformInvert(CGContextGetTextMatrix(context));
    129         positions[0] = CGPointZero;
    130         for (size_t i = 1; i < count; ++i) {
    131             CGSize advance = CGSizeApplyAffineTransform(advances[i - 1], matrix);
    132             positions[i].x = positions[i - 1].x + advance.width;
    133             positions[i].y = positions[i - 1].y + advance.height;
    134         }
    135         CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context);
    136     }
    137 #endif
    138 }
    139 
    140 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
    141 {
    142     CGContextRef cgContext = context->platformContext();
    143 
    144     bool shouldSmoothFonts = true;
    145     bool changeFontSmoothing = false;
    146 
    147     switch(fontDescription().fontSmoothing()) {
    148     case Antialiased: {
    149         context->setShouldAntialias(true);
    150         shouldSmoothFonts = false;
    151         changeFontSmoothing = true;
    152         break;
    153     }
    154     case SubpixelAntialiased: {
    155         context->setShouldAntialias(true);
    156         shouldSmoothFonts = true;
    157         changeFontSmoothing = true;
    158         break;
    159     }
    160     case NoSmoothing: {
    161         context->setShouldAntialias(false);
    162         shouldSmoothFonts = false;
    163         changeFontSmoothing = true;
    164         break;
    165     }
    166     case AutoSmoothing: {
    167         // For the AutoSmooth case, don't do anything! Keep the default settings.
    168         break;
    169     }
    170     default:
    171         ASSERT_NOT_REACHED();
    172     }
    173 
    174     if (!shouldUseSmoothing()) {
    175         shouldSmoothFonts = false;
    176         changeFontSmoothing = true;
    177     }
    178 
    179     bool originalShouldUseFontSmoothing = false;
    180     if (changeFontSmoothing) {
    181         originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
    182         CGContextSetShouldSmoothFonts(cgContext, shouldSmoothFonts);
    183     }
    184 
    185     const FontPlatformData& platformData = font->platformData();
    186     NSFont* drawFont;
    187     if (!isPrinterFont()) {
    188         drawFont = [platformData.font() screenFont];
    189         if (drawFont != platformData.font())
    190             // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually).
    191             LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen.  Using screen font anyway, may result in incorrect metrics.",
    192                 [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
    193     } else {
    194         drawFont = [platformData.font() printerFont];
    195         if (drawFont != platformData.font())
    196             NSLog(@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.",
    197                 [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
    198     }
    199 
    200     CGContextSetFont(cgContext, platformData.cgFont());
    201 
    202     CGAffineTransform matrix = CGAffineTransformIdentity;
    203     if (drawFont && !platformData.isColorBitmapFont())
    204         memcpy(&matrix, [drawFont matrix], sizeof(matrix));
    205     matrix.b = -matrix.b;
    206     matrix.d = -matrix.d;
    207     if (platformData.m_syntheticOblique)
    208         matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0));
    209     CGContextSetTextMatrix(cgContext, matrix);
    210 
    211     if (drawFont) {
    212         wkSetCGFontRenderingMode(cgContext, drawFont);
    213         CGContextSetFontSize(cgContext, 1.0f);
    214     } else
    215         CGContextSetFontSize(cgContext, platformData.m_size);
    216 
    217 
    218     FloatSize shadowOffset;
    219     float shadowBlur;
    220     Color shadowColor;
    221     ColorSpace shadowColorSpace;
    222     ColorSpace fillColorSpace = context->fillColorSpace();
    223     context->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
    224 
    225     bool hasSimpleShadow = context->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && !platformData.isColorBitmapFont() && (!context->shadowsIgnoreTransforms() || context->getCTM().isIdentityOrTranslationOrFlipped());
    226     if (hasSimpleShadow) {
    227         // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
    228         context->clearShadow();
    229         Color fillColor = context->fillColor();
    230         Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
    231         context->setFillColor(shadowFillColor, shadowColorSpace);
    232         float shadowTextX = point.x() + shadowOffset.width();
    233         // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative.
    234         float shadowTextY = point.y() + shadowOffset.height() * (context->shadowsIgnoreTransforms() ? -1 : 1);
    235         showGlyphsWithAdvances(FloatPoint(shadowTextX, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
    236         if (font->syntheticBoldOffset())
    237             showGlyphsWithAdvances(FloatPoint(shadowTextX + font->syntheticBoldOffset(), shadowTextY), font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
    238         context->setFillColor(fillColor, fillColorSpace);
    239     }
    240 
    241     showGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
    242     if (font->syntheticBoldOffset())
    243         showGlyphsWithAdvances(FloatPoint(point.x() + font->syntheticBoldOffset(), point.y()), font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
    244 
    245     if (hasSimpleShadow)
    246         context->setShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
    247 
    248     if (changeFontSmoothing)
    249         CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
    250 }
    251 
    252 }
    253