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 <pal/spi/cocoa/CoreTextSPI.h>
35#endif
36#include "CharacterProperties.h"
37#include "FontCache.h"
38#include "FontCascade.h"
39#include "OpenTypeMathData.h"
40#include <wtf/MathExtras.h>
41#include <wtf/NeverDestroyed.h>
42#include <wtf/text/AtomicStringHash.h>
43
44#if ENABLE(OPENTYPE_VERTICAL)
45#include "OpenTypeVerticalData.h"
46#endif
47
48#if USE(DIRECT2D)
49#include <dwrite.h>
50#endif
51
52namespace WebCore {
53
54unsigned GlyphPage::s_count = 0;
55
56const float smallCapsFontSizeMultiplier = 0.7f;
57const float emphasisMarkFontSizeMultiplier = 0.5f;
58
59Font::Font(const FontPlatformData& platformData, Origin origin, Interstitial interstitial, Visibility visibility, OrientationFallback orientationFallback)
60 : m_platformData(platformData)
61 , m_origin(origin)
62 , m_visibility(visibility)
63 , m_treatAsFixedPitch(false)
64 , m_isInterstitial(interstitial == Interstitial::Yes)
65 , m_isTextOrientationFallback(orientationFallback == OrientationFallback::Yes)
66 , m_isBrokenIdeographFallback(false)
67 , m_hasVerticalGlyphs(false)
68 , m_isUsedInSystemFallbackCache(false)
69#if PLATFORM(IOS_FAMILY)
70 , m_shouldNotBeUsedForArabic(false)
71#endif
72{
73 platformInit();
74 platformGlyphInit();
75 platformCharWidthInit();
76#if ENABLE(OPENTYPE_VERTICAL)
77 if (platformData.orientation() == FontOrientation::Vertical && orientationFallback == OrientationFallback::No) {
78 m_verticalData = FontCache::singleton().verticalData(platformData);
79 m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics();
80 }
81#endif
82}
83
84// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font.
85void Font::initCharWidths()
86{
87 auto* glyphPageZero = glyphPage(GlyphPage::pageNumberForCodePoint('0'));
88
89 // Treat the width of a '0' as the avgCharWidth.
90 if (m_avgCharWidth <= 0.f && glyphPageZero) {
91 Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph;
92 if (digitZeroGlyph)
93 m_avgCharWidth = widthForGlyph(digitZeroGlyph);
94 }
95
96 // If we can't retrieve the width of a '0', fall back to the x height.
97 if (m_avgCharWidth <= 0.f)
98 m_avgCharWidth = m_fontMetrics.xHeight();
99
100 if (m_maxCharWidth <= 0.f)
101 m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent());
102}
103
104void Font::platformGlyphInit()
105{
106#if USE(FREETYPE)
107 auto* glyphPageZeroWidthSpace = glyphPage(GlyphPage::pageNumberForCodePoint(zeroWidthSpace));
108 UChar32 zeroWidthSpaceCharacter = zeroWidthSpace;
109#else
110 // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0,
111 // are mapped to the ZERO WIDTH SPACE glyph for non FreeType based ports.
112 auto* glyphPageZeroWidthSpace = glyphPage(0);
113 UChar32 zeroWidthSpaceCharacter = 0;
114#endif
115 auto* glyphPageCharacterZero = glyphPage(GlyphPage::pageNumberForCodePoint('0'));
116 auto* glyphPageSpace = glyphPage(GlyphPage::pageNumberForCodePoint(space));
117
118 if (glyphPageZeroWidthSpace)
119 m_zeroWidthSpaceGlyph = glyphPageZeroWidthSpace->glyphDataForCharacter(zeroWidthSpaceCharacter).glyph;
120
121 // Nasty hack to determine if we should round or ceil space widths.
122 // If the font is monospace or fake monospace we ceil to ensure that
123 // every character and the space are the same width. Otherwise we round.
124 if (glyphPageSpace)
125 m_spaceGlyph = glyphPageSpace->glyphDataForCharacter(space).glyph;
126 float width = widthForGlyph(m_spaceGlyph);
127 m_spaceWidth = width;
128 if (glyphPageCharacterZero)
129 m_zeroGlyph = glyphPageCharacterZero->glyphDataForCharacter('0').glyph;
130 m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph));
131 determinePitch();
132 m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width);
133
134 // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE.
135 // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph.
136 // See <http://bugs.webkit.org/show_bug.cgi?id=13178> and Font::isZeroWidthSpaceGlyph()
137 if (m_zeroWidthSpaceGlyph == m_spaceGlyph)
138 m_zeroWidthSpaceGlyph = 0;
139}
140
141Font::~Font()
142{
143 removeFromSystemFallbackCache();
144}
145
146static bool fillGlyphPage(GlyphPage& pageToFill, UChar* buffer, unsigned bufferLength, const Font& font)
147{
148 bool hasGlyphs = pageToFill.fill(buffer, bufferLength);
149#if ENABLE(OPENTYPE_VERTICAL)
150 if (hasGlyphs && font.verticalData())
151 font.verticalData()->substituteWithVerticalGlyphs(&font, &pageToFill);
152#else
153 UNUSED_PARAM(font);
154#endif
155 return hasGlyphs;
156}
157
158static Optional<size_t> codePointSupportIndex(UChar32 codePoint)
159{
160 // FIXME: Consider reordering these so the most common ones are at the front.
161 // Doing this could cause the BitVector to fit inside inline storage and therefore
162 // be both a performance and a memory progression.
163 if (codePoint < 0x20)
164 return codePoint;
165 if (codePoint >= 0x7F && codePoint < 0xA0)
166 return codePoint - 0x7F + 0x20;
167 Optional<size_t> result;
168 switch (codePoint) {
169 case softHyphen:
170 result = 0x41;
171 break;
172 case newlineCharacter:
173 result = 0x42;
174 break;
175 case tabCharacter:
176 result = 0x43;
177 break;
178 case noBreakSpace:
179 result = 0x44;
180 break;
181 case narrowNoBreakSpace:
182 result = 0x45;
183 break;
184 case leftToRightMark:
185 result = 0x46;
186 break;
187 case rightToLeftMark:
188 result = 0x47;
189 break;
190 case leftToRightEmbed:
191 result = 0x48;
192 break;
193 case rightToLeftEmbed:
194 result = 0x49;
195 break;
196 case leftToRightOverride:
197 result = 0x4A;
198 break;
199 case rightToLeftOverride:
200 result = 0x4B;
201 break;
202 case leftToRightIsolate:
203 result = 0x4C;
204 break;
205 case rightToLeftIsolate:
206 result = 0x4D;
207 break;
208 case zeroWidthNonJoiner:
209 result = 0x4E;
210 break;
211 case zeroWidthJoiner:
212 result = 0x4F;
213 break;
214 case popDirectionalFormatting:
215 result = 0x50;
216 break;
217 case popDirectionalIsolate:
218 result = 0x51;
219 break;
220 case firstStrongIsolate:
221 result = 0x52;
222 break;
223 case objectReplacementCharacter:
224 result = 0x53;
225 break;
226 case zeroWidthNoBreakSpace:
227 result = 0x54;
228 break;
229 default:
230 result = WTF::nullopt;
231 }
232
233#ifndef NDEBUG
234 UChar32 codePointOrder[] = {
235 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
236 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
237 0x7F,
238 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
239 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
240 softHyphen,
241 newlineCharacter,
242 tabCharacter,
243 noBreakSpace,
244 narrowNoBreakSpace,
245 leftToRightMark,
246 rightToLeftMark,
247 leftToRightEmbed,
248 rightToLeftEmbed,
249 leftToRightOverride,
250 rightToLeftOverride,
251 leftToRightIsolate,
252 rightToLeftIsolate,
253 zeroWidthNonJoiner,
254 zeroWidthJoiner,
255 popDirectionalFormatting,
256 popDirectionalIsolate,
257 firstStrongIsolate,
258 objectReplacementCharacter,
259 zeroWidthNoBreakSpace
260 };
261 bool found = false;
262 for (size_t i = 0; i < WTF_ARRAY_LENGTH(codePointOrder); ++i) {
263 if (codePointOrder[i] == codePoint) {
264 ASSERT(i == result);
265 found = true;
266 }
267 }
268 ASSERT(found == static_cast<bool>(result));
269#endif
270 return result;
271}
272
273#if !USE(FREETYPE)
274static void overrideControlCharacters(Vector<UChar>& buffer, unsigned start, unsigned end)
275{
276 auto overwriteCodePoints = [&](unsigned minimum, unsigned maximum, UChar newCodePoint) {
277 unsigned begin = std::max(start, minimum);
278 unsigned complete = std::min(end, maximum);
279 for (unsigned i = begin; i < complete; ++i) {
280 ASSERT(codePointSupportIndex(i));
281 buffer[i - start] = newCodePoint;
282 }
283 };
284
285 auto overwriteCodePoint = [&](UChar codePoint, UChar newCodePoint) {
286 ASSERT(codePointSupportIndex(codePoint));
287 if (codePoint >= start && codePoint < end)
288 buffer[codePoint - start] = newCodePoint;
289 };
290
291 // Code points 0x0 - 0x20 and 0x7F - 0xA0 are control character and shouldn't render. Map them to ZERO WIDTH SPACE.
292 overwriteCodePoints(0x0, 0x20, zeroWidthSpace);
293 overwriteCodePoints(0x7F, 0xA0, zeroWidthSpace);
294 overwriteCodePoint(softHyphen, zeroWidthSpace);
295 overwriteCodePoint('\n', space);
296 overwriteCodePoint('\t', space);
297 overwriteCodePoint(noBreakSpace, space);
298 overwriteCodePoint(narrowNoBreakSpace, zeroWidthSpace);
299 overwriteCodePoint(leftToRightMark, zeroWidthSpace);
300 overwriteCodePoint(rightToLeftMark, zeroWidthSpace);
301 overwriteCodePoint(leftToRightEmbed, zeroWidthSpace);
302 overwriteCodePoint(rightToLeftEmbed, zeroWidthSpace);
303 overwriteCodePoint(leftToRightOverride, zeroWidthSpace);
304 overwriteCodePoint(rightToLeftOverride, zeroWidthSpace);
305 overwriteCodePoint(leftToRightIsolate, zeroWidthSpace);
306 overwriteCodePoint(rightToLeftIsolate, zeroWidthSpace);
307 overwriteCodePoint(zeroWidthNonJoiner, zeroWidthSpace);
308 overwriteCodePoint(zeroWidthJoiner, zeroWidthSpace);
309 overwriteCodePoint(popDirectionalFormatting, zeroWidthSpace);
310 overwriteCodePoint(popDirectionalIsolate, zeroWidthSpace);
311 overwriteCodePoint(firstStrongIsolate, zeroWidthSpace);
312 overwriteCodePoint(objectReplacementCharacter, zeroWidthSpace);
313 overwriteCodePoint(zeroWidthNoBreakSpace, zeroWidthSpace);
314}
315#endif
316
317static RefPtr<GlyphPage> createAndFillGlyphPage(unsigned pageNumber, const Font& font)
318{
319#if PLATFORM(IOS_FAMILY)
320 // FIXME: Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. See <rdar://problem/9823975>.
321 // Once we have the fix for <rdar://problem/9823975> then remove this code together with Font::shouldNotBeUsedForArabic()
322 // in <rdar://problem/12096835>.
323 if (GlyphPage::pageNumberIsUsedForArabic(pageNumber) && font.shouldNotBeUsedForArabic())
324 return nullptr;
325#endif
326
327 unsigned glyphPageSize = GlyphPage::sizeForPageNumber(pageNumber);
328
329 unsigned start = GlyphPage::startingCodePointInPageNumber(pageNumber);
330 Vector<UChar> buffer(glyphPageSize * 2 + 2);
331 unsigned bufferLength;
332 // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for.
333 if (U_IS_BMP(start)) {
334 bufferLength = glyphPageSize;
335 for (unsigned i = 0; i < bufferLength; i++)
336 buffer[i] = start + i;
337
338#if !USE(FREETYPE)
339 overrideControlCharacters(buffer, start, start + glyphPageSize);
340#endif
341 } else {
342 bufferLength = glyphPageSize * 2;
343 for (unsigned i = 0; i < glyphPageSize; i++) {
344 int c = i + start;
345 buffer[i * 2] = U16_LEAD(c);
346 buffer[i * 2 + 1] = U16_TRAIL(c);
347 }
348 }
349
350 // Now that we have a buffer full of characters, we want to get back an array
351 // of glyph indices. This part involves calling into the platform-specific
352 // routine of our glyph map for actually filling in the page with the glyphs.
353 // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data
354 // for only 128 out of 256 characters.
355 Ref<GlyphPage> glyphPage = GlyphPage::create(font);
356
357 bool haveGlyphs = fillGlyphPage(glyphPage, buffer.data(), bufferLength, font);
358 if (!haveGlyphs)
359 return nullptr;
360
361 return glyphPage;
362}
363
364const GlyphPage* Font::glyphPage(unsigned pageNumber) const
365{
366 if (!pageNumber) {
367 if (!m_glyphPageZero)
368 m_glyphPageZero = createAndFillGlyphPage(0, *this);
369 return m_glyphPageZero.get();
370 }
371 auto addResult = m_glyphPages.add(pageNumber, nullptr);
372 if (addResult.isNewEntry)
373 addResult.iterator->value = createAndFillGlyphPage(pageNumber, *this);
374
375 return addResult.iterator->value.get();
376}
377
378Glyph Font::glyphForCharacter(UChar32 character) const
379{
380 auto* page = glyphPage(GlyphPage::pageNumberForCodePoint(character));
381 if (!page)
382 return 0;
383 return page->glyphForCharacter(character);
384}
385
386GlyphData Font::glyphDataForCharacter(UChar32 character) const
387{
388 auto* page = glyphPage(GlyphPage::pageNumberForCodePoint(character));
389 if (!page)
390 return GlyphData();
391 return page->glyphDataForCharacter(character);
392}
393
394auto Font::ensureDerivedFontData() const -> DerivedFonts&
395{
396 if (!m_derivedFontData)
397 m_derivedFontData = std::make_unique<DerivedFonts>();
398 return *m_derivedFontData;
399}
400
401const Font& Font::verticalRightOrientationFont() const
402{
403 DerivedFonts& derivedFontData = ensureDerivedFontData();
404 if (!derivedFontData.verticalRightOrientationFont) {
405 auto verticalRightPlatformData = FontPlatformData::cloneWithOrientation(m_platformData, FontOrientation::Horizontal);
406 derivedFontData.verticalRightOrientationFont = create(verticalRightPlatformData, origin(), Interstitial::No, Visibility::Visible, OrientationFallback::Yes);
407 }
408 ASSERT(derivedFontData.verticalRightOrientationFont != this);
409 return *derivedFontData.verticalRightOrientationFont;
410}
411
412const Font& Font::uprightOrientationFont() const
413{
414 DerivedFonts& derivedFontData = ensureDerivedFontData();
415 if (!derivedFontData.uprightOrientationFont)
416 derivedFontData.uprightOrientationFont = create(m_platformData, origin(), Interstitial::No, Visibility::Visible, OrientationFallback::Yes);
417 ASSERT(derivedFontData.uprightOrientationFont != this);
418 return *derivedFontData.uprightOrientationFont;
419}
420
421const Font& Font::invisibleFont() const
422{
423 DerivedFonts& derivedFontData = ensureDerivedFontData();
424 if (!derivedFontData.invisibleFont)
425 derivedFontData.invisibleFont = create(m_platformData, origin(), Interstitial::Yes, Visibility::Invisible);
426 ASSERT(derivedFontData.invisibleFont != this);
427 return *derivedFontData.invisibleFont;
428}
429
430const Font* Font::smallCapsFont(const FontDescription& fontDescription) const
431{
432 DerivedFonts& derivedFontData = ensureDerivedFontData();
433 if (!derivedFontData.smallCapsFont)
434 derivedFontData.smallCapsFont = createScaledFont(fontDescription, smallCapsFontSizeMultiplier);
435 ASSERT(derivedFontData.smallCapsFont != this);
436 return derivedFontData.smallCapsFont.get();
437}
438
439const Font& Font::noSynthesizableFeaturesFont() const
440{
441#if PLATFORM(COCOA)
442 DerivedFonts& derivedFontData = ensureDerivedFontData();
443 if (!derivedFontData.noSynthesizableFeaturesFont)
444 derivedFontData.noSynthesizableFeaturesFont = createFontWithoutSynthesizableFeatures();
445 ASSERT(derivedFontData.noSynthesizableFeaturesFont != this);
446 return *derivedFontData.noSynthesizableFeaturesFont;
447#else
448 return *this;
449#endif
450}
451
452const Font* Font::emphasisMarkFont(const FontDescription& fontDescription) const
453{
454 DerivedFonts& derivedFontData = ensureDerivedFontData();
455 if (!derivedFontData.emphasisMarkFont)
456 derivedFontData.emphasisMarkFont = createScaledFont(fontDescription, emphasisMarkFontSizeMultiplier);
457 ASSERT(derivedFontData.emphasisMarkFont != this);
458 return derivedFontData.emphasisMarkFont.get();
459}
460
461const Font& Font::brokenIdeographFont() const
462{
463 DerivedFonts& derivedFontData = ensureDerivedFontData();
464 if (!derivedFontData.brokenIdeographFont) {
465 derivedFontData.brokenIdeographFont = create(m_platformData, origin(), Interstitial::No);
466 derivedFontData.brokenIdeographFont->m_isBrokenIdeographFallback = true;
467 }
468 ASSERT(derivedFontData.brokenIdeographFont != this);
469 return *derivedFontData.brokenIdeographFont;
470}
471
472#if !LOG_DISABLED
473String Font::description() const
474{
475 if (origin() == Origin::Remote)
476 return "[custom font]";
477
478 return platformData().description();
479}
480#endif
481
482const OpenTypeMathData* Font::mathData() const
483{
484 if (isInterstitial())
485 return nullptr;
486 if (!m_mathData) {
487 m_mathData = OpenTypeMathData::create(m_platformData);
488 if (!m_mathData->hasMathData())
489 m_mathData = nullptr;
490 }
491 return m_mathData.get();
492}
493
494RefPtr<Font> Font::createScaledFont(const FontDescription& fontDescription, float scaleFactor) const
495{
496 return platformCreateScaledFont(fontDescription, scaleFactor);
497}
498
499bool Font::applyTransforms(GlyphBufferGlyph* glyphs, GlyphBufferAdvance* advances, size_t glyphCount, bool enableKerning, bool requiresShaping) const
500{
501#if PLATFORM(COCOA)
502 CTFontTransformOptions options = (enableKerning ? kCTFontTransformApplyPositioning : 0) | (requiresShaping ? kCTFontTransformApplyShaping : 0);
503 return CTFontTransformGlyphs(m_platformData.ctFont(), glyphs, reinterpret_cast<CGSize*>(advances), glyphCount, options);
504#else
505 UNUSED_PARAM(glyphs);
506 UNUSED_PARAM(advances);
507 UNUSED_PARAM(glyphCount);
508 UNUSED_PARAM(enableKerning);
509 UNUSED_PARAM(requiresShaping);
510 return false;
511#endif
512}
513
514class CharacterFallbackMapKey {
515public:
516 CharacterFallbackMapKey()
517 {
518 }
519
520 CharacterFallbackMapKey(const AtomicString& locale, UChar32 character, IsForPlatformFont isForPlatformFont)
521 : locale(locale)
522 , character(character)
523 , isForPlatformFont(isForPlatformFont == IsForPlatformFont::Yes)
524 {
525 }
526
527 CharacterFallbackMapKey(WTF::HashTableDeletedValueType)
528 : character(-1)
529 {
530 }
531
532 bool isHashTableDeletedValue() const { return character == -1; }
533
534 bool operator==(const CharacterFallbackMapKey& other) const
535 {
536 return locale == other.locale && character == other.character && isForPlatformFont == other.isForPlatformFont;
537 }
538
539 static const bool emptyValueIsZero = true;
540
541private:
542 friend struct CharacterFallbackMapKeyHash;
543
544 AtomicString locale;
545 UChar32 character { 0 };
546 bool isForPlatformFont { false };
547};
548
549struct CharacterFallbackMapKeyHash {
550 static unsigned hash(const CharacterFallbackMapKey& key)
551 {
552 IntegerHasher hasher;
553 hasher.add(key.character);
554 hasher.add(key.isForPlatformFont);
555 hasher.add(key.locale.existingHash());
556 return hasher.hash();
557 }
558
559 static bool equal(const CharacterFallbackMapKey& a, const CharacterFallbackMapKey& b)
560 {
561 return a == b;
562 }
563
564 static const bool safeToCompareToEmptyOrDeleted = true;
565};
566
567// Fonts are not ref'd to avoid cycles.
568// FIXME: Shouldn't these be WeakPtrs?
569typedef HashMap<CharacterFallbackMapKey, Font*, CharacterFallbackMapKeyHash, WTF::SimpleClassHashTraits<CharacterFallbackMapKey>> CharacterFallbackMap;
570typedef HashMap<const Font*, CharacterFallbackMap> SystemFallbackCache;
571
572static SystemFallbackCache& systemFallbackCache()
573{
574 static NeverDestroyed<SystemFallbackCache> map;
575 return map.get();
576}
577
578RefPtr<Font> Font::systemFallbackFontForCharacter(UChar32 character, const FontDescription& description, IsForPlatformFont isForPlatformFont) const
579{
580 auto fontAddResult = systemFallbackCache().add(this, CharacterFallbackMap());
581
582 if (!character) {
583 UChar codeUnit = 0;
584 return FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, FontCache::PreferColoredFont::No, &codeUnit, 1);
585 }
586
587 auto key = CharacterFallbackMapKey(description.locale(), character, isForPlatformFont);
588 auto characterAddResult = fontAddResult.iterator->value.add(WTFMove(key), nullptr);
589
590 Font*& fallbackFont = characterAddResult.iterator->value;
591
592 if (!fallbackFont) {
593 UChar codeUnits[2];
594 unsigned codeUnitsLength;
595 if (U_IS_BMP(character)) {
596 codeUnits[0] = FontCascade::normalizeSpaces(character);
597 codeUnitsLength = 1;
598 } else {
599 codeUnits[0] = U16_LEAD(character);
600 codeUnits[1] = U16_TRAIL(character);
601 codeUnitsLength = 2;
602 }
603
604 fallbackFont = FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, FontCache::PreferColoredFont::No, codeUnits, codeUnitsLength).get();
605 if (fallbackFont)
606 fallbackFont->m_isUsedInSystemFallbackCache = true;
607 }
608
609 return fallbackFont;
610}
611
612void Font::removeFromSystemFallbackCache()
613{
614 systemFallbackCache().remove(this);
615
616 if (!m_isUsedInSystemFallbackCache)
617 return;
618
619 for (auto& characterMap : systemFallbackCache().values()) {
620 Vector<CharacterFallbackMapKey, 512> toRemove;
621 for (auto& entry : characterMap) {
622 if (entry.value == this)
623 toRemove.append(entry.key);
624 }
625 for (auto& key : toRemove)
626 characterMap.remove(key);
627 }
628}
629
630#if !PLATFORM(COCOA) && !USE(FREETYPE)
631bool Font::variantCapsSupportsCharacterForSynthesis(FontVariantCaps fontVariantCaps, UChar32) const
632{
633 switch (fontVariantCaps) {
634 case FontVariantCaps::Small:
635 case FontVariantCaps::Petite:
636 case FontVariantCaps::AllSmall:
637 case FontVariantCaps::AllPetite:
638 return false;
639 default:
640 // Synthesis only supports the variant-caps values listed above.
641 return true;
642 }
643}
644
645bool Font::platformSupportsCodePoint(UChar32 character, Optional<UChar32> variation) const
646{
647 return variation ? false : glyphForCharacter(character);
648}
649#endif
650
651bool Font::supportsCodePoint(UChar32 character) const
652{
653 // This is very similar to static_cast<bool>(glyphForCharacter(character))
654 // except that glyphForCharacter() maps certain code points to ZWS (because they
655 // shouldn't be visible). This function doesn't do that mapping, and instead is
656 // as honest as possible about what code points the font supports. This is so
657 // that we can accurately determine which characters are supported by this font
658 // so we know which boundaries to break strings when we send them to the complex
659 // text codepath. The complex text codepath is totally separate from this ZWS
660 // replacement logic (because CoreText handles those characters instead of WebKit).
661 if (auto index = codePointSupportIndex(character)) {
662 m_codePointSupport.ensureSize(2 * (*index + 1));
663 bool hasBeenSet = m_codePointSupport.quickSet(2 * *index);
664 if (!hasBeenSet && platformSupportsCodePoint(character))
665 m_codePointSupport.quickSet(2 * *index + 1);
666 return m_codePointSupport.quickGet(2 * *index + 1);
667 }
668 return glyphForCharacter(character);
669}
670
671bool Font::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
672{
673 ASSERT(isMainThread());
674
675 auto codePoints = StringView(characters, length).codePoints();
676 auto it = codePoints.begin();
677 auto end = codePoints.end();
678 while (it != end) {
679 auto codePoint = *it;
680 ++it;
681
682 if (it != end && isVariationSelector(*it)) {
683 if (!platformSupportsCodePoint(codePoint, *it)) {
684 // Try the characters individually.
685 if (!supportsCodePoint(codePoint) || !supportsCodePoint(*it))
686 return false;
687 }
688 ++it;
689 continue;
690 }
691
692 if (!supportsCodePoint(codePoint))
693 return false;
694 }
695 return true;
696}
697
698// Don't store the result of this! The hash map is free to rehash at any point, leaving this reference dangling.
699const Path& Font::pathForGlyph(Glyph glyph) const
700{
701 if (const auto& path = m_glyphPathMap.existingMetricsForGlyph(glyph))
702 return *path;
703 auto path = platformPathForGlyph(glyph);
704 m_glyphPathMap.setMetricsForGlyph(glyph, path);
705 return *m_glyphPathMap.existingMetricsForGlyph(glyph);
706}
707
708} // namespace WebCore
709