2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 // (This file gets included by juce_linux_NativeCode.cpp, rather than being
27 // compiled on its own).
28 #if JUCE_INCLUDED_FILE
31 //==============================================================================
32 class FreeTypeFontFace
35 //==============================================================================
43 //==============================================================================
44 FreeTypeFontFace (const String
& familyName
)
51 void setFileName (const String
& name
, const int faceIndex
, FontStyle style
)
53 if (names
[(int) style
].fileName
.isEmpty())
55 names
[(int) style
].fileName
= name
;
56 names
[(int) style
].faceIndex
= faceIndex
;
60 const String
& getFamilyName() const noexcept
{ return family
; }
62 const String
& getFileName (const int style
, int& faceIndex
) const noexcept
64 faceIndex
= names
[style
].faceIndex
;
65 return names
[style
].fileName
;
68 void setMonospaced (bool mono
) noexcept
{ monospaced
= mono
; }
69 bool getMonospaced() const noexcept
{ return monospaced
; }
71 void setSerif (const bool serif
) noexcept
{ hasSerif
= serif
; }
72 bool getSerif() const noexcept
{ return hasSerif
; }
75 //==============================================================================
84 FontNameIndex names
[4];
85 bool hasSerif
, monospaced
;
88 //==============================================================================
89 class LinuxFontFileIterator
92 LinuxFontFileIterator()
95 fontDirs
.addTokens (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty
);
96 fontDirs
.removeEmptyStrings (true);
98 if (fontDirs
.size() == 0)
100 const ScopedPointer
<XmlElement
> fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf")));
102 if (fontsInfo
!= nullptr)
104 forEachXmlChildElementWithTagName (*fontsInfo
, e
, "dir")
106 fontDirs
.add (e
->getAllSubText().trim());
111 if (fontDirs
.size() == 0)
112 fontDirs
.add ("/usr/X11R6/lib/X11/fonts");
114 fontDirs
.removeEmptyStrings (true);
122 if (getFile().hasFileExtension ("ttf;pfb;pcf"))
126 if (index
>= fontDirs
.size())
129 iter
= new DirectoryIterator (fontDirs
[index
++], true);
133 File
getFile() const { jassert (iter
!= nullptr); return iter
->getFile(); }
136 StringArray fontDirs
;
138 ScopedPointer
<DirectoryIterator
> iter
;
142 //==============================================================================
143 class FreeTypeInterface
: public DeletedAtShutdown
146 //==============================================================================
153 if (FT_Init_FreeType (&ftLib
) != 0)
156 DBG ("Failed to initialize FreeType");
159 LinuxFontFileIterator fontFileIterator
;
161 while (fontFileIterator
.next())
169 if (FT_New_Face (ftLib
, fontFileIterator
.getFile().getFullPathName().toUTF8(),
170 faceIndex
, &face
) == 0)
173 numFaces
= face
->num_faces
;
175 if ((face
->face_flags
& FT_FACE_FLAG_SCALABLE
) != 0)
177 FreeTypeFontFace
* const newFace
= findOrCreate (face
->family_name
, true);
178 int style
= (int) FreeTypeFontFace::Plain
;
180 if ((face
->style_flags
& FT_STYLE_FLAG_BOLD
) != 0)
181 style
|= (int) FreeTypeFontFace::Bold
;
183 if ((face
->style_flags
& FT_STYLE_FLAG_ITALIC
) != 0)
184 style
|= (int) FreeTypeFontFace::Italic
;
186 newFace
->setFileName (fontFileIterator
.getFile().getFullPathName(),
187 faceIndex
, (FreeTypeFontFace::FontStyle
) style
);
188 newFace
->setMonospaced ((face
->face_flags
& FT_FACE_FLAG_FIXED_WIDTH
) != 0);
190 // Surely there must be a better way to do this?
191 const String
name (face
->family_name
);
192 newFace
->setSerif (! (name
.containsIgnoreCase ("Sans")
193 || name
.containsIgnoreCase ("Verdana")
194 || name
.containsIgnoreCase ("Arial")));
196 //DBG (fontFileIterator.getFile().getFullPathName() << " - " << name);
204 while (faceIndex
< numFaces
);
211 FT_Done_Face (lastFace
);
214 FT_Done_FreeType (ftLib
);
216 clearSingletonInstance();
219 //==============================================================================
220 FreeTypeFontFace
* findOrCreate (const String
& familyName
, const bool create
= false)
222 for (int i
= 0; i
< faces
.size(); i
++)
223 if (faces
[i
]->getFamilyName() == familyName
)
229 FreeTypeFontFace
* newFace
= new FreeTypeFontFace (familyName
);
235 // Create a FreeType face object for a given font
236 FT_Face
createFT_Face (const String
& fontName
, const bool bold
, const bool italic
)
240 if (fontName
== lastFontName
&& bold
== lastBold
&& italic
== lastItalic
)
248 FT_Done_Face (lastFace
);
252 lastFontName
= fontName
;
256 FreeTypeFontFace
* const ftFace
= findOrCreate (fontName
);
260 int style
= (int) FreeTypeFontFace::Plain
;
263 style
|= (int) FreeTypeFontFace::Bold
;
266 style
|= (int) FreeTypeFontFace::Italic
;
269 String
fileName (ftFace
->getFileName (style
, faceIndex
));
271 if (fileName
.isEmpty())
273 style
^= (int) FreeTypeFontFace::Bold
;
275 fileName
= ftFace
->getFileName (style
, faceIndex
);
277 if (fileName
.isEmpty())
279 style
^= (int) FreeTypeFontFace::Bold
;
280 style
^= (int) FreeTypeFontFace::Italic
;
282 fileName
= ftFace
->getFileName (style
, faceIndex
);
284 if (! fileName
.length())
286 style
^= (int) FreeTypeFontFace::Bold
;
287 fileName
= ftFace
->getFileName (style
, faceIndex
);
292 if (! FT_New_Face (ftLib
, fileName
.toUTF8(), faceIndex
, &lastFace
))
296 // If there isn't a unicode charmap then select the first one.
297 if (FT_Select_Charmap (face
, ft_encoding_unicode
))
298 FT_Set_Charmap (face
, face
->charmaps
[0]);
306 bool addGlyph (FT_Face face
, CustomTypeface
& dest
, uint32 character
)
308 const unsigned int glyphIndex
= FT_Get_Char_Index (face
, character
);
309 const float height
= (float) (face
->ascender
- face
->descender
);
310 const float scaleX
= 1.0f
/ height
;
311 const float scaleY
= -1.0f
/ height
;
314 if (FT_Load_Glyph (face
, glyphIndex
, FT_LOAD_NO_SCALE
| FT_LOAD_NO_BITMAP
| FT_LOAD_IGNORE_TRANSFORM
) != 0
315 || face
->glyph
->format
!= ft_glyph_format_outline
)
320 const FT_Outline
* const outline
= &face
->glyph
->outline
;
321 const short* const contours
= outline
->contours
;
322 const char* const tags
= outline
->tags
;
323 FT_Vector
* const points
= outline
->points
;
325 for (int c
= 0; c
< outline
->n_contours
; c
++)
327 const int startPoint
= (c
== 0) ? 0 : contours
[c
- 1] + 1;
328 const int endPoint
= contours
[c
];
330 for (int p
= startPoint
; p
<= endPoint
; p
++)
332 const float x
= scaleX
* points
[p
].x
;
333 const float y
= scaleY
* points
[p
].y
;
337 if (FT_CURVE_TAG (tags
[p
]) == FT_Curve_Tag_Conic
)
339 float x2
= scaleX
* points
[endPoint
].x
;
340 float y2
= scaleY
* points
[endPoint
].y
;
342 if (FT_CURVE_TAG (tags
[endPoint
]) != FT_Curve_Tag_On
)
344 x2
= (x
+ x2
) * 0.5f
;
345 y2
= (y
+ y2
) * 0.5f
;
348 destShape
.startNewSubPath (x2
, y2
);
352 destShape
.startNewSubPath (x
, y
);
356 if (FT_CURVE_TAG (tags
[p
]) == FT_Curve_Tag_On
)
359 destShape
.lineTo (x
, y
);
361 else if (FT_CURVE_TAG (tags
[p
]) == FT_Curve_Tag_Conic
)
363 const int nextIndex
= (p
== endPoint
) ? startPoint
: p
+ 1;
364 float x2
= scaleX
* points
[nextIndex
].x
;
365 float y2
= scaleY
* points
[nextIndex
].y
;
367 if (FT_CURVE_TAG (tags
[nextIndex
]) == FT_Curve_Tag_Conic
)
369 x2
= (x
+ x2
) * 0.5f
;
370 y2
= (y
+ y2
) * 0.5f
;
377 destShape
.quadraticTo (x
, y
, x2
, y2
);
379 else if (FT_CURVE_TAG (tags
[p
]) == FT_Curve_Tag_Cubic
)
384 const int next1
= p
+ 1;
385 const int next2
= (p
== (endPoint
- 1)) ? startPoint
: p
+ 2;
387 const float x2
= scaleX
* points
[next1
].x
;
388 const float y2
= scaleY
* points
[next1
].y
;
389 const float x3
= scaleX
* points
[next2
].x
;
390 const float y3
= scaleY
* points
[next2
].y
;
392 if (FT_CURVE_TAG (tags
[next1
]) != FT_Curve_Tag_Cubic
393 || FT_CURVE_TAG (tags
[next2
]) != FT_Curve_Tag_On
)
396 destShape
.cubicTo (x
, y
, x2
, y2
, x3
, y3
);
401 destShape
.closeSubPath();
404 dest
.addGlyph (character
, destShape
, face
->glyph
->metrics
.horiAdvance
/ height
);
406 if ((face
->face_flags
& FT_FACE_FLAG_KERNING
) != 0)
407 addKerning (face
, dest
, character
, glyphIndex
);
412 void addKerning (FT_Face face
, CustomTypeface
& dest
, const uint32 character
, const uint32 glyphIndex
)
414 const float height
= (float) (face
->ascender
- face
->descender
);
416 uint32 rightGlyphIndex
;
417 uint32 rightCharCode
= FT_Get_First_Char (face
, &rightGlyphIndex
);
419 while (rightGlyphIndex
!= 0)
423 if (FT_Get_Kerning (face
, glyphIndex
, rightGlyphIndex
, ft_kerning_unscaled
, &kerning
) == 0)
426 dest
.addKerningPair (character
, rightCharCode
, kerning
.x
/ height
);
429 rightCharCode
= FT_Get_Next_Char (face
, rightCharCode
, &rightGlyphIndex
);
433 // Add a glyph to a font
434 bool addGlyphToFont (const uint32 character
, const String
& fontName
,
435 bool bold
, bool italic
, CustomTypeface
& dest
)
437 FT_Face face
= createFT_Face (fontName
, bold
, italic
);
439 return face
!= 0 && addGlyph (face
, dest
, character
);
442 //==============================================================================
443 void getFamilyNames (StringArray
& familyNames
) const
445 for (int i
= 0; i
< faces
.size(); i
++)
446 familyNames
.add (faces
[i
]->getFamilyName());
449 void getMonospacedNames (StringArray
& monoSpaced
) const
451 for (int i
= 0; i
< faces
.size(); i
++)
452 if (faces
[i
]->getMonospaced())
453 monoSpaced
.add (faces
[i
]->getFamilyName());
456 void getSerifNames (StringArray
& serif
) const
458 for (int i
= 0; i
< faces
.size(); i
++)
459 if (faces
[i
]->getSerif())
460 serif
.add (faces
[i
]->getFamilyName());
463 void getSansSerifNames (StringArray
& sansSerif
) const
465 for (int i
= 0; i
< faces
.size(); i
++)
466 if (! faces
[i
]->getSerif())
467 sansSerif
.add (faces
[i
]->getFamilyName());
470 juce_DeclareSingleton_SingleThreaded_Minimal (FreeTypeInterface
);
473 //==============================================================================
477 bool lastBold
, lastItalic
;
478 OwnedArray
<FreeTypeFontFace
> faces
;
481 juce_ImplementSingleton_SingleThreaded (FreeTypeInterface
)
484 //==============================================================================
485 class FreetypeTypeface
: public CustomTypeface
488 FreetypeTypeface (const Font
& font
)
490 FT_Face face
= FreeTypeInterface::getInstance()
491 ->createFT_Face (font
.getTypefaceName(), font
.isBold(), font
.isItalic());
496 String
msg ("Failed to create typeface: ");
497 msg
<< font
.getTypefaceName() << " " << (font
.isBold() ? 'B' : ' ') << (font
.isItalic() ? 'I' : ' ');
503 setCharacteristics (font
.getTypefaceName(),
504 face
->ascender
/ (float) (face
->ascender
- face
->descender
),
505 font
.isBold(), font
.isItalic(),
510 bool loadGlyphIfPossible (juce_wchar character
)
512 return FreeTypeInterface::getInstance()
513 ->addGlyphToFont (character
, name
, isBold
, isItalic
, *this);
517 Typeface::Ptr
Typeface::createSystemTypefaceFor (const Font
& font
)
519 return new FreetypeTypeface (font
);
522 //==============================================================================
523 StringArray
Font::findAllTypefaceNames()
526 FreeTypeInterface::getInstance()->getFamilyNames (s
);
531 namespace LinuxFontHelpers
533 String
pickBestFont (const StringArray
& names
,
534 const char* const* choicesString
)
536 const StringArray
choices (choicesString
);
539 for (j
= 0; j
< choices
.size(); ++j
)
540 if (names
.contains (choices
[j
], true))
543 for (j
= 0; j
< choices
.size(); ++j
)
544 for (i
= 0; i
< names
.size(); i
++)
545 if (names
[i
].startsWithIgnoreCase (choices
[j
]))
548 for (j
= 0; j
< choices
.size(); ++j
)
549 for (i
= 0; i
< names
.size(); i
++)
550 if (names
[i
].containsIgnoreCase (choices
[j
]))
556 String
getDefaultSansSerifFontName()
558 StringArray allFonts
;
559 FreeTypeInterface::getInstance()->getSansSerifNames (allFonts
);
561 const char* targets
[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans", "Sans", 0 };
562 return pickBestFont (allFonts
, targets
);
565 String
getDefaultSerifFontName()
567 StringArray allFonts
;
568 FreeTypeInterface::getInstance()->getSerifNames (allFonts
);
570 const char* targets
[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman", "Serif", 0 };
571 return pickBestFont (allFonts
, targets
);
574 String
getDefaultMonospacedFontName()
576 StringArray allFonts
;
577 FreeTypeInterface::getInstance()->getMonospacedNames (allFonts
);
579 const char* targets
[] = { "Bitstream Vera Sans Mono", "Courier", "Sans Mono", "Mono", 0 };
580 return pickBestFont (allFonts
, targets
);
584 void Font::getPlatformDefaultFontNames (String
& defaultSans
, String
& defaultSerif
, String
& defaultFixed
, String
& /*defaultFallback*/)
586 defaultSans
= LinuxFontHelpers::getDefaultSansSerifFontName();
587 defaultSerif
= LinuxFontHelpers::getDefaultSerifFontName();
588 defaultFixed
= LinuxFontHelpers::getDefaultMonospacedFontName();