1/*
2 * Copyright (C) 2005, 2006, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 Alexey Proskuryakov
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#import "config.h"
28#import "SimpleFontData.h"
29
30#import "BlockExceptions.h"
31#import "Font.h"
32#import "FontCache.h"
33#import "FontDescription.h"
34#import "FontServicesIOS.h"
35#import <CoreGraphics/CGFontInfo.h>
36#import <CoreText/CoreText.h>
37#import <float.h>
38#import <unicode/uchar.h>
39#import <wtf/Assertions.h>
40#import <wtf/StdLibExtras.h>
41
42namespace WebCore {
43
44static bool fontFamilyShouldNotBeUsedForArabic(CFStringRef fontFamilyName)
45{
46    if (!fontFamilyName)
47        return false;
48
49    // Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. <rdar://problem/9823975>
50    // FIXME <rdar://problem/12096835> remove this function once the above bug is fixed.
51    // Arial and Tahoma are have performance issues so don't use them as well.
52    return (CFStringCompare(CFSTR("Times New Roman"), fontFamilyName, 0) == kCFCompareEqualTo)
53           || (CFStringCompare(CFSTR("Arial"), fontFamilyName, 0) == kCFCompareEqualTo)
54           || (CFStringCompare(CFSTR("Tahoma"), fontFamilyName, 0) == kCFCompareEqualTo);
55}
56
57static bool fontHasVerticalGlyphs(CTFontRef ctFont)
58{
59    // The check doesn't look neat but this is what AppKit does for vertical writing...
60    RetainPtr<CFArrayRef> tableTags = adoptCF(CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions));
61    CFIndex numTables = CFArrayGetCount(tableTags.get());
62    for (CFIndex index = 0; index < numTables; ++index) {
63        CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tableTags.get(), index);
64        if (tag == kCTFontTableVhea || tag == kCTFontTableVORG)
65            return true;
66    }
67    return false;
68}
69
70void SimpleFontData::platformInit()
71{
72    m_syntheticBoldOffset = m_platformData.m_syntheticBold ? ceilf(m_platformData.size()  / 24.0f) : 0.f;
73    m_spaceGlyph = 0;
74    m_spaceWidth = 0;
75    unsigned unitsPerEm;
76    float ascent;
77    float descent;
78    float lineGap;
79    float lineSpacing;
80    float xHeight;
81    RetainPtr<CFStringRef> familyName;
82    if (CTFontRef ctFont = m_platformData.font()) {
83        FontServicesIOS fontService(ctFont);
84        ascent = ceilf(fontService.ascent());
85        descent = ceilf(fontService.descent());
86        lineSpacing = fontService.lineSpacing();
87        lineGap = fontService.lineGap();
88        xHeight = fontService.xHeight();
89        unitsPerEm = fontService.unitsPerEm();
90        familyName = adoptCF(CTFontCopyFamilyName(ctFont));
91    } else {
92        CGFontRef cgFont = m_platformData.cgFont();
93
94        unitsPerEm = CGFontGetUnitsPerEm(cgFont);
95
96        float pointSize = m_platformData.size();
97        ascent = lroundf(scaleEmToUnits(CGFontGetAscent(cgFont), unitsPerEm) * pointSize);
98        descent = lroundf(-scaleEmToUnits(-abs(CGFontGetDescent(cgFont)), unitsPerEm) * pointSize);
99        lineGap = lroundf(scaleEmToUnits(CGFontGetLeading(cgFont), unitsPerEm) * pointSize);
100        xHeight = scaleEmToUnits(CGFontGetXHeight(cgFont), unitsPerEm) * pointSize;
101
102        lineSpacing = ascent + descent + lineGap;
103        familyName = adoptCF(CGFontCopyFamilyName(cgFont));
104    }
105
106    m_fontMetrics.setUnitsPerEm(unitsPerEm);
107    m_fontMetrics.setAscent(ascent);
108    m_fontMetrics.setDescent(descent);
109    m_fontMetrics.setLineGap(lineGap);
110    m_fontMetrics.setLineSpacing(lineSpacing);
111    m_fontMetrics.setXHeight(xHeight);
112    m_shouldNotBeUsedForArabic = fontFamilyShouldNotBeUsedForArabic(familyName.get());
113
114    if (platformData().orientation() == Vertical && !isTextOrientationFallback())
115        m_hasVerticalGlyphs = fontHasVerticalGlyphs(m_platformData.ctFont());
116
117    if (!m_platformData.m_isEmoji)
118        return;
119
120    int thirdOfSize = m_platformData.size() / 3;
121    m_fontMetrics.setAscent(thirdOfSize);
122    m_fontMetrics.setDescent(thirdOfSize);
123    m_fontMetrics.setLineGap(thirdOfSize);
124    m_fontMetrics.setLineSpacing(0);
125}
126
127void SimpleFontData::platformCharWidthInit()
128{
129    m_avgCharWidth = 0;
130    m_maxCharWidth = 0;
131
132    // Fallback to a cross-platform estimate, which will populate these values if they are non-positive.
133    initCharWidths();
134}
135
136PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription&, float scaleFactor) const
137{
138    if (isCustomFont()) {
139        FontPlatformData scaledFontData(m_platformData);
140        scaledFontData.m_size = scaledFontData.m_size * scaleFactor;
141        return SimpleFontData::create(scaledFontData, true, false);
142    }
143
144    float size = m_platformData.size() * scaleFactor;
145    CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
146    RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(m_platformData.font()));
147    RetainPtr<CTFontRef> scaledFont = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr));
148    FontPlatformData scaledFontData(scaledFont.get(), size, m_platformData.isPrinterFont(), false, false, m_platformData.orientation());
149
150    if (scaledFontData.font()) {
151        if (m_platformData.m_syntheticBold)
152            fontTraits |= kCTFontBoldTrait;
153        if (m_platformData.m_syntheticOblique)
154            fontTraits |= kCTFontItalicTrait;
155
156        CTFontSymbolicTraits scaledFontTraits = CTFontGetSymbolicTraits(scaledFontData.font());
157        scaledFontData.m_syntheticBold = (fontTraits & kCTFontBoldTrait) && !(scaledFontTraits & kCTFontTraitBold);
158        scaledFontData.m_syntheticOblique = (fontTraits & kCTFontItalicTrait) && !(scaledFontTraits & kCTFontTraitItalic);
159
160        return fontCache().getCachedFontData(&scaledFontData);
161    }
162
163    return nullptr;
164}
165
166bool SimpleFontData::containsCharacters(const UChar*, int) const
167{
168    return false;
169}
170
171void SimpleFontData::determinePitch()
172{
173    CTFontRef ctFont = m_platformData.font();
174    m_treatAsFixedPitch = false;
175    if (!ctFont)
176        return; // CTFont is null in the case of SVG fonts for example.
177
178    RetainPtr<CFStringRef> fullName = adoptCF(CTFontCopyFullName(ctFont));
179    RetainPtr<CFStringRef> familyName = adoptCF(CTFontCopyFamilyName(ctFont));
180
181    m_treatAsFixedPitch = CGFontIsFixedPitch(m_platformData.cgFont()) || (fullName && (CFStringCompare(fullName.get(), CFSTR("Osaka-Mono"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare(fullName.get(), CFSTR("MS-PGothic"), kCFCompareCaseInsensitive) == kCFCompareEqualTo));
182    if (familyName && CFStringCompare(familyName.get(), CFSTR("Courier New"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
183        // Special case Courier New to not be treated as fixed pitch, as this will make use of a hacked space width which is undesireable for iPhone (see rdar://6269783).
184        m_treatAsFixedPitch = false;
185    }
186}
187
188CGFontRenderingStyle SimpleFontData::renderingStyle() const
189{
190    return kCGFontRenderingStyleAntialiasing | kCGFontRenderingStyleSubpixelPositioning | kCGFontRenderingStyleSubpixelQuantization | kCGFontAntialiasingStyleUnfiltered;
191}
192
193bool SimpleFontData::advanceForColorBitmapFont(Glyph, CGSize&) const
194{
195    return false;
196}
197
198} // namespace WebCore
199