2 * Copyright (C) 2005, 2006, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 Alexey Proskuryakov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #import "SimpleFontData.h"
30 #import "BlockExceptions.h"
31 #import "CoreGraphicsSPI.h"
34 #import "FontDescription.h"
35 #import "FontServicesIOS.h"
36 #import <CoreText/CoreText.h>
38 #import <unicode/uchar.h>
39 #import <wtf/Assertions.h>
40 #import <wtf/StdLibExtras.h>
44 static bool fontFamilyShouldNotBeUsedForArabic(CFStringRef fontFamilyName)
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);
57 static bool fontHasVerticalGlyphs(CTFontRef ctFont)
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)
70 void SimpleFontData::platformInit()
72 m_syntheticBoldOffset = m_platformData.m_syntheticBold ? ceilf(m_platformData.size() / 24.0f) : 0.f;
82 float decorationThickness;
83 float underlinePosition;
84 RetainPtr<CFStringRef> familyName;
85 if (CTFontRef ctFont = m_platformData.font()) {
86 FontServicesIOS fontService(ctFont);
87 ascent = ceilf(fontService.ascent());
88 descent = ceilf(fontService.descent());
89 lineSpacing = fontService.lineSpacing();
90 lineGap = fontService.lineGap();
91 xHeight = fontService.xHeight();
92 decorationThickness = CTFontGetUnderlineThickness(ctFont);
93 underlinePosition = -CTFontGetUnderlinePosition(ctFont);
94 capHeight = fontService.capHeight();
95 unitsPerEm = fontService.unitsPerEm();
96 familyName = adoptCF(CTFontCopyFamilyName(ctFont));
98 CGFontRef cgFont = m_platformData.cgFont();
100 unitsPerEm = CGFontGetUnitsPerEm(cgFont);
102 float pointSize = m_platformData.size();
103 ascent = lroundf(scaleEmToUnits(CGFontGetAscent(cgFont), unitsPerEm) * pointSize);
104 descent = lroundf(-scaleEmToUnits(-abs(CGFontGetDescent(cgFont)), unitsPerEm) * pointSize);
105 lineGap = lroundf(scaleEmToUnits(CGFontGetLeading(cgFont), unitsPerEm) * pointSize);
106 xHeight = scaleEmToUnits(CGFontGetXHeight(cgFont), unitsPerEm) * pointSize;
107 capHeight = scaleEmToUnits(CGFontGetCapHeight(cgFont), unitsPerEm) * pointSize;
108 populateDecorationMetrics(m_platformData.size(), decorationThickness, underlinePosition);
110 lineSpacing = ascent + descent + lineGap;
111 familyName = adoptCF(CGFontCopyFamilyName(cgFont));
114 m_fontMetrics.setUnitsPerEm(unitsPerEm);
115 m_fontMetrics.setAscent(ascent);
116 m_fontMetrics.setDescent(descent);
117 m_fontMetrics.setLineGap(lineGap);
118 m_fontMetrics.setLineSpacing(lineSpacing);
119 m_fontMetrics.setXHeight(xHeight);
120 m_fontMetrics.setCapHeight(capHeight);
121 m_fontMetrics.setDecorationThickness(decorationThickness);
122 m_fontMetrics.setUnderlinePosition(underlinePosition);
123 m_shouldNotBeUsedForArabic = fontFamilyShouldNotBeUsedForArabic(familyName.get());
125 if (platformData().orientation() == Vertical && !isTextOrientationFallback())
126 m_hasVerticalGlyphs = fontHasVerticalGlyphs(m_platformData.ctFont());
128 if (!m_platformData.m_isEmoji)
131 int thirdOfSize = m_platformData.size() / 3;
132 m_fontMetrics.setAscent(thirdOfSize);
133 m_fontMetrics.setDescent(thirdOfSize);
134 m_fontMetrics.setLineGap(thirdOfSize);
135 m_fontMetrics.setLineSpacing(0);
138 void SimpleFontData::platformCharWidthInit()
143 // Fallback to a cross-platform estimate, which will populate these values if they are non-positive.
147 PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription&, float scaleFactor) const
149 if (isCustomFont()) {
150 FontPlatformData scaledFontData(m_platformData);
151 scaledFontData.m_size = scaledFontData.m_size * scaleFactor;
152 return SimpleFontData::create(scaledFontData, true, false);
155 float size = m_platformData.size() * scaleFactor;
156 CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
157 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(m_platformData.font()));
158 RetainPtr<CTFontRef> scaledFont = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr));
159 FontPlatformData scaledFontData(scaledFont.get(), size, m_platformData.isPrinterFont(), false, false, m_platformData.orientation());
161 if (scaledFontData.font()) {
162 if (m_platformData.m_syntheticBold)
163 fontTraits |= kCTFontBoldTrait;
164 if (m_platformData.m_syntheticOblique)
165 fontTraits |= kCTFontItalicTrait;
167 CTFontSymbolicTraits scaledFontTraits = CTFontGetSymbolicTraits(scaledFontData.font());
168 scaledFontData.m_syntheticBold = (fontTraits & kCTFontBoldTrait) && !(scaledFontTraits & kCTFontTraitBold);
169 scaledFontData.m_syntheticOblique = (fontTraits & kCTFontItalicTrait) && !(scaledFontTraits & kCTFontTraitItalic);
171 return fontCache().getCachedFontData(&scaledFontData);
177 bool SimpleFontData::containsCharacters(const UChar*, int) const
182 void SimpleFontData::determinePitch()
184 CTFontRef ctFont = m_platformData.font();
185 m_treatAsFixedPitch = false;
187 return; // CTFont is null in the case of SVG fonts for example.
189 RetainPtr<CFStringRef> fullName = adoptCF(CTFontCopyFullName(ctFont));
190 RetainPtr<CFStringRef> familyName = adoptCF(CTFontCopyFamilyName(ctFont));
192 m_treatAsFixedPitch = CGFontIsFixedPitch(m_platformData.cgFont()) || (fullName && (CFStringCompare(fullName.get(), CFSTR("Osaka-Mono"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare(fullName.get(), CFSTR("MS-PGothic"), kCFCompareCaseInsensitive) == kCFCompareEqualTo));
193 if (familyName && CFStringCompare(familyName.get(), CFSTR("Courier New"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
194 // 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).
195 m_treatAsFixedPitch = false;
199 CGFontRenderingStyle SimpleFontData::renderingStyle() const
201 return kCGFontRenderingStyleAntialiasing | kCGFontRenderingStyleSubpixelPositioning | kCGFontRenderingStyleSubpixelQuantization | kCGFontAntialiasingStyleUnfiltered;
204 bool SimpleFontData::advanceForColorBitmapFont(Glyph, CGSize&) const
209 } // namespace WebCore