1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 /*  This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data.
30     It's needed because although win32 will happily load a TTF file from in-memory data, it won't
31     tell you the name of the damned font that it just loaded.. and in order to actually use the font,
32     you need to know its name!! Anyway, this awful hack seems to work for most fonts.
33 */
34 namespace TTFNameExtractor
35 {
36     struct OffsetTable
37     {
38         uint32 version;
39         uint16 numTables, searchRange, entrySelector, rangeShift;
40     };
41 
42     struct TableDirectory
43     {
44         char tag[4];
45         uint32 checkSum, offset, length;
46     };
47 
48     struct NamingTable
49     {
50         uint16 formatSelector;
51         uint16 numberOfNameRecords;
52         uint16 offsetStartOfStringStorage;
53     };
54 
55     struct NameRecord
56     {
57         uint16 platformID, encodingID, languageID;
58         uint16 nameID, stringLength, offsetFromStorageArea;
59     };
60 
parseNameRecord(MemoryInputStream & input,const NameRecord & nameRecord,const int64 directoryOffset,const int64 offsetOfStringStorage)61     static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord,
62                                    const int64 directoryOffset, const int64 offsetOfStringStorage)
63     {
64         String result;
65         auto oldPos = input.getPosition();
66         input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea));
67         auto stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength);
68         auto platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID);
69 
70         if (platformID == 0 || platformID == 3)
71         {
72             auto numChars = stringLength / 2 + 1;
73             HeapBlock<uint16> buffer;
74             buffer.calloc (numChars + 1);
75             input.read (buffer, stringLength);
76 
77             for (int i = 0; i < numChars; ++i)
78                 buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]);
79 
80             static_assert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16), "Sanity check UTF-16 type");
81             result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData());
82         }
83         else
84         {
85             HeapBlock<char> buffer;
86             buffer.calloc (stringLength + 1);
87             input.read (buffer, stringLength);
88             result = CharPointer_UTF8 (buffer.getData());
89         }
90 
91         input.setPosition (oldPos);
92         return result;
93     }
94 
parseNameTable(MemoryInputStream & input,int64 directoryOffset)95     static String parseNameTable (MemoryInputStream& input, int64 directoryOffset)
96     {
97         input.setPosition (directoryOffset);
98 
99         NamingTable namingTable = {};
100         input.read (&namingTable, sizeof (namingTable));
101 
102         for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i)
103         {
104             NameRecord nameRecord = {};
105             input.read (&nameRecord, sizeof (nameRecord));
106 
107             if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4)
108             {
109                 const String result (parseNameRecord (input, nameRecord, directoryOffset,
110                                                       ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage)));
111 
112                 if (result.isNotEmpty())
113                     return result;
114             }
115         }
116 
117         return {};
118     }
119 
getTypefaceNameFromFile(MemoryInputStream & input)120     static String getTypefaceNameFromFile (MemoryInputStream& input)
121     {
122         OffsetTable offsetTable = {};
123         input.read (&offsetTable, sizeof (offsetTable));
124 
125         for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i)
126         {
127             TableDirectory tableDirectory;
128             zerostruct (tableDirectory);
129             input.read (&tableDirectory, sizeof (tableDirectory));
130 
131             if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name"))
132                 return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset));
133         }
134 
135         return {};
136     }
137 }
138 
139 namespace FontEnumerators
140 {
fontEnum2(ENUMLOGFONTEXW * lpelfe,NEWTEXTMETRICEXW *,int type,LPARAM lParam)141     static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
142     {
143         if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
144         {
145             const String fontName (lpelfe->elfLogFont.lfFaceName);
146             ((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
147         }
148 
149         return 1;
150     }
151 
fontEnum1(ENUMLOGFONTEXW * lpelfe,NEWTEXTMETRICEXW *,int type,LPARAM lParam)152     static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
153     {
154         if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
155         {
156             LOGFONTW lf = {};
157             lf.lfWeight = FW_DONTCARE;
158             lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
159             lf.lfQuality = DEFAULT_QUALITY;
160             lf.lfCharSet = DEFAULT_CHARSET;
161             lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
162             lf.lfPitchAndFamily = FF_DONTCARE;
163 
164             const String fontName (lpelfe->elfLogFont.lfFaceName);
165             fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
166 
167             auto dc = CreateCompatibleDC (nullptr);
168             EnumFontFamiliesEx (dc, &lf, (FONTENUMPROCW) &fontEnum2, lParam, 0);
169             DeleteDC (dc);
170         }
171 
172         return 1;
173     }
174 }
175 
findAllTypefaceNames()176 StringArray Font::findAllTypefaceNames()
177 {
178     StringArray results;
179 
180    #if JUCE_USE_DIRECTWRITE
181     SharedResourcePointer<Direct2DFactories> factories;
182 
183     if (factories->systemFonts != nullptr)
184     {
185         ComSmartPtr<IDWriteFontFamily> fontFamily;
186         uint32 fontFamilyCount = 0;
187         fontFamilyCount = factories->systemFonts->GetFontFamilyCount();
188 
189         for (uint32 i = 0; i < fontFamilyCount; ++i)
190         {
191             auto hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress());
192 
193             if (SUCCEEDED (hr))
194                 results.addIfNotAlreadyThere (getFontFamilyName (fontFamily));
195         }
196     }
197     else
198    #endif
199     {
200         auto dc = CreateCompatibleDC (nullptr);
201 
202         {
203             LOGFONTW lf = {};
204             lf.lfWeight = FW_DONTCARE;
205             lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
206             lf.lfQuality = DEFAULT_QUALITY;
207             lf.lfCharSet = DEFAULT_CHARSET;
208             lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
209             lf.lfPitchAndFamily = FF_DONTCARE;
210 
211             EnumFontFamiliesEx (dc, &lf,
212                                 (FONTENUMPROCW) &FontEnumerators::fontEnum1,
213                                 (LPARAM) &results, 0);
214         }
215 
216         DeleteDC (dc);
217     }
218 
219     results.sort (true);
220     return results;
221 }
222 
findAllTypefaceStyles(const String & family)223 StringArray Font::findAllTypefaceStyles (const String& family)
224 {
225     if (FontStyleHelpers::isPlaceholderFamilyName (family))
226         return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
227 
228     StringArray results;
229 
230    #if JUCE_USE_DIRECTWRITE
231     SharedResourcePointer<Direct2DFactories> factories;
232 
233     if (factories->systemFonts != nullptr)
234     {
235         BOOL fontFound = false;
236         uint32 fontIndex = 0;
237         auto hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound);
238 
239         if (! fontFound)
240             fontIndex = 0;
241 
242         // Get the font family using the search results
243         // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family
244         ComSmartPtr<IDWriteFontFamily> fontFamily;
245         hr = factories->systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
246 
247         // Get the font faces
248         ComSmartPtr<IDWriteFont> dwFont;
249         uint32 fontFacesCount = 0;
250         fontFacesCount = fontFamily->GetFontCount();
251 
252         for (uint32 i = 0; i < fontFacesCount; ++i)
253         {
254             hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress());
255 
256             // Ignore any algorithmically generated bold and oblique styles..
257             if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE)
258                 results.addIfNotAlreadyThere (getFontFaceName (dwFont));
259         }
260     }
261     else
262    #endif
263     {
264         results.add ("Regular");
265         results.add ("Italic");
266         results.add ("Bold");
267         results.add ("Bold Italic");
268     }
269 
270     return results;
271 }
272 
273 extern bool juce_isRunningInWine();
274 
275 struct DefaultFontNames
276 {
DefaultFontNamesjuce::DefaultFontNames277     DefaultFontNames()
278     {
279         if (juce_isRunningInWine())
280         {
281             // If we're running in Wine, then use fonts that might be available on Linux..
282             defaultSans     = "Bitstream Vera Sans";
283             defaultSerif    = "Bitstream Vera Serif";
284             defaultFixed    = "Bitstream Vera Sans Mono";
285         }
286         else
287         {
288             defaultSans     = "Verdana";
289             defaultSerif    = "Times New Roman";
290             defaultFixed    = "Lucida Console";
291             defaultFallback = "Tahoma";  // (contains plenty of unicode characters)
292         }
293     }
294 
295     String defaultSans, defaultSerif, defaultFixed, defaultFallback;
296 };
297 
getDefaultTypefaceForFont(const Font & font)298 Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
299 {
300     static DefaultFontNames defaultNames;
301 
302     Font newFont (font);
303     auto& faceName = font.getTypefaceName();
304 
305     if (faceName == getDefaultSansSerifFontName())       newFont.setTypefaceName (defaultNames.defaultSans);
306     else if (faceName == getDefaultSerifFontName())      newFont.setTypefaceName (defaultNames.defaultSerif);
307     else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
308 
309     if (font.getTypefaceStyle() == getDefaultStyle())
310         newFont.setTypefaceStyle ("Regular");
311 
312     return Typeface::createSystemTypefaceFor (newFont);
313 }
314 
315 //==============================================================================
316 class WindowsTypeface   : public Typeface
317 {
318 public:
WindowsTypeface(const Font & font)319     WindowsTypeface (const Font& font)  : Typeface (font.getTypefaceName(),
320                                                     font.getTypefaceStyle())
321     {
322         loadFont();
323     }
324 
WindowsTypeface(const void * data,size_t dataSize)325     WindowsTypeface (const void* data, size_t dataSize)
326         : Typeface (String(), String())
327     {
328         DWORD numInstalled = 0;
329         memoryFont = AddFontMemResourceEx (const_cast<void*> (data), (DWORD) dataSize,
330                                            nullptr, &numInstalled);
331 
332         MemoryInputStream m (data, dataSize, false);
333         name = TTFNameExtractor::getTypefaceNameFromFile (m);
334         loadFont();
335     }
336 
~WindowsTypeface()337     ~WindowsTypeface()
338     {
339         SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
340         DeleteDC (dc);
341 
342         if (fontH != nullptr)
343             DeleteObject (fontH);
344 
345         if (memoryFont != nullptr)
346             RemoveFontMemResourceEx (memoryFont);
347     }
348 
getAscent() const349     float getAscent() const                 { return ascent; }
getDescent() const350     float getDescent() const                { return 1.0f - ascent; }
getHeightToPointsFactor() const351     float getHeightToPointsFactor() const   { return heightToPointsFactor; }
352 
getStringWidth(const String & text)353     float getStringWidth (const String& text)
354     {
355         auto utf16 = text.toUTF16();
356         auto numChars = utf16.length();
357         HeapBlock<uint16> results (numChars);
358         float x = 0;
359 
360         if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast<WORD*> (results.getData()),
361                              GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
362         {
363             for (size_t i = 0; i < numChars; ++i)
364                 x += getKerning (dc, results[i], (i + 1) < numChars ? results[i + 1] : -1);
365         }
366 
367         return x;
368     }
369 
getGlyphPositions(const String & text,Array<int> & resultGlyphs,Array<float> & xOffsets)370     void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets)
371     {
372         auto utf16 = text.toUTF16();
373         auto numChars = utf16.length();
374         HeapBlock<uint16> results (numChars);
375         float x = 0;
376 
377         if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast<WORD*> (results.getData()),
378                              GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
379         {
380             resultGlyphs.ensureStorageAllocated ((int) numChars);
381             xOffsets.ensureStorageAllocated ((int) numChars + 1);
382 
383             for (size_t i = 0; i < numChars; ++i)
384             {
385                 resultGlyphs.add (results[i]);
386                 xOffsets.add (x);
387                 x += getKerning (dc, results[i], (i + 1) < numChars ? results[i + 1] : -1);
388             }
389         }
390 
391         xOffsets.add (x);
392     }
393 
getOutlineForGlyph(int glyphNumber,Path & glyphPath)394     bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
395     {
396         if (glyphNumber < 0)
397             glyphNumber = defaultGlyph;
398 
399         GLYPHMETRICS gm;
400         // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
401         auto bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
402                                               &gm, 0, nullptr, &identityMatrix);
403 
404         if (bufSize > 0)
405         {
406             HeapBlock<char> data (bufSize);
407             GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
408                              (DWORD) bufSize, data, &identityMatrix);
409 
410             auto pheader = reinterpret_cast<const TTPOLYGONHEADER*> (data.getData());
411 
412             auto scaleX = 1.0f / tm.tmHeight;
413             auto scaleY = -scaleX;
414 
415             while ((char*) pheader < data + bufSize)
416             {
417                 glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
418                                            scaleY * pheader->pfxStart.y.value);
419 
420                 auto curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
421                 auto curveEnd = ((const char*) pheader) + pheader->cb;
422 
423                 while ((const char*) curve < curveEnd)
424                 {
425                     if (curve->wType == TT_PRIM_LINE)
426                     {
427                         for (int i = 0; i < curve->cpfx; ++i)
428                             glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
429                                               scaleY * curve->apfx[i].y.value);
430                     }
431                     else if (curve->wType == TT_PRIM_QSPLINE)
432                     {
433                         for (int i = 0; i < curve->cpfx - 1; ++i)
434                         {
435                             auto x2 = scaleX * curve->apfx[i].x.value;
436                             auto y2 = scaleY * curve->apfx[i].y.value;
437                             auto x3 = scaleX * curve->apfx[i + 1].x.value;
438                             auto y3 = scaleY * curve->apfx[i + 1].y.value;
439 
440                             if (i < curve->cpfx - 2)
441                             {
442                                 x3 = 0.5f * (x2 + x3);
443                                 y3 = 0.5f * (y2 + y3);
444                             }
445 
446                             glyphPath.quadraticTo (x2, y2, x3, y3);
447                         }
448                     }
449 
450                     curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
451                 }
452 
453                 pheader = (const TTPOLYGONHEADER*) curve;
454 
455                 glyphPath.closeSubPath();
456             }
457         }
458 
459         return true;
460     }
461 
462 private:
463     static const MAT2 identityMatrix;
464     HFONT fontH = {};
465     HGDIOBJ previousFontH = {};
466     HDC dc { CreateCompatibleDC (nullptr) };
467     TEXTMETRIC tm;
468     HANDLE memoryFont = {};
469     float ascent = 1.0f, heightToPointsFactor = 1.0f;
470     int defaultGlyph = -1, heightInPoints = 0;
471     std::unordered_map<uint64, float> kerningPairs;
472 
kerningPairIndex(int glyph1,int glyph2)473     static uint64 kerningPairIndex (int glyph1, int glyph2)
474     {
475         return (((uint64) (uint32) glyph1) << 32) | (uint64) (uint32) glyph2;
476     }
477 
loadFont()478     void loadFont()
479     {
480         SetMapperFlags (dc, 0);
481         SetMapMode (dc, MM_TEXT);
482 
483         LOGFONTW lf = {};
484         lf.lfCharSet = DEFAULT_CHARSET;
485         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
486         lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
487         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
488         lf.lfQuality = PROOF_QUALITY;
489         lf.lfItalic = (BYTE) (style.contains ("Italic") ? TRUE : FALSE);
490         lf.lfWeight = style.contains ("Bold") ? FW_BOLD : FW_NORMAL;
491         lf.lfHeight = -256;
492         name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
493 
494         auto standardSizedFont = CreateFontIndirect (&lf);
495 
496         if (standardSizedFont != nullptr)
497         {
498             if ((previousFontH = SelectObject (dc, standardSizedFont)) != nullptr)
499             {
500                 fontH = standardSizedFont;
501                 OUTLINETEXTMETRIC otm;
502 
503                 if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
504                 {
505                     heightInPoints = (int) otm.otmEMSquare;
506                     lf.lfHeight = -heightInPoints;
507                     fontH = CreateFontIndirect (&lf);
508 
509                     SelectObject (dc, fontH);
510                     DeleteObject (standardSizedFont);
511                 }
512             }
513         }
514 
515         if (GetTextMetrics (dc, &tm))
516         {
517             auto dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f;
518             heightToPointsFactor = (dpi / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight;
519             ascent = tm.tmAscent / (float) tm.tmHeight;
520             std::unordered_map<int, int> glyphsForChars;
521             defaultGlyph = getGlyphForChar (dc, glyphsForChars, tm.tmDefaultChar);
522             createKerningPairs (dc, glyphsForChars, (float) tm.tmHeight);
523         }
524     }
525 
createKerningPairs(HDC hdc,std::unordered_map<int,int> & glyphsForChars,float height)526     void createKerningPairs (HDC hdc, std::unordered_map<int, int>& glyphsForChars, float height)
527     {
528         HeapBlock<KERNINGPAIR> rawKerning;
529         auto numKPs = GetKerningPairs (hdc, 0, nullptr);
530         rawKerning.calloc (numKPs);
531         GetKerningPairs (hdc, numKPs, rawKerning);
532 
533         std::unordered_map<int, int> widthsForGlyphs;
534 
535         for (DWORD i = 0; i < numKPs; ++i)
536         {
537             auto glyph1 = getGlyphForChar (hdc, glyphsForChars, rawKerning[i].wFirst);
538             auto glyph2 = getGlyphForChar (hdc, glyphsForChars, rawKerning[i].wSecond);
539             auto standardWidth = getGlyphWidth (hdc, widthsForGlyphs, glyph1);
540 
541             kerningPairs[kerningPairIndex (glyph1, glyph2)] = (standardWidth + rawKerning[i].iKernAmount) / height;
542             kerningPairs[kerningPairIndex (glyph1, -1)]     = standardWidth / height;
543         }
544     }
545 
getGlyphForChar(HDC dc,std::unordered_map<int,int> & cache,juce_wchar character)546     static int getGlyphForChar (HDC dc, std::unordered_map<int, int>& cache, juce_wchar character)
547     {
548         auto existing = cache.find ((int) character);
549 
550         if (existing != cache.end())
551             return existing->second;
552 
553         const WCHAR charToTest[] = { (WCHAR) character, 0 };
554         WORD index = 0;
555 
556         if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
557               || index == 0xffff)
558             return -1;
559 
560         cache[(int) character] = index;
561         return index;
562     }
563 
getGlyphWidth(HDC dc,std::unordered_map<int,int> & cache,int glyphNumber)564     static int getGlyphWidth (HDC dc, std::unordered_map<int, int>& cache, int glyphNumber)
565     {
566         auto existing = cache.find (glyphNumber);
567 
568         if (existing != cache.end())
569             return existing->second;
570 
571         auto width = getGlyphWidth (dc, glyphNumber);
572         cache[glyphNumber] = width;
573         return width;
574     }
575 
getGlyphWidth(HDC dc,int glyphNumber)576     static int getGlyphWidth (HDC dc, int glyphNumber)
577     {
578         GLYPHMETRICS gm;
579         gm.gmCellIncX = 0;
580         GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &identityMatrix);
581         return gm.gmCellIncX;
582     }
583 
getKerning(HDC hdc,int glyph1,int glyph2)584     float getKerning (HDC hdc, int glyph1, int glyph2)
585     {
586         auto pair = kerningPairs.find (kerningPairIndex (glyph1, glyph2));
587 
588         if (pair != kerningPairs.end())
589             return pair->second;
590 
591         auto single = kerningPairs.find (kerningPairIndex (glyph1, -1));
592 
593         if (single != kerningPairs.end())
594             return single->second;
595 
596         auto width = getGlyphWidth (hdc, glyph1) / (float) tm.tmHeight;
597         kerningPairs[kerningPairIndex (glyph1, -1)] = width;
598         return width;
599     }
600 
601     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface)
602 };
603 
604 const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
605 
createSystemTypefaceFor(const Font & font)606 Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
607 {
608    #if JUCE_USE_DIRECTWRITE
609     SharedResourcePointer<Direct2DFactories> factories;
610 
611     if (factories->systemFonts != nullptr)
612     {
613         std::unique_ptr<WindowsDirectWriteTypeface> wtf (new WindowsDirectWriteTypeface (font, factories->systemFonts));
614 
615         if (wtf->loadedOk())
616             return wtf.release();
617     }
618    #endif
619 
620     return new WindowsTypeface (font);
621 }
622 
createSystemTypefaceFor(const void * data,size_t dataSize)623 Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
624 {
625     return new WindowsTypeface (data, dataSize);
626 }
627 
scanFolderForFonts(const File &)628 void Typeface::scanFolderForFonts (const File&)
629 {
630     jassertfalse; // not implemented on this platform
631 }
632 
633 } // namespace juce
634