Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: Source/core/platform/graphics/mac/SimpleFontDataMac.mm

Issue 99103006: Moving GraphicsContext and dependencies from core to platform. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Final patch - fixes Android Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "core/platform/graphics/SimpleFontData.h"
29
30 #import <AppKit/AppKit.h>
31 #import <ApplicationServices/ApplicationServices.h>
32 #import <float.h>
33 #import <unicode/uchar.h>
34 #import "platform/SharedBuffer.h"
35 #import "core/platform/graphics/Font.h"
36 #import "core/platform/graphics/FontCache.h"
37 #import "platform/fonts/FontDescription.h"
38 #import "platform/geometry/FloatRect.h"
39 #import "platform/graphics/Color.h"
40 #import "platform/mac/BlockExceptions.h"
41 #import <wtf/Assertions.h>
42 #import <wtf/RetainPtr.h>
43 #import <wtf/StdLibExtras.h>
44 #import <wtf/UnusedParam.h>
45
46 @interface NSFont (WebAppKitSecretAPI)
47 - (BOOL)_isFakeFixedPitch;
48 @end
49
50 // The names of these constants were taken from history
51 // /trunk/WebKit/WebCoreSupport.subproj/WebTextRenderer.m@9311. The values
52 // were derived from the assembly of libWebKitSystemInterfaceLeopard.a.
53 enum CGFontRenderingMode {
54 kCGFontRenderingMode1BitPixelAligned = 0x0,
55 kCGFontRenderingModeAntialiasedPixelAligned = 0x1,
56 kCGFontRenderingModeAntialiased = 0xd
57 };
58
59 // Forward declare Mac SPIs.
60 extern "C" {
61 // Request for public API: rdar://13803586
62 bool CGFontGetGlyphAdvancesForStyle(CGFontRef font, CGAffineTransform* transform , CGFontRenderingMode renderingMode, ATSGlyphRef* glyph, size_t count, CGSize* a dvance);
63
64 // Request for public API: rdar://13803619
65 CTLineRef CTLineCreateWithUniCharProvider(const UniChar* (*provide)(CFIndex stri ngIndex, CFIndex* charCount, CFDictionaryRef* attributes, void* context), void ( *dispose)(const UniChar* chars, void* context), void* context);
66 }
67
68 static CGFontRenderingMode cgFontRenderingModeForNSFont(NSFont* font) {
69 if (!font)
70 return kCGFontRenderingModeAntialiasedPixelAligned;
71
72 switch ([font renderingMode]) {
73 case NSFontIntegerAdvancementsRenderingMode: return kCGFontRenderingMode 1BitPixelAligned;
74 case NSFontAntialiasedIntegerAdvancementsRenderingMode: return kCGFontRe nderingModeAntialiasedPixelAligned;
75 default: return kCGFontRenderingModeAntialiased;
76 }
77 }
78
79 using namespace std;
80
81 namespace WebCore {
82
83 static bool fontHasVerticalGlyphs(CTFontRef ctFont)
84 {
85 // The check doesn't look neat but this is what AppKit does for vertical wri ting...
86 RetainPtr<CFArrayRef> tableTags(AdoptCF, CTFontCopyAvailableTables(ctFont, k CTFontTableOptionNoOptions));
87 CFIndex numTables = CFArrayGetCount(tableTags.get());
88 for (CFIndex index = 0; index < numTables; ++index) {
89 CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(t ableTags.get(), index);
90 if (tag == kCTFontTableVhea || tag == kCTFontTableVORG)
91 return true;
92 }
93 return false;
94 }
95
96 static bool initFontData(SimpleFontData* fontData)
97 {
98 if (!fontData->platformData().cgFont())
99 return false;
100
101 return true;
102 }
103
104 static NSString *webFallbackFontFamily(void)
105 {
106 DEFINE_STATIC_LOCAL(RetainPtr<NSString>, webFallbackFontFamily, ([[NSFont sy stemFontOfSize:16.0f] familyName]));
107 return webFallbackFontFamily.get();
108 }
109
110 const SimpleFontData* SimpleFontData::getCompositeFontReferenceFontData(NSFont * key) const
111 {
112 if (key && !CFEqual(RetainPtr<CFStringRef>(AdoptCF, CTFontCopyPostScriptName (CTFontRef(key))).get(), CFSTR("LastResort"))) {
113 if (!m_derivedFontData)
114 m_derivedFontData = DerivedFontData::create(isCustomFont());
115 if (!m_derivedFontData->compositeFontReferences)
116 m_derivedFontData->compositeFontReferences.adoptCF(CFDictionaryCreat eMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, NULL));
117 else {
118 const SimpleFontData* found = static_cast<const SimpleFontData*>(CFD ictionaryGetValue(m_derivedFontData->compositeFontReferences.get(), static_cast< const void *>(key)));
119 if (found)
120 return found;
121 }
122 if (CFMutableDictionaryRef dictionary = m_derivedFontData->compositeFont References.get()) {
123 bool isUsingPrinterFont = platformData().isPrinterFont();
124 NSFont *substituteFont = isUsingPrinterFont ? [key printerFont] : [k ey screenFont];
125
126 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(toCTFontRef(su bstituteFont));
127 bool syntheticBold = platformData().syntheticBold() && !(traits & kC TFontBoldTrait);
128 bool syntheticOblique = platformData().syntheticOblique() && !(trait s & kCTFontItalicTrait);
129
130 FontPlatformData substitutePlatform(substituteFont, platformData().s ize(), isUsingPrinterFont, syntheticBold, syntheticOblique, platformData().orien tation(), platformData().widthVariant());
131 SimpleFontData* value = new SimpleFontData(substitutePlatform, isCus tomFont() ? CustomFontData::create(false) : 0);
132 if (value) {
133 CFDictionaryAddValue(dictionary, key, value);
134 return value;
135 }
136 }
137 }
138 return 0;
139 }
140
141 void SimpleFontData::platformInit()
142 {
143 m_syntheticBoldOffset = m_platformData.m_syntheticBold ? 1.0f : 0.f;
144
145 bool failedSetup = false;
146 if (!initFontData(this)) {
147 // Ack! Something very bad happened, like a corrupt font.
148 // Try looking for an alternate 'base' font for this renderer.
149
150 // Special case hack to use "Times New Roman" in place of "Times".
151 // "Times RO" is a common font whose family name is "Times".
152 // It overrides the normal "Times" family font.
153 // It also appears to have a corrupt regular variant.
154 NSString *fallbackFontFamily;
155 if ([[m_platformData.font() familyName] isEqual:@"Times"])
156 fallbackFontFamily = @"Times New Roman";
157 else
158 fallbackFontFamily = webFallbackFontFamily();
159
160 // Try setting up the alternate font.
161 // This is a last ditch effort to use a substitute font when something h as gone wrong.
162 #if !ERROR_DISABLED
163 RetainPtr<NSFont> initialFont = m_platformData.font();
164 #endif
165 if (m_platformData.font())
166 m_platformData.setFont([[NSFontManager sharedFontManager] convertFon t:m_platformData.font() toFamily:fallbackFontFamily]);
167 else
168 m_platformData.setFont([NSFont fontWithName:fallbackFontFamily size: m_platformData.size()]);
169
170 if (!initFontData(this)) {
171 if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
172 // OK, couldn't setup Times New Roman as an alternate to Times, fallback
173 // on the system font. If this fails we have no alternative lef t.
174 m_platformData.setFont([[NSFontManager sharedFontManager] conver tFont:m_platformData.font() toFamily:webFallbackFontFamily()]);
175 if (!initFontData(this)) {
176 // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
177 WTF_LOG_ERROR("unable to initialize with font %@", initialFo nt.get());
178 failedSetup = true;
179 }
180 } else {
181 // We tried the requested font and the system font. No joy. We h ave to give up.
182 WTF_LOG_ERROR("unable to initialize with font %@", initialFont.g et());
183 failedSetup = true;
184 }
185 }
186
187 // Report the problem.
188 WTF_LOG_ERROR("Corrupt font detected, using %@ in place of %@.",
189 [m_platformData.font() familyName], [initialFont.get() familyName]);
190 }
191
192 // If all else fails, try to set up using the system font.
193 // This is probably because Times and Times New Roman are both unavailable.
194 if (failedSetup) {
195 m_platformData.setFont([NSFont systemFontOfSize:[m_platformData.font() p ointSize]]);
196 WTF_LOG_ERROR("failed to set up font, using system font %s", m_platformD ata.font());
197 initFontData(this);
198 }
199
200 int iAscent;
201 int iDescent;
202 int iLineGap;
203 unsigned unitsPerEm;
204 iAscent = CGFontGetAscent(m_platformData.cgFont());
205 // Some fonts erroneously specify a positive descender value. We follow Core Text in assuming that
206 // such fonts meant the same distance, but in the reverse direction.
207 iDescent = -abs(CGFontGetDescent(m_platformData.cgFont()));
208 iLineGap = CGFontGetLeading(m_platformData.cgFont());
209 unitsPerEm = CGFontGetUnitsPerEm(m_platformData.cgFont());
210
211 float pointSize = m_platformData.m_size;
212 float ascent = scaleEmToUnits(iAscent, unitsPerEm) * pointSize;
213 float descent = -scaleEmToUnits(iDescent, unitsPerEm) * pointSize;
214 float lineGap = scaleEmToUnits(iLineGap, unitsPerEm) * pointSize;
215
216 // We need to adjust Times, Helvetica, and Courier to closely match the
217 // vertical metrics of their Microsoft counterparts that are the de facto
218 // web standard. The AppKit adjustment of 20% is too big and is
219 // incorrectly added to line spacing, so we use a 15% adjustment instead
220 // and add it to the ascent.
221 NSString *familyName = [m_platformData.font() familyName];
222 if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"H elvetica"] || [familyName isEqualToString:@"Courier"])
223 ascent += floorf(((ascent + descent) * 0.15f) + 0.5f);
224
225 // Compute and store line spacing, before the line metrics hacks are applied .
226 m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(li neGap));
227
228 // Hack Hiragino line metrics to allow room for marked text underlines.
229 // <rdar://problem/5386183>
230 if (descent < 3 && lineGap >= 3 && [familyName hasPrefix:@"Hiragino"]) {
231 lineGap -= 3 - descent;
232 descent = 3;
233 }
234
235 if (platformData().orientation() == Vertical && !isTextOrientationFallback() )
236 m_hasVerticalGlyphs = fontHasVerticalGlyphs(m_platformData.ctFont());
237
238 float xHeight;
239
240 if (platformData().orientation() == Horizontal) {
241 // Measure the actual character "x", since it's possible for it to exten d below the baseline, and we need the
242 // reported x-height to only include the portion of the glyph that is ab ove the baseline.
243 GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->pag e();
244 NSGlyph xGlyph = glyphPageZero ? glyphPageZero->glyphForCharacter('x') : 0;
245 if (xGlyph)
246 xHeight = -CGRectGetMinY(platformBoundsForGlyph(xGlyph));
247 else
248 xHeight = scaleEmToUnits(CGFontGetXHeight(m_platformData.cgFont()), unitsPerEm) * pointSize;
249 } else
250 xHeight = verticalRightOrientationFontData()->fontMetrics().xHeight();
251
252 m_fontMetrics.setUnitsPerEm(unitsPerEm);
253 m_fontMetrics.setAscent(ascent);
254 m_fontMetrics.setDescent(descent);
255 m_fontMetrics.setLineGap(lineGap);
256 m_fontMetrics.setXHeight(xHeight);
257 }
258
259 static CFDataRef copyFontTableForTag(FontPlatformData& platformData, FourCharCod e tableName)
260 {
261 return CGFontCopyTableForTag(platformData.cgFont(), tableName);
262 }
263
264 void SimpleFontData::platformCharWidthInit()
265 {
266 m_avgCharWidth = 0;
267 m_maxCharWidth = 0;
268
269 RetainPtr<CFDataRef> os2Table(AdoptCF, copyFontTableForTag(m_platformData, ' OS/2'));
270 if (os2Table && CFDataGetLength(os2Table.get()) >= 4) {
271 const UInt8* os2 = CFDataGetBytePtr(os2Table.get());
272 SInt16 os2AvgCharWidth = os2[2] * 256 + os2[3];
273 m_avgCharWidth = scaleEmToUnits(os2AvgCharWidth, m_fontMetrics.unitsPerE m()) * m_platformData.m_size;
274 }
275
276 RetainPtr<CFDataRef> headTable(AdoptCF, copyFontTableForTag(m_platformData, 'head'));
277 if (headTable && CFDataGetLength(headTable.get()) >= 42) {
278 const UInt8* head = CFDataGetBytePtr(headTable.get());
279 ushort uxMin = head[36] * 256 + head[37];
280 ushort uxMax = head[40] * 256 + head[41];
281 SInt16 xMin = static_cast<SInt16>(uxMin);
282 SInt16 xMax = static_cast<SInt16>(uxMax);
283 float diff = static_cast<float>(xMax - xMin);
284 m_maxCharWidth = scaleEmToUnits(diff, m_fontMetrics.unitsPerEm()) * m_pl atformData.m_size;
285 }
286
287 // Fallback to a cross-platform estimate, which will populate these values i f they are non-positive.
288 initCharWidths();
289 }
290
291 void SimpleFontData::platformDestroy()
292 {
293 if (!isCustomFont() && m_derivedFontData) {
294 // These come from the cache.
295 if (m_derivedFontData->smallCaps)
296 fontCache()->releaseFontData(m_derivedFontData->smallCaps.get());
297
298 if (m_derivedFontData->emphasisMark)
299 fontCache()->releaseFontData(m_derivedFontData->emphasisMark.get());
300 }
301 }
302
303 PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const Fo ntDescription& fontDescription, float scaleFactor) const
304 {
305 if (isCustomFont()) {
306 FontPlatformData scaledFontData(m_platformData);
307 scaledFontData.m_size = scaledFontData.m_size * scaleFactor;
308 return SimpleFontData::create(scaledFontData, CustomFontData::create(fal se));
309 }
310
311 BEGIN_BLOCK_OBJC_EXCEPTIONS;
312 float size = m_platformData.size() * scaleFactor;
313 FontPlatformData scaledFontData([[NSFontManager sharedFontManager] convertFo nt:m_platformData.font() toSize:size], size, m_platformData.isPrinterFont(), fal se, false, m_platformData.orientation());
314
315 // AppKit resets the type information (screen/printer) when you convert a fo nt to a different size.
316 // We have to fix up the font that we're handed back.
317 scaledFontData.setFont(fontDescription.usePrinterFont() ? [scaledFontData.fo nt() printerFont] : [scaledFontData.font() screenFont]);
318
319 if (scaledFontData.font()) {
320 NSFontManager *fontManager = [NSFontManager sharedFontManager];
321 NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_platformData.fo nt()];
322
323 if (m_platformData.m_syntheticBold)
324 fontTraits |= NSBoldFontMask;
325 if (m_platformData.m_syntheticOblique)
326 fontTraits |= NSItalicFontMask;
327
328 NSFontTraitMask scaledFontTraits = [fontManager traitsOfFont:scaledFontD ata.font()];
329 scaledFontData.m_syntheticBold = (fontTraits & NSBoldFontMask) && !(scal edFontTraits & NSBoldFontMask);
330 scaledFontData.m_syntheticOblique = (fontTraits & NSItalicFontMask) && ! (scaledFontTraits & NSItalicFontMask);
331
332 // SimpleFontData::platformDestroy() takes care of not deleting the cach ed font data twice.
333 return fontCache()->fontDataFromFontPlatformData(&scaledFontData);
334 }
335 END_BLOCK_OBJC_EXCEPTIONS;
336
337 return 0;
338 }
339
340 bool SimpleFontData::containsCharacters(const UChar* characters, int length) con st
341 {
342 NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<uni char*>(characters) length:length freeWhenDone:NO];
343 NSCharacterSet *set = [[m_platformData.font() coveredCharacterSet] invertedS et];
344 bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotF ound;
345 [string release];
346 return result;
347 }
348
349 void SimpleFontData::determinePitch()
350 {
351 NSFont* f = m_platformData.font();
352 // Special case Osaka-Mono.
353 // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixe d pitch.
354 // Note that the AppKit does not report Osaka-Mono as fixed pitch.
355
356 // Special case MS-PGothic.
357 // According to <rdar://problem/4032938>, we should not treat MS-PGothic as fixed pitch.
358 // Note that AppKit does report MS-PGothic as fixed pitch.
359
360 // Special case MonotypeCorsiva
361 // According to <rdar://problem/5454704>, we should not treat MonotypeCorsiv a as fixed pitch.
362 // Note that AppKit does report MonotypeCorsiva as fixed pitch.
363
364 NSString *name = [f fontName];
365 m_treatAsFixedPitch = ([f isFixedPitch] || [f _isFakeFixedPitch] ||
366 [name caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame) &&
367 [name caseInsensitiveCompare:@"MS-PGothic"] != NSOrderedSame &&
368 [name caseInsensitiveCompare:@"MonotypeCorsiva"] != NSOrderedSame;
369 }
370
371 FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const
372 {
373 FloatRect boundingBox;
374 boundingBox = CTFontGetBoundingRectsForGlyphs(m_platformData.ctFont(), platf ormData().orientation() == Vertical ? kCTFontVerticalOrientation : kCTFontHorizo ntalOrientation, &glyph, 0, 1);
375 boundingBox.setY(-boundingBox.maxY());
376 if (m_syntheticBoldOffset)
377 boundingBox.setWidth(boundingBox.width() + m_syntheticBoldOffset);
378
379 return boundingBox;
380 }
381
382 float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
383 {
384 CGSize advance = CGSizeZero;
385 if (platformData().orientation() == Horizontal || m_isBrokenIdeographFallbac k) {
386 NSFont *font = platformData().font();
387 if (font && platformData().isColorBitmapFont())
388 advance = NSSizeToCGSize([font advancementForGlyph:glyph]);
389 else {
390 float pointSize = platformData().m_size;
391 CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSiz e);
392 if (!CGFontGetGlyphAdvancesForStyle(platformData().cgFont(), &m, cgF ontRenderingModeForNSFont(font), &glyph, 1, &advance)) {
393 WTF_LOG_ERROR("Unable to cache glyph widths for %@ %f", [font di splayName], pointSize);
394 advance.width = 0;
395 }
396 }
397 } else
398 CTFontGetAdvancesForGlyphs(m_platformData.ctFont(), kCTFontVerticalOrien tation, &glyph, &advance, 1);
399
400 return advance.width + m_syntheticBoldOffset;
401 }
402
403 struct ProviderInfo {
404 const UChar* characters;
405 size_t length;
406 CFDictionaryRef attributes;
407 };
408
409 static const UniChar* provideStringAndAttributes(CFIndex stringIndex, CFIndex* c ount, CFDictionaryRef* attributes, void* context)
410 {
411 ProviderInfo* info = static_cast<struct ProviderInfo*>(context);
412 if (stringIndex < 0 || static_cast<size_t>(stringIndex) >= info->length)
413 return 0;
414
415 *count = info->length - stringIndex;
416 *attributes = info->attributes;
417 return info->characters + stringIndex;
418 }
419
420 bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters , size_t length) const
421 {
422 ASSERT(isMainThread());
423
424 if (!m_combiningCharacterSequenceSupport)
425 m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool> );
426
427 WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequen ceSupport->add(String(characters, length), false);
428 if (!addResult.isNewEntry)
429 return addResult.iterator->value;
430
431 RetainPtr<CGFontRef> cgFont(AdoptCF, CTFontCopyGraphicsFont(platformData().c tFont(), 0));
432
433 ProviderInfo info = { characters, length, getCFStringAttributes(0, platformD ata().orientation()) };
434 RetainPtr<CTLineRef> line(AdoptCF, CTLineCreateWithUniCharProvider(&provideS tringAndAttributes, 0, &info));
435
436 CFArrayRef runArray = CTLineGetGlyphRuns(line.get());
437 CFIndex runCount = CFArrayGetCount(runArray);
438
439 for (CFIndex r = 0; r < runCount; r++) {
440 CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r));
441 ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID());
442 CFDictionaryRef runAttributes = CTRunGetAttributes(ctRun);
443 CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttri butes, kCTFontAttributeName));
444 RetainPtr<CGFontRef> runCGFont(AdoptCF, CTFontCopyGraphicsFont(runFont, 0));
445 if (!CFEqual(runCGFont.get(), cgFont.get()))
446 return false;
447 }
448
449 addResult.iterator->value = true;
450 return true;
451 }
452
453 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698