[Cocoa] System fonts do not get correct tracking
[WebKit-https.git] / Source / WebCore / platform / graphics / Font.cpp
1 /*
2  * Copyright (C) 2005, 2008, 2010, 2015 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  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "Font.h"
32
33 #if PLATFORM(COCOA)
34 #include "CoreTextSPI.h"
35 #endif
36 #include "FontCache.h"
37 #include "FontCascade.h"
38 #include "OpenTypeMathData.h"
39 #include <wtf/MathExtras.h>
40 #include <wtf/NeverDestroyed.h>
41
42 #if ENABLE(OPENTYPE_VERTICAL)
43 #include "OpenTypeVerticalData.h"
44 #endif
45
46 namespace WebCore {
47
48 unsigned GlyphPage::s_count = 0;
49
50 const float smallCapsFontSizeMultiplier = 0.7f;
51 const float emphasisMarkFontSizeMultiplier = 0.5f;
52
53 Font::Font(const FontPlatformData& platformData, std::unique_ptr<SVGData>&& svgData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback)
54     : m_maxCharWidth(-1)
55     , m_avgCharWidth(-1)
56     , m_platformData(platformData)
57     , m_svgData(WTF::move(svgData))
58     , m_mathData(nullptr)
59     , m_treatAsFixedPitch(false)
60     , m_isCustomFont(isCustomFont)
61     , m_isLoading(isLoading)
62     , m_isTextOrientationFallback(isTextOrientationFallback)
63     , m_isBrokenIdeographFallback(false)
64     , m_hasVerticalGlyphs(false)
65     , m_isUsedInSystemFallbackCache(false)
66 #if PLATFORM(COCOA) || PLATFORM(WIN)
67     , m_isSystemFont(false)
68 #endif
69 #if PLATFORM(IOS)
70     , m_shouldNotBeUsedForArabic(false)
71 #endif
72 {
73 }
74
75 Font::Font(const FontPlatformData& platformData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback)
76     : Font(platformData, std::unique_ptr<SVGData>(), isCustomFont, isLoading, isTextOrientationFallback)
77 {
78     platformInit();
79     platformGlyphInit();
80     platformCharWidthInit();
81 #if ENABLE(OPENTYPE_VERTICAL)
82     if (platformData.orientation() == Vertical && !isTextOrientationFallback) {
83         m_verticalData = platformData.verticalData();
84         m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics();
85     }
86 #endif
87 }
88
89 Font::Font(std::unique_ptr<SVGData> svgData, float fontSize, bool syntheticBold, bool syntheticItalic)
90     : Font(FontPlatformData(fontSize, syntheticBold, syntheticItalic), WTF::move(svgData), true, false, false)
91 {
92     m_svgData->initializeFont(this, fontSize);
93 }
94
95 // Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font.
96 void Font::initCharWidths()
97 {
98     auto* glyphPageZero = glyphPage(0);
99
100     // Treat the width of a '0' as the avgCharWidth.
101     if (m_avgCharWidth <= 0.f && glyphPageZero) {
102         static const UChar32 digitZeroChar = '0';
103         Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter(digitZeroChar).glyph;
104         if (digitZeroGlyph)
105             m_avgCharWidth = widthForGlyph(digitZeroGlyph);
106     }
107
108     // If we can't retrieve the width of a '0', fall back to the x height.
109     if (m_avgCharWidth <= 0.f)
110         m_avgCharWidth = m_fontMetrics.xHeight();
111
112     if (m_maxCharWidth <= 0.f)
113         m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent());
114 }
115
116 void Font::platformGlyphInit()
117 {
118     auto* glyphPageZero = glyphPage(0);
119     if (!glyphPageZero) {
120         m_spaceGlyph = 0;
121         m_spaceWidth = 0;
122         m_zeroGlyph = 0;
123         m_adjustedSpaceWidth = 0;
124         determinePitch();
125         m_zeroWidthSpaceGlyph = 0;
126         return;
127     }
128
129     // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0,
130     // are mapped to the ZERO WIDTH SPACE glyph.
131     m_zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph;
132
133     // Nasty hack to determine if we should round or ceil space widths.
134     // If the font is monospace or fake monospace we ceil to ensure that 
135     // every character and the space are the same width. Otherwise we round.
136     m_spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph;
137     float width = widthForGlyph(m_spaceGlyph);
138     m_spaceWidth = width;
139     m_zeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph;
140     m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph));
141     determinePitch();
142     m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width);
143
144     // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE.
145     // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph.
146     // See <http://bugs.webkit.org/show_bug.cgi?id=13178> and Font::isZeroWidthSpaceGlyph()
147     if (m_zeroWidthSpaceGlyph == m_spaceGlyph)
148         m_zeroWidthSpaceGlyph = 0;
149 }
150
151 Font::~Font()
152 {
153     removeFromSystemFallbackCache();
154 }
155
156 static bool fillGlyphPage(GlyphPage& pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const Font* font)
157 {
158 #if ENABLE(SVG_FONTS)
159     if (auto* svgData = font->svgData())
160         return svgData->fillSVGGlyphPage(&pageToFill, offset, length, buffer, bufferLength, font);
161 #endif
162     bool hasGlyphs = pageToFill.fill(offset, length, buffer, bufferLength, font);
163 #if ENABLE(OPENTYPE_VERTICAL)
164     if (hasGlyphs && font->verticalData())
165         font->verticalData()->substituteWithVerticalGlyphs(font, &pageToFill, offset, length);
166 #endif
167     return hasGlyphs;
168 }
169
170 static RefPtr<GlyphPage> createAndFillGlyphPage(unsigned pageNumber, const Font* font)
171 {
172 #if PLATFORM(IOS)
173     // FIXME: Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. See <rdar://problem/9823975>.
174     // Once we have the fix for <rdar://problem/9823975> then remove this code together with Font::shouldNotBeUsedForArabic()
175     // in <rdar://problem/12096835>.
176     if (pageNumber == 6 && font->shouldNotBeUsedForArabic())
177         return nullptr;
178 #endif
179
180     unsigned start = pageNumber * GlyphPage::size;
181     UChar buffer[GlyphPage::size * 2 + 2];
182     unsigned bufferLength;
183     // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for.
184     if (U_IS_BMP(start)) {
185         bufferLength = GlyphPage::size;
186         for (unsigned i = 0; i < GlyphPage::size; i++)
187             buffer[i] = start + i;
188
189         if (!start) {
190             // Control characters must not render at all.
191             for (unsigned i = 0; i < 0x20; ++i)
192                 buffer[i] = zeroWidthSpace;
193             for (unsigned i = 0x7F; i < 0xA0; i++)
194                 buffer[i] = zeroWidthSpace;
195             buffer[softHyphen] = zeroWidthSpace;
196
197             // \n, \t, and nonbreaking space must render as a space.
198             buffer[(int)'\n'] = ' ';
199             buffer[(int)'\t'] = ' ';
200             buffer[noBreakSpace] = ' ';
201         } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) {
202             // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all.
203             buffer[leftToRightMark - start] = zeroWidthSpace;
204             buffer[rightToLeftMark - start] = zeroWidthSpace;
205             buffer[leftToRightEmbed - start] = zeroWidthSpace;
206             buffer[rightToLeftEmbed - start] = zeroWidthSpace;
207             buffer[leftToRightOverride - start] = zeroWidthSpace;
208             buffer[rightToLeftOverride - start] = zeroWidthSpace;
209             buffer[zeroWidthNonJoiner - start] = zeroWidthSpace;
210             buffer[zeroWidthJoiner - start] = zeroWidthSpace;
211             buffer[popDirectionalFormatting - start] = zeroWidthSpace;
212         } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) {
213             // Object replacement character must not render at all.
214             buffer[objectReplacementCharacter - start] = zeroWidthSpace;
215         } else if (start == (zeroWidthNoBreakSpace & ~(GlyphPage::size - 1))) {
216             // ZWNBS/BOM must not render at all.
217             buffer[zeroWidthNoBreakSpace - start] = zeroWidthSpace;
218         }
219     } else {
220         bufferLength = GlyphPage::size * 2;
221         for (unsigned i = 0; i < GlyphPage::size; i++) {
222             int c = i + start;
223             buffer[i * 2] = U16_LEAD(c);
224             buffer[i * 2 + 1] = U16_TRAIL(c);
225         }
226     }
227
228     // Now that we have a buffer full of characters, we want to get back an array
229     // of glyph indices. This part involves calling into the platform-specific
230     // routine of our glyph map for actually filling in the page with the glyphs.
231     // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data
232     // for only 128 out of 256 characters.
233     RefPtr<GlyphPage> glyphPage;
234     if (GlyphPage::mayUseMixedFontsWhenFilling(buffer, bufferLength, font))
235         glyphPage = GlyphPage::createForMixedFonts();
236     else
237         glyphPage = GlyphPage::createForSingleFont(font);
238
239     bool haveGlyphs = fillGlyphPage(*glyphPage, 0, GlyphPage::size, buffer, bufferLength, font);
240     if (!haveGlyphs)
241         return nullptr;
242
243     glyphPage->setImmutable();
244     return glyphPage;
245 }
246
247 const GlyphPage* Font::glyphPage(unsigned pageNumber) const
248 {
249     if (!pageNumber) {
250         if (!m_glyphPageZero)
251             m_glyphPageZero = createAndFillGlyphPage(0, this);
252         return m_glyphPageZero.get();
253     }
254     auto addResult = m_glyphPages.add(pageNumber, nullptr);
255     if (addResult.isNewEntry)
256         addResult.iterator->value = createAndFillGlyphPage(pageNumber, this);
257
258     return addResult.iterator->value.get();
259 }
260
261 Glyph Font::glyphForCharacter(UChar32 character) const
262 {
263     auto* page = glyphPage(character / GlyphPage::size);
264     if (!page)
265         return 0;
266     return page->glyphAt(character % GlyphPage::size);
267 }
268
269 GlyphData Font::glyphDataForCharacter(UChar32 character) const
270 {
271     auto* page = glyphPage(character / GlyphPage::size);
272     if (!page)
273         return GlyphData();
274     return page->glyphDataForCharacter(character);
275 }
276
277 PassRefPtr<Font> Font::verticalRightOrientationFont() const
278 {
279     if (!m_derivedFontData)
280         m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
281     if (!m_derivedFontData->verticalRightOrientation) {
282         FontPlatformData verticalRightPlatformData(m_platformData);
283         verticalRightPlatformData.setOrientation(Horizontal);
284         m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, isCustomFont(), false, true);
285     }
286     ASSERT(m_derivedFontData->verticalRightOrientation != this);
287     return m_derivedFontData->verticalRightOrientation;
288 }
289
290 PassRefPtr<Font> Font::uprightOrientationFont() const
291 {
292     if (!m_derivedFontData)
293         m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
294     if (!m_derivedFontData->uprightOrientation)
295         m_derivedFontData->uprightOrientation = create(m_platformData, isCustomFont(), false, true);
296     ASSERT(m_derivedFontData->uprightOrientation != this);
297     return m_derivedFontData->uprightOrientation;
298 }
299
300 PassRefPtr<Font> Font::smallCapsFont(const FontDescription& fontDescription) const
301 {
302     if (!m_derivedFontData)
303         m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
304     if (!m_derivedFontData->smallCaps)
305         m_derivedFontData->smallCaps = createScaledFont(fontDescription, smallCapsFontSizeMultiplier);
306     ASSERT(m_derivedFontData->smallCaps != this);
307     return m_derivedFontData->smallCaps;
308 }
309
310 PassRefPtr<Font> Font::emphasisMarkFont(const FontDescription& fontDescription) const
311 {
312     if (!m_derivedFontData)
313         m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
314     if (!m_derivedFontData->emphasisMark)
315         m_derivedFontData->emphasisMark = createScaledFont(fontDescription, emphasisMarkFontSizeMultiplier);
316     ASSERT(m_derivedFontData->emphasisMark != this);
317     return m_derivedFontData->emphasisMark;
318 }
319
320 PassRefPtr<Font> Font::brokenIdeographFont() const
321 {
322     if (!m_derivedFontData)
323         m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
324     if (!m_derivedFontData->brokenIdeograph) {
325         m_derivedFontData->brokenIdeograph = create(m_platformData, isCustomFont(), false);
326         m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true;
327     }
328     ASSERT(m_derivedFontData->brokenIdeograph != this);
329     return m_derivedFontData->brokenIdeograph;
330 }
331
332 PassRefPtr<Font> Font::nonSyntheticItalicFont() const
333 {
334     if (!m_derivedFontData)
335         m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
336     if (!m_derivedFontData->nonSyntheticItalic) {
337         FontPlatformData nonSyntheticItalicFontPlatformData(m_platformData);
338 #if PLATFORM(COCOA)
339         nonSyntheticItalicFontPlatformData.m_syntheticOblique = false;
340 #endif
341         m_derivedFontData->nonSyntheticItalic = create(nonSyntheticItalicFontPlatformData, isCustomFont());
342     }
343     ASSERT(m_derivedFontData->nonSyntheticItalic != this);
344     return m_derivedFontData->nonSyntheticItalic;
345 }
346
347 #ifndef NDEBUG
348 String Font::description() const
349 {
350     if (isSVGFont())
351         return "[SVG font]";
352     if (isCustomFont())
353         return "[custom font]";
354
355     return platformData().description();
356 }
357 #endif
358
359 const OpenTypeMathData* Font::mathData() const
360 {
361     if (m_isLoading)
362         return nullptr;
363     if (!m_mathData) {
364         m_mathData = OpenTypeMathData::create(m_platformData);
365         if (!m_mathData->hasMathData())
366             m_mathData.clear();
367     }
368     return m_mathData.get();
369 }
370
371 Font::DerivedFontData::~DerivedFontData()
372 {
373 }
374
375 PassRefPtr<Font> Font::createScaledFont(const FontDescription& fontDescription, float scaleFactor) const
376 {
377     if (isSVGFont())
378         return nullptr;
379
380     return platformCreateScaledFont(fontDescription, scaleFactor);
381 }
382
383 bool Font::applyTransforms(GlyphBufferGlyph* glyphs, GlyphBufferAdvance* advances, size_t glyphCount, TypesettingFeatures typesettingFeatures) const
384 {
385     // We need to handle transforms on SVG fonts internally, since they are rendered internally.
386     ASSERT(!isSVGFont());
387 #if PLATFORM(COCOA)
388     CTFontTransformOptions options = (typesettingFeatures & Kerning ? kCTFontTransformApplyPositioning : 0) | (typesettingFeatures & Ligatures ? kCTFontTransformApplyShaping : 0);
389     return CTFontTransformGlyphs(m_platformData.ctFont(), glyphs, reinterpret_cast<CGSize*>(advances), glyphCount, options);
390 #else
391     UNUSED_PARAM(glyphs);
392     UNUSED_PARAM(advances);
393     UNUSED_PARAM(glyphCount);
394     UNUSED_PARAM(typesettingFeatures);
395     return false;
396 #endif
397 }
398
399 // Fonts are not ref'd to avoid cycles.
400 typedef std::pair<UChar32, bool /* isForPlatformFont */> CharacterFallbackMapKey;
401 typedef HashMap<CharacterFallbackMapKey, Font*> CharacterFallbackMap;
402 typedef HashMap<const Font*, CharacterFallbackMap> SystemFallbackCache;
403
404 static SystemFallbackCache& systemFallbackCache()
405 {
406     static NeverDestroyed<SystemFallbackCache> map;
407     return map.get();
408 }
409
410 RefPtr<Font> Font::systemFallbackFontForCharacter(UChar32 character, const FontDescription& description, bool isForPlatformFont) const
411 {
412     auto fontAddResult = systemFallbackCache().add(this, CharacterFallbackMap());
413
414     if (!character) {
415         UChar codeUnit = 0;
416         return FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, &codeUnit, 1);
417     }
418
419     auto key = std::make_pair(character, isForPlatformFont);
420     auto characterAddResult = fontAddResult.iterator->value.add(key, nullptr);
421
422     Font*& fallbackFont = characterAddResult.iterator->value;
423
424     if (!fallbackFont) {
425         UChar codeUnits[2];
426         unsigned codeUnitsLength;
427         if (U_IS_BMP(character)) {
428             codeUnits[0] = FontCascade::normalizeSpaces(character);
429             codeUnitsLength = 1;
430         } else {
431             codeUnits[0] = U16_LEAD(character);
432             codeUnits[1] = U16_TRAIL(character);
433             codeUnitsLength = 2;
434         }
435
436         fallbackFont = FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, codeUnits, codeUnitsLength).get();
437         if (fallbackFont)
438             fallbackFont->m_isUsedInSystemFallbackCache = true;
439     }
440
441     return fallbackFont;
442 }
443
444 void Font::removeFromSystemFallbackCache()
445 {
446     systemFallbackCache().remove(this);
447
448     if (!m_isUsedInSystemFallbackCache)
449         return;
450
451     for (auto& characterMap : systemFallbackCache().values()) {
452         Vector<CharacterFallbackMapKey, 512> toRemove;
453         for (auto& entry : characterMap) {
454             if (entry.value == this)
455                 toRemove.append(entry.key);
456         }
457         for (auto& key : toRemove)
458             characterMap.remove(key);
459     }
460 }
461
462 } // namespace WebCore