diff --git a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp index 06082e47ac..a8859f029d 100644 --- a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp +++ b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp @@ -38,7 +38,7 @@ SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_, #if JUCE_MAC Font font (10.6f); - font.setTypefaceName ("Menlo Regular"); + font.setTypefaceName ("Menlo"); #else Font font (10.0f); font.setTypefaceName (Font::getDefaultMonospacedFontName()); diff --git a/modules/juce_graphics/fonts/juce_CustomTypeface.cpp b/modules/juce_graphics/fonts/juce_CustomTypeface.cpp index a9505f0109..a7fc61339b 100644 --- a/modules/juce_graphics/fonts/juce_CustomTypeface.cpp +++ b/modules/juce_graphics/fonts/juce_CustomTypeface.cpp @@ -102,13 +102,13 @@ namespace CustomTypefaceHelpers //============================================================================== CustomTypeface::CustomTypeface() - : Typeface (String::empty) + : Typeface (String::empty, String::empty) { clear(); } CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) - : Typeface (String::empty) + : Typeface (String::empty, String::empty) { clear(); @@ -116,8 +116,11 @@ CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) BufferedInputStream in (gzin, 32768); name = in.readString(); - isBold = in.readBool(); - isItalic = in.readBool(); + + const bool isBold = in.readBool(); + const bool isItalic = in.readBool(); + style = FontStyleHelpers::getStyleName (isBold, isItalic); + ascent = in.readFloat(); defaultCharacter = CustomTypefaceHelpers::readChar (in); @@ -153,19 +156,27 @@ void CustomTypeface::clear() { defaultCharacter = 0; ascent = 1.0f; - isBold = isItalic = false; + style = "Regular"; zeromem (lookupTable, sizeof (lookupTable)); glyphs.clear(); } -void CustomTypeface::setCharacteristics (const String& name_, const float ascent_, const bool isBold_, - const bool isItalic_, const juce_wchar defaultCharacter_) noexcept +void CustomTypeface::setCharacteristics (const String& name_, const float ascent_, const bool isBold, + const bool isItalic, const juce_wchar defaultCharacter_) noexcept { name = name_; defaultCharacter = defaultCharacter_; ascent = ascent_; - isBold = isBold_; - isItalic = isItalic_; + style = FontStyleHelpers::getStyleName (isBold, isItalic); +} + +void CustomTypeface::setCharacteristics (const String& name_, const String& style_, const float ascent_, + const juce_wchar defaultCharacter_) noexcept +{ + name = name_; + style = style_; + defaultCharacter = defaultCharacter_; + ascent = ascent_; } void CustomTypeface::addGlyph (const juce_wchar character, const Path& path, const float width) noexcept @@ -216,7 +227,7 @@ bool CustomTypeface::loadGlyphIfPossible (const juce_wchar /*characterNeeded*/) void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept { - setCharacteristics (name, typefaceToCopy.getAscent(), isBold, isItalic, defaultCharacter); + setCharacteristics (name, style, typefaceToCopy.getAscent(), defaultCharacter); for (int i = 0; i < numCharacters; ++i) { @@ -256,8 +267,8 @@ bool CustomTypeface::writeToStream (OutputStream& outputStream) GZIPCompressorOutputStream out (&outputStream); out.writeString (name); - out.writeBool (isBold); - out.writeBool (isItalic); + out.writeBool (FontStyleHelpers::isBold (style)); + out.writeBool (FontStyleHelpers::isItalic (style)); out.writeFloat (ascent); CustomTypefaceHelpers::writeChar (out, defaultCharacter); out.writeInt (glyphs.size()); diff --git a/modules/juce_graphics/fonts/juce_CustomTypeface.h b/modules/juce_graphics/fonts/juce_CustomTypeface.h index bd4d14a67c..b37b9ecb62 100644 --- a/modules/juce_graphics/fonts/juce_CustomTypeface.h +++ b/modules/juce_graphics/fonts/juce_CustomTypeface.h @@ -64,19 +64,31 @@ public: void clear(); /** Sets the vital statistics for the typeface. - @param name the typeface's name - @param ascent the ascent - this is normalised to a height of 1.0 and this is - the value that will be returned by Typeface::getAscent(). The - descent is assumed to be (1.0 - ascent) - @param isBold should be true if the typeface is bold - @param isItalic should be true if the typeface is italic - @param defaultCharacter the character to be used as a replacement if there's - no glyph available for the character that's being drawn + @param fontFamily the typeface's font family + @param ascent the ascent - this is normalised to a height of 1.0 and this is + the value that will be returned by Typeface::getAscent(). The + descent is assumed to be (1.0 - ascent) + @param isBold should be true if the typeface is bold + @param isItalic should be true if the typeface is italic + @param defaultCharacter the character to be used as a replacement if there's + no glyph available for the character that's being drawn */ - void setCharacteristics (const String& name, float ascent, + void setCharacteristics (const String& fontFamily, float ascent, bool isBold, bool isItalic, juce_wchar defaultCharacter) noexcept; + /** Sets the vital statistics for the typeface. + @param fontFamily the typeface's font family + @param fontStyle the typeface's font style + @param ascent the ascent - this is normalised to a height of 1.0 and this is + the value that will be returned by Typeface::getAscent(). The + descent is assumed to be (1.0 - ascent) + @param defaultCharacter the character to be used as a replacement if there's + no glyph available for the character that's being drawn + */ + void setCharacteristics (const String& fontFamily, const String& fontStyle, + float ascent, juce_wchar defaultCharacter) noexcept; + /** Adds a glyph to the typeface. The path that is passed in is normalised so that the font height is 1.0, and its @@ -117,7 +129,6 @@ protected: //============================================================================== juce_wchar defaultCharacter; float ascent; - bool isBold, isItalic; //============================================================================== /** If a subclass overrides this, it can load glyphs into the font on-demand. diff --git a/modules/juce_graphics/fonts/juce_Font.cpp b/modules/juce_graphics/fonts/juce_Font.cpp index d43636d612..21985cb97f 100644 --- a/modules/juce_graphics/fonts/juce_Font.cpp +++ b/modules/juce_graphics/fonts/juce_Font.cpp @@ -32,6 +32,7 @@ namespace FontValues const float defaultFontHeight = 14.0f; String fallbackFont; + String fallbackFontStyle; } typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); @@ -62,16 +63,18 @@ public: Typeface::Ptr findTypefaceFor (const Font& font) { - const int flags = font.getStyleFlags() & (Font::bold | Font::italic); const String faceName (font.getTypefaceName()); + const String faceStyle (font.getTypefaceStyle()); - int i; - for (i = faces.size(); --i >= 0;) + jassert (faceName.isNotEmpty()); + + for (int i = faces.size(); --i >= 0;) { CachedFace& face = faces.getReference(i); - if (face.flags == flags - && face.typefaceName == faceName + if (face.typefaceName == faceName + && face.typefaceStyle == faceStyle + && face.typeface != nullptr && face.typeface->isSuitableForFont (font)) { face.lastUsageCount = ++counter; @@ -82,7 +85,7 @@ public: int replaceIndex = 0; size_t bestLastUsageCount = std::numeric_limits::max(); - for (i = faces.size(); --i >= 0;) + for (int i = faces.size(); --i >= 0;) { const size_t lu = faces.getReference(i).lastUsageCount; @@ -95,7 +98,7 @@ public: CachedFace& face = faces.getReference (replaceIndex); face.typefaceName = faceName; - face.flags = flags; + face.typefaceStyle = faceStyle; face.lastUsageCount = ++counter; if (juce_getTypefaceForFont == nullptr) @@ -120,7 +123,7 @@ private: struct CachedFace { CachedFace() noexcept - : lastUsageCount (0), flags (-1) + : lastUsageCount (0) { } @@ -129,8 +132,8 @@ private: // Since the typeface itself doesn't know that it may have this alias, the name under // which it was fetched needs to be stored separately. String typefaceName; + String typefaceStyle; size_t lastUsageCount; - int flags; Typeface::Ptr typeface; }; @@ -152,47 +155,54 @@ void Typeface::setTypefaceCacheSize (int numFontsToCache) class Font::SharedFontInternal : public SingleThreadedReferenceCountedObject { public: - SharedFontInternal (const float height_, const int styleFlags_) noexcept + SharedFontInternal (const String& typefaceStyle_, const float height_) noexcept : typefaceName (Font::getDefaultSansSerifFontName()), + typefaceStyle (typefaceStyle_), height (height_), horizontalScale (1.0f), kerning (0), ascent (0), - styleFlags (styleFlags_), - typeface ((styleFlags_ & (Font::bold | Font::italic)) == 0 + underline (false), + typeface (typefaceStyle_ == Font::getDefaultStyle() ? TypefaceCache::getInstance()->getDefaultTypeface() : nullptr) { } - SharedFontInternal (const String& typefaceName_, const float height_, const int styleFlags_) noexcept + SharedFontInternal (const String& typefaceName_, const String& typefaceStyle_, const float height_) noexcept : typefaceName (typefaceName_), + typefaceStyle (typefaceStyle_), height (height_), horizontalScale (1.0f), kerning (0), ascent (0), - styleFlags (styleFlags_), + underline (false), typeface (nullptr) { + if (typefaceName.isEmpty()) + typefaceName = Font::getDefaultSansSerifFontName(); } SharedFontInternal (const Typeface::Ptr& typeface_) noexcept : typefaceName (typeface_->getName()), + typefaceStyle (typeface_->getStyle()), height (FontValues::defaultFontHeight), horizontalScale (1.0f), kerning (0), ascent (0), - styleFlags (Font::plain), + underline (false), typeface (typeface_) { + jassert (typefaceName.isNotEmpty()); } SharedFontInternal (const SharedFontInternal& other) noexcept : typefaceName (other.typefaceName), + typefaceStyle (other.typefaceStyle), height (other.height), horizontalScale (other.horizontalScale), kerning (other.kerning), ascent (other.ascent), - styleFlags (other.styleFlags), + underline (other.underline), typeface (other.typeface) { } @@ -200,31 +210,44 @@ public: bool operator== (const SharedFontInternal& other) const noexcept { return height == other.height - && styleFlags == other.styleFlags + && underline == other.underline && horizontalScale == other.horizontalScale && kerning == other.kerning - && typefaceName == other.typefaceName; + && typefaceName == other.typefaceName + && typefaceStyle == other.typefaceStyle; } - String typefaceName; + String typefaceName, typefaceStyle; float height, horizontalScale, kerning, ascent; - int styleFlags; + bool underline; Typeface::Ptr typeface; }; //============================================================================== Font::Font() - : font (new SharedFontInternal (FontValues::defaultFontHeight, Font::plain)) + : font (new SharedFontInternal (Font::getDefaultStyle(), FontValues::defaultFontHeight)) { } Font::Font (const float fontHeight, const int styleFlags) - : font (new SharedFontInternal (FontValues::limitFontHeight (fontHeight), styleFlags)) + : font (new SharedFontInternal (Font::getDefaultStyle(), FontValues::limitFontHeight (fontHeight))) { + setStyleFlags(styleFlags); } Font::Font (const String& typefaceName, const float fontHeight, const int styleFlags) - : font (new SharedFontInternal (typefaceName, FontValues::limitFontHeight (fontHeight), styleFlags)) + : font (new SharedFontInternal (typefaceName, Font::getDefaultStyle(), FontValues::limitFontHeight (fontHeight))) +{ + setStyleFlags (styleFlags); +} + +Font::Font (const String& typefaceStyle, float fontHeight) + : font (new SharedFontInternal (typefaceStyle, FontValues::limitFontHeight (fontHeight))) +{ +} + +Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight) + : font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight))) { } @@ -297,6 +320,12 @@ const String& Font::getDefaultMonospacedFontName() return name; } +const String& Font::getDefaultStyle() +{ + static const String style (""); + return style; +} + const String& Font::getTypefaceName() const noexcept { return font->typefaceName; @@ -306,6 +335,8 @@ void Font::setTypefaceName (const String& faceName) { if (faceName != font->typefaceName) { + jassert (faceName.isNotEmpty()); + dupeInternalIfShared(); font->typefaceName = faceName; font->typeface = nullptr; @@ -313,6 +344,22 @@ void Font::setTypefaceName (const String& faceName) } } +const String& Font::getTypefaceStyle() const noexcept +{ + return font->typefaceStyle; +} + +void Font::setTypefaceStyle (const String& typefaceStyle) +{ + if (typefaceStyle != font->typefaceStyle) + { + dupeInternalIfShared(); + font->typefaceStyle = typefaceStyle; + font->typeface = nullptr; + font->ascent = 0; + } +} + Typeface* Font::getTypeface() const { if (font->typeface == nullptr) @@ -336,6 +383,20 @@ void Font::setFallbackFontName (const String& name) #endif } +const String& Font::getFallbackFontStyle() +{ + return FontValues::fallbackFontStyle; +} + +void Font::setFallbackFontStyle (const String& style) +{ + FontValues::fallbackFontStyle = style; + + #if JUCE_MAC || JUCE_IOS + jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX.. + #endif +} + //============================================================================== float Font::getHeight() const noexcept { @@ -374,7 +435,12 @@ void Font::setHeightWithoutChangingWidth (float newHeight) int Font::getStyleFlags() const noexcept { - return font->styleFlags; + int styleFlags = font->underline ? underlined : plain; + + if (isBold()) styleFlags |= bold; + if (isItalic()) styleFlags |= italic; + + return styleFlags; } Font Font::withStyle (const int newFlags) const @@ -386,10 +452,12 @@ Font Font::withStyle (const int newFlags) const void Font::setStyleFlags (const int newFlags) { - if (font->styleFlags != newFlags) + if (getStyleFlags() != newFlags) { dupeInternalIfShared(); - font->styleFlags = newFlags; + font->typefaceStyle = FontStyleHelpers::getStyleName ((newFlags & bold) != 0, + (newFlags & italic) != 0); + font->underline = (newFlags & underlined) != 0; font->typeface = nullptr; font->ascent = 0; } @@ -415,6 +483,26 @@ void Font::setSizeAndStyle (float newHeight, setStyleFlags (newStyleFlags); } +void Font::setSizeAndStyle (float newHeight, + const String& newStyle, + const float newHorizontalScale, + const float newKerningAmount) +{ + newHeight = FontValues::limitFontHeight (newHeight); + + if (font->height != newHeight + || font->horizontalScale != newHorizontalScale + || font->kerning != newKerningAmount) + { + dupeInternalIfShared(); + font->height = newHeight; + font->horizontalScale = newHorizontalScale; + font->kerning = newKerningAmount; + } + + setTypefaceStyle (newStyle); +} + float Font::getHorizontalScale() const noexcept { return font->horizontalScale; @@ -451,33 +539,41 @@ void Font::setExtraKerningFactor (const float extraKerning) font->kerning = extraKerning; } -Font Font::boldened() const { return withStyle (font->styleFlags | bold); } -Font Font::italicised() const { return withStyle (font->styleFlags | italic); } +Font Font::boldened() const { return withStyle (getStyleFlags() | bold); } +Font Font::italicised() const { return withStyle (getStyleFlags() | italic); } -bool Font::isBold() const noexcept { return (font->styleFlags & bold) != 0; } -bool Font::isItalic() const noexcept { return (font->styleFlags & italic) != 0; } +bool Font::isBold() const noexcept +{ + return FontStyleHelpers::isBold (font->typefaceStyle); +} + +bool Font::isItalic() const noexcept +{ + return FontStyleHelpers::isItalic (font->typefaceStyle); +} void Font::setBold (const bool shouldBeBold) { - setStyleFlags (shouldBeBold ? (font->styleFlags | bold) - : (font->styleFlags & ~bold)); + const int flags = getStyleFlags(); + setStyleFlags (shouldBeBold ? (flags | bold) + : (flags & ~bold)); } void Font::setItalic (const bool shouldBeItalic) { - setStyleFlags (shouldBeItalic ? (font->styleFlags | italic) - : (font->styleFlags & ~italic)); + const int flags = getStyleFlags(); + setStyleFlags (shouldBeItalic ? (flags | italic) + : (flags & ~italic)); } void Font::setUnderline (const bool shouldBeUnderlined) { - setStyleFlags (shouldBeUnderlined ? (font->styleFlags | underlined) - : (font->styleFlags & ~underlined)); + font->underline = shouldBeUnderlined; } bool Font::isUnderlined() const noexcept { - return (font->styleFlags & underlined) != 0; + return font->underline; } float Font::getAscent() const @@ -537,23 +633,30 @@ void Font::findFonts (Array& destArray) const StringArray names (findAllTypefaceNames()); for (int i = 0; i < names.size(); ++i) - destArray.add (Font (names[i], FontValues::defaultFontHeight, Font::plain)); + { + const StringArray styles (findAllTypefaceStyles (names[i])); + + String style ("Regular"); + + if (! styles.contains (style, true)) + style = styles[0]; + + destArray.add (Font (names[i], style, FontValues::defaultFontHeight)); + } } //============================================================================== String Font::toString() const { - String s (getTypefaceName()); + String s; - if (s == getDefaultSansSerifFontName()) - s = String::empty; - else - s += "; "; + if (getTypefaceName() != getDefaultSansSerifFontName()) + s << getTypefaceName() << "; "; - s += String (getHeight(), 1); + s << String (getHeight(), 1); - if (isBold()) s += " bold"; - if (isItalic()) s += " italic"; + if (getTypefaceStyle() != getDefaultStyle()) + s << ' ' << getTypefaceStyle(); return s; } @@ -575,9 +678,7 @@ Font Font::fromString (const String& fontDescription) if (height <= 0) height = 10.0f; - int flags = Font::plain; - if (sizeAndStyle.containsIgnoreCase ("bold")) flags |= Font::bold; - if (sizeAndStyle.containsIgnoreCase ("italic")) flags |= Font::italic; + const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false)); - return Font (name, height, flags); + return Font (name, style, height); } diff --git a/modules/juce_graphics/fonts/juce_Font.h b/modules/juce_graphics/fonts/juce_Font.h index b237d636ac..b65636a83a 100644 --- a/modules/juce_graphics/fonts/juce_Font.h +++ b/modules/juce_graphics/fonts/juce_Font.h @@ -68,7 +68,7 @@ public: /** Creates a font with a given typeface and parameters. - @param typefaceName the name of the typeface to use + @param typefaceName the font family of the typeface to use @param fontHeight the height in pixels (can be fractional) @param styleFlags the style to use - this can be a combination of the Font::bold, Font::italic and Font::underlined, or @@ -77,6 +77,21 @@ public: */ Font (const String& typefaceName, float fontHeight, int styleFlags); + /** Creates a sans-serif font in a given style and size. + + @param typefaceStyle the font style of the typeface to use + @param fontHeight the height in pixels (can be fractional) + */ + Font (const String& typefaceStyle, float fontHeight); + + /** Creates a font with a given typeface and parameters. + + @param typefaceName the font family of the typeface to use + @param typefaceStyle the font style of the typeface to use + @param fontHeight the height in pixels (can be fractional) + */ + Font (const String& typefaceName, const String& typefaceStyle, float fontHeight); + /** Creates a copy of another Font object. */ Font (const Font& other) noexcept; @@ -106,64 +121,88 @@ public: ~Font() noexcept; //============================================================================== - /** Changes the name of the typeface family. + /** Changes the font family of the typeface. e.g. "Arial", "Courier", etc. This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), - or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, - but are generic names that are used to represent the various default fonts. - If you need to know the exact typeface name being used, you can call - Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, + but are generic font family names that are used to represent the various default fonts. + If you need to know the exact typeface font family being used, you can call + Font::getTypeface()->getFamily(), which will give you the platform-specific font family. If a suitable font isn't found on the machine, it'll just use a default instead. */ void setTypefaceName (const String& faceName); - /** Returns the name of the typeface family that this font uses. + /** Returns the font family of the typeface that this font uses. e.g. "Arial", "Courier", etc. This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), - or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, - but are generic names that are used to represent the various default fonts. + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, + but are generic font familiy names that are used to represent the various default fonts. - If you need to know the exact typeface name being used, you can call - Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. + If you need to know the exact typeface font family being used, you can call + Font::getTypeface()->getFamily(), which will give you the platform-specific font family. */ const String& getTypefaceName() const noexcept; //============================================================================== - /** Returns a typeface name that represents the default sans-serif font. + /** Changes the font style of the typeface + + e.g. "Regular", "Italic", etc. + + */ + void setTypefaceStyle (const String& typefaceStyle); + + /** Returns the font style of the typeface that this font uses. + + e.g. "Regular", "Italic", etc. + + */ + const String& getTypefaceStyle() const noexcept; + + //============================================================================== + /** Returns a typeface font family that represents the default sans-serif font. This is also the typeface that will be used when a font is created without specifying any typeface details. Note that this method just returns a generic placeholder string that means "the default - sans-serif font" - it's not the actual name of this font. + sans-serif font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName */ static const String& getDefaultSansSerifFontName(); - /** Returns a typeface name that represents the default sans-serif font. + /** Returns a typeface font family that represents the default sans-serif font. Note that this method just returns a generic placeholder string that means "the default - serif font" - it's not the actual name of this font. + serif font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName */ static const String& getDefaultSerifFontName(); - /** Returns a typeface name that represents the default sans-serif font. + /** Returns a typeface font family that represents the default sans-serif font. Note that this method just returns a generic placeholder string that means "the default - monospaced font" - it's not the actual name of this font. + monospaced font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName */ static const String& getDefaultMonospacedFontName(); + /** Returns a typeface font style that represents the default sans-serif font. + + Note that this method just returns a generic placeholder string that means "the default + font style" - it's not the actual font style of this font. + + @see setTypefaceStyle + */ + static const String& getDefaultStyle(); + /** Returns the default system typeface for the given font. */ static Typeface::Ptr getDefaultTypefaceForFont (const Font& font); @@ -299,6 +338,12 @@ public: float newHorizontalScale, float newKerningAmount); + /** Changes all the font's characteristics with one call. */ + void setSizeAndStyle (float newHeight, + const String& newStyle, + float newHorizontalScale, + float newKerningAmount); + //============================================================================== /** Returns the total width of a string as it would be drawn using this font. @@ -329,33 +374,52 @@ public: /** Creates an array of Font objects to represent all the fonts on the system. - If you just need the names of the typefaces, you can also use + If you just need the font family names of the typefaces, you can also use findAllTypefaceNames() instead. @param results the array to which new Font objects will be added. */ static void findFonts (Array& results); - /** Returns a list of all the available typeface names. + /** Returns a list of all the available typeface font families. The names returned can be passed into setTypefaceName(). - You can use this instead of findFonts() if you only need their names, and not - font objects. + You can use this instead of findFonts() if you only need their font family names, + and not font objects. */ static StringArray findAllTypefaceNames(); + /** Returns a list of all the available typeface font styles. + + The names returned can be passed into setTypefaceStyle(). + + You can use this instead of findFonts() if you only need their styles, and not + font objects. + */ + static StringArray findAllTypefaceStyles (const String& family); + //============================================================================== - /** Returns the name of the typeface to be used for rendering glyphs that aren't found - in the requested typeface. + /** Returns the font family of the typeface to be used for rendering glyphs that aren't + found in the requested typeface. */ static const String& getFallbackFontName(); - /** Sets the (platform-specific) name of the typeface to use to find glyphs that aren't - available in whatever font you're trying to use. + /** Sets the (platform-specific) font family of the typeface to use to find glyphs that + aren't available in whatever font you're trying to use. */ static void setFallbackFontName (const String& name); + /** Returns the font style of the typeface to be used for rendering glyphs that aren't + found in the requested typeface. + */ + static const String& getFallbackFontStyle(); + + /** Sets the (platform-specific) font style of the typeface to use to find glyphs that + aren't available in whatever font you're trying to use. + */ + static void setFallbackFontStyle (const String& style); + //============================================================================== /** Creates a string to describe this font. The string will contain information to describe the font's typeface, size, and diff --git a/modules/juce_graphics/fonts/juce_Typeface.cpp b/modules/juce_graphics/fonts/juce_Typeface.cpp index 1c183080e6..e6025b801c 100644 --- a/modules/juce_graphics/fonts/juce_Typeface.cpp +++ b/modules/juce_graphics/fonts/juce_Typeface.cpp @@ -23,8 +23,45 @@ ============================================================================== */ -Typeface::Typeface (const String& name_) noexcept - : name (name_) +namespace FontStyleHelpers +{ + static const char* getStyleName (const bool bold, + const bool italic) noexcept + { + if (bold && ! italic) return "Bold"; + if (italic && ! bold) return "Italic"; + if (bold && italic) return "Bold Italic"; + return "Regular"; + } + + static bool isBold (const String& style) noexcept + { + return style.containsWholeWordIgnoreCase ("Bold"); + } + + static bool isItalic (const String& style) noexcept + { + return style.containsWholeWordIgnoreCase ("Italic") + || style.containsWholeWordIgnoreCase ("Oblique"); + } + + static bool isPlaceholderFamilyName (const String& family) + { + return family == Font::getDefaultSansSerifFontName() + || family == Font::getDefaultSerifFontName() + || family == Font::getDefaultMonospacedFontName(); + } + + static String getConcreteFamilyNameFromPlaceholder (const String& family) + { + const Font f (family, Font::getDefaultStyle(), 15.0f); + return Font::getDefaultTypefaceForFont (f)->getName(); + } +} + +//============================================================================== +Typeface::Typeface (const String& name_, const String& style_) noexcept + : name (name_), style (style_) { } @@ -34,7 +71,7 @@ Typeface::~Typeface() Typeface::Ptr Typeface::getFallbackTypeface() { - const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + const Font fallbackFont (Font::getFallbackFontName(), Font::getFallbackFontStyle(), 10.0f); return fallbackFont.getTypeface(); } diff --git a/modules/juce_graphics/fonts/juce_Typeface.h b/modules/juce_graphics/fonts/juce_Typeface.h index 507c5e159b..b44856dc57 100644 --- a/modules/juce_graphics/fonts/juce_Typeface.h +++ b/modules/juce_graphics/fonts/juce_Typeface.h @@ -55,11 +55,17 @@ public: typedef ReferenceCountedObjectPtr Ptr; //============================================================================== - /** Returns the name of the typeface. + /** Returns the font family of the typeface. @see Font::getTypefaceName */ const String& getName() const noexcept { return name; } + //============================================================================== + /** Returns the font style of the typeface. + @see Font::getTypefaceStyle + */ + const String& getStyle() const noexcept { return style; } + //============================================================================== /** Creates a new system typeface. */ static Ptr createSystemTypefaceFor (const Font& font); @@ -122,9 +128,9 @@ public: protected: //============================================================================== - String name; + String name, style; - explicit Typeface (const String& name) noexcept; + Typeface (const String& name, const String& style) noexcept; static Ptr getFallbackTypeface(); diff --git a/modules/juce_graphics/juce_graphics.cpp b/modules/juce_graphics/juce_graphics.cpp index e5d7646ce9..77fe2e9302 100644 --- a/modules/juce_graphics/juce_graphics.cpp +++ b/modules/juce_graphics/juce_graphics.cpp @@ -67,8 +67,6 @@ namespace juce { -// START_AUTOINCLUDE colour/*.cpp, geometry/*.cpp, placement/*.cpp, contexts/*.cpp, images/*.cpp, -// image_formats/*.cpp, fonts/*.cpp, effects/*.cpp #include "colour/juce_Colour.cpp" #include "colour/juce_ColourGradient.cpp" #include "colour/juce_Colours.cpp" @@ -92,14 +90,13 @@ namespace juce #include "image_formats/juce_JPEGLoader.cpp" #include "image_formats/juce_PNGLoader.cpp" #include "fonts/juce_AttributedString.cpp" +#include "fonts/juce_Typeface.cpp" #include "fonts/juce_CustomTypeface.cpp" #include "fonts/juce_Font.cpp" #include "fonts/juce_GlyphArrangement.cpp" #include "fonts/juce_TextLayout.cpp" -#include "fonts/juce_Typeface.cpp" #include "effects/juce_DropShadowEffect.cpp" #include "effects/juce_GlowEffect.cpp" -// END_AUTOINCLUDE //============================================================================== #if JUCE_MAC || JUCE_IOS diff --git a/modules/juce_graphics/native/juce_android_Fonts.cpp b/modules/juce_graphics/native/juce_android_Fonts.cpp index a04c522363..fa2b7846c7 100644 --- a/modules/juce_graphics/native/juce_android_Fonts.cpp +++ b/modules/juce_graphics/native/juce_android_Fonts.cpp @@ -41,7 +41,22 @@ StringArray Font::findAllTypefaceNames() File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, "*.ttf"); for (int i = 0; i < fonts.size(); ++i) - results.add (fonts.getReference(i).getFileNameWithoutExtension()); + results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension() + .upToLastOccurrenceOf ("-", false, false)); + + return results; +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + StringArray results ("Regular"); + + Array fonts; + File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, family + "-*.ttf"); + + for (int i = 0; i < fonts.size(); ++i) + results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension() + .fromLastOccurrenceOf ("-", false, false)); return results; } @@ -79,24 +94,27 @@ class AndroidTypeface : public Typeface { public: AndroidTypeface (const Font& font) - : Typeface (font.getTypefaceName()), + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), ascent (0), descent (0) { - jint flags = 0; - if (font.isBold()) flags = 1; - if (font.isItalic()) flags += 2; + JNIEnv* const env = getEnv(); - JNIEnv* env = getEnv(); + const bool isBold = style.contains ("Bold"); + const bool isItalic = style.contains ("Italic"); - File fontFile (File ("/system/fonts").getChildFile (name).withFileExtension (".ttf")); + File fontFile (getFontFile (name, style)); + + if (! fontFile.exists()) + fontFile = findFontFile (name, isBold, isItalic); if (fontFile.exists()) typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, javaString (fontFile.getFullPathName()).get())); else typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, - javaString (getName()).get(), flags)); + javaString (getName()).get(), + (isBold ? 1 : 0) + (isItalic ? 2 : 0))); rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0)); @@ -212,6 +230,41 @@ public: float ascent, descent, unitsToHeightScaleFactor; private: + static File findFontFile (const String& family, + const bool bold, const bool italic) + { + File file; + + if (bold || italic) + { + String suffix; + if (bold) suffix = "Bold"; + if (italic) suffix << "Italic"; + + file = getFontFile (family, suffix); + + if (file.exists()) + return file; + } + + file = getFontFile (family, "Regular"); + + if (! file.exists()) + file = getFontFile (family, String::empty); + + return file; + } + + static File getFontFile (const String& family, const String& style) + { + String path ("/system/fonts/" + family); + + if (style.isNotEmpty()) + path << '-' << style; + + return File (path + ".ttf"); + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface); }; diff --git a/modules/juce_graphics/native/juce_linux_Fonts.cpp b/modules/juce_graphics/native/juce_linux_Fonts.cpp index aee5b1dd01..a82904cc38 100644 --- a/modules/juce_graphics/native/juce_linux_Fonts.cpp +++ b/modules/juce_graphics/native/juce_linux_Fonts.cpp @@ -222,10 +222,29 @@ public: } //============================================================================== - void getFamilyNames (StringArray& familyNames) const + StringArray findAllFamilyNames() const { + StringArray s; + for (int i = 0; i < faces.size(); i++) - familyNames.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + s.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + + return s; + } + + StringArray findAllTypefaceStyles (const String& family) const + { + StringArray s; + + for (int i = 0; i < faces.size(); i++) + { + const KnownTypeface* const face = faces.getUnchecked(i); + + if (face->family == family) + s.addIfNotAlreadyThere (FontStyleHelpers::getStyleName (face->isBold, face->isItalic)); + } + + return s; } void getMonospacedNames (StringArray& monoSpaced) const @@ -298,14 +317,13 @@ public: if (faceWrapper != nullptr) { setCharacteristics (font.getTypefaceName(), + font.getTypefaceStyle(), faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), - font.isBold(), font.isItalic(), L' '); } else { - DBG ("Failed to create typeface: " << font.getTypefaceName() << " " - << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' ')); + DBG ("Failed to create typeface: " << font.toString()); } } @@ -457,10 +475,12 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) StringArray Font::findAllTypefaceNames() { - StringArray s; - FTTypefaceList::getInstance()->getFamilyNames (s); - s.sort (true); - return s; + return FTTypefaceList::getInstance()->findAllFamilyNames(); +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + return FTTypefaceList::getInstance()->findAllTypefaceStyles (family); } //============================================================================== @@ -480,17 +500,16 @@ private: { const StringArray choices (choicesArray); - int j; - for (j = 0; j < choices.size(); ++j) + for (int j = 0; j < choices.size(); ++j) if (names.contains (choices[j], true)) return choices[j]; - for (j = 0; j < choices.size(); ++j) + for (int j = 0; j < choices.size(); ++j) for (int i = 0; i < names.size(); ++i) if (names[i].startsWithIgnoreCase (choices[j])) return names[i]; - for (j = 0; j < choices.size(); ++j) + for (int j = 0; j < choices.size(); ++j) for (int i = 0; i < names.size(); ++i) if (names[i].containsIgnoreCase (choices[j])) return names[i]; diff --git a/modules/juce_graphics/native/juce_mac_Fonts.mm b/modules/juce_graphics/native/juce_mac_Fonts.mm index 5fe704c99a..216ad69b87 100644 --- a/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -34,52 +34,37 @@ namespace CoreTextTypeLayout { static CTFontRef createCTFont (const Font& font, const float fontSize, - const bool applyScaleFactor, bool& needsItalicTransform) + const bool applyScaleFactor) { - CFStringRef cfName = font.getTypefaceName().toCFString(); - CTFontRef ctFontRef = CTFontCreateWithName (cfName, fontSize, nullptr); - CFRelease (cfName); + CFStringRef cfFontFamily = font.getTypefaceName().toCFString(); + CFStringRef cfFontStyle = font.getTypefaceStyle().toCFString(); + CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute }; + CFTypeRef values[] = { cfFontFamily, cfFontStyle }; - if (ctFontRef != nullptr) + CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, + (const void**) &values, + numElementsInArray (keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cfFontStyle); + CFRelease (cfFontFamily); + + CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); + CFRelease (fontDescAttributes); + + CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSize, nullptr); + CFRelease (ctFontDescRef); + + if (applyScaleFactor) { - if (font.isItalic()) - { - CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, - kCTFontItalicTrait, kCTFontItalicTrait); + CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); + const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef)); + const float factor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight; + CGFontRelease (cgFontRef); - if (newFont != nullptr) - { - CFRelease (ctFontRef); - ctFontRef = newFont; - } - else - { - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - } - } - - if (font.isBold()) - { - CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, - kCTFontBoldTrait, kCTFontBoldTrait); - if (newFont != nullptr) - { - CFRelease (ctFontRef); - ctFontRef = newFont; - } - } - - if (applyScaleFactor) - { - CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); - const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef)); - const float factor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight; - CGFontRelease (cgFontRef); - - CTFontRef newFont = CTFontCreateCopyWithAttributes (ctFontRef, fontSize * factor, nullptr, nullptr); - CFRelease (ctFontRef); - ctFontRef = newFont; - } + CTFontRef newFont = CTFontCreateCopyWithAttributes (ctFontRef, fontSize * factor, nullptr, nullptr); + CFRelease (ctFontRef); + ctFontRef = newFont; } return ctFontRef; @@ -164,8 +149,7 @@ namespace CoreTextTypeLayout if (attr->getFont() != nullptr) { const Font& f = *attr->getFont(); - bool needsItalicTransform = false; - CTFontRef ctFontRef = createCTFont (f, f.getHeight(), true, needsItalicTransform); + CTFontRef ctFontRef = createCTFont (f, f.getHeight(), true); CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), kCTFontAttributeName, ctFontRef); @@ -317,17 +301,24 @@ namespace CoreTextTypeLayout CTFontRef ctRunFont; if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void **) &ctRunFont)) { - CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont); - CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, 1024, nullptr); + CTFontDescriptorRef ctFontDescRef = CTFontCopyFontDescriptor (ctRunFont); + CFDictionaryRef fontDescAttributes = CTFontDescriptorCopyAttributes (ctFontDescRef); + CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, 1024, nullptr); + CFRelease (ctFontDescRef); + CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); CFRelease (ctFontRef); const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef)); const float fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight; CGFontRelease (cgFontRef); - glyphRun->font = Font (String::fromCFString (cfsFontName), - CTFontGetSize (ctRunFont) / fontHeightToCGSizeFactor, 0); // XXX bold/italic flags? - CFRelease (cfsFontName); + CFStringRef cfsFontFamily = (CFStringRef) CFDictionaryGetValue (fontDescAttributes, kCTFontFamilyNameAttribute); + CFStringRef cfsFontStyle = (CFStringRef) CFDictionaryGetValue (fontDescAttributes, kCTFontStyleNameAttribute); + + glyphRun->font = Font (String::fromCFString (cfsFontFamily), + String::fromCFString (cfsFontStyle), + CTFontGetSize (ctRunFont) / fontHeightToCGSizeFactor); + CFRelease (fontDescAttributes); } CGColorRef cgRunColor; @@ -360,7 +351,8 @@ class OSXTypeface : public Typeface { public: OSXTypeface (const Font& font) - : Typeface (font.getTypefaceName()), + : Typeface (font.getTypefaceName(), + font.getTypefaceStyle()), fontRef (nullptr), fontHeightToCGSizeFactor (1.0f), renderingTransform (CGAffineTransformIdentity), @@ -369,8 +361,7 @@ public: ascent (0.0f), unitsToHeightScaleFactor (0.0f) { - bool needsItalicTransform = false; - ctFontRef = CoreTextTypeLayout::createCTFont (font, 1024.0f, false, needsItalicTransform); + ctFontRef = CoreTextTypeLayout::createCTFont (font, 1024.0f, false); if (ctFontRef != nullptr) { @@ -380,12 +371,6 @@ public: pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); - if (needsItalicTransform) - { - pathTransform = pathTransform.sheared (-0.15f, 0.0f); - renderingTransform.c = 0.15f; - } - fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); @@ -550,6 +535,87 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface); }; +StringArray Font::findAllTypefaceNames() +{ + StringArray names; + + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 && ! JUCE_IOS + // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS + CFArrayRef fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames(); + + for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i) + { + const String family (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i))); + + if (! family.startsWithChar ('.')) // ignore fonts that start with a '.' + names.addIfNotAlreadyThere (family); + } + + CFRelease (fontFamilyArray); + #else + CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr); + CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef); + CFRelease (fontCollectionRef); + + for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i) + { + CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i); + CFStringRef cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute); + + names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily)); + + CFRelease (cfsFontFamily); + } + + CFRelease (fontDescriptorArray); + #endif + + names.sort (true); + return names; +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + if (FontStyleHelpers::isPlaceholderFamilyName (family)) + return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); + + StringArray results; + + CFStringRef cfsFontFamily = family.toCFString(); + CFStringRef keys[] = { kCTFontFamilyNameAttribute }; + CFTypeRef values[] = { cfsFontFamily }; + + CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease (cfsFontFamily); + + CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); + CFRelease (fontDescAttributes); + + CFArrayRef fontFamilyArray = CFArrayCreate(kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks); + CFRelease (ctFontDescRef); + + CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr); + CFRelease (fontFamilyArray); + + CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef); + CFRelease (fontCollectionRef); + + if (fontDescriptorArray != nullptr) + { + for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i) + { + CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i); + CFStringRef cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute); + results.add (String::fromCFString (cfsFontStyle)); + CFRelease (cfsFontStyle); + } + + CFRelease (fontDescriptorArray); + } + + return results; +} + #else //============================================================================== @@ -580,49 +646,13 @@ class OSXTypeface : public Typeface { public: OSXTypeface (const Font& font) - : Typeface (font.getTypefaceName()) + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()) { JUCE_AUTORELEASEPOOL renderingTransform = CGAffineTransformIdentity; - bool needsItalicTransform = false; - #if JUCE_IOS - NSString* fontName = juceStringToNS (font.getTypefaceName()); - - if (font.isItalic() || font.isBold()) - { - NSArray* familyFonts = [UIFont fontNamesForFamilyName: juceStringToNS (font.getTypefaceName())]; - - for (NSString* i in familyFonts) - { - const String fn (nsStringToJuce (i)); - const String afterDash (fn.fromFirstOccurrenceOf ("-", false, false)); - - const bool probablyBold = afterDash.containsIgnoreCase ("bold") || fn.endsWithIgnoreCase ("bold"); - const bool probablyItalic = afterDash.containsIgnoreCase ("oblique") - || afterDash.containsIgnoreCase ("italic") - || fn.endsWithIgnoreCase ("oblique") - || fn.endsWithIgnoreCase ("italic"); - - if (probablyBold == font.isBold() - && probablyItalic == font.isItalic()) - { - fontName = i; - needsItalicTransform = false; - break; - } - else if (probablyBold && (! probablyItalic) && probablyBold == font.isBold()) - { - fontName = i; - needsItalicTransform = true; // not ideal, so carry on in case we find a better one - } - } - - if (needsItalicTransform) - renderingTransform.c = 0.15f; - } - + NSString* fontName = juceStringToNS (style); fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); if (fontRef == 0) @@ -638,21 +668,12 @@ public: unitsToHeightScaleFactor = 1.0f / totalHeight; fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / totalHeight; #else - nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; + NSDictionary* nsDict = [NSDictionary dictionaryWithObjectsAndKeys: + juceStringToNS (name), NSFontFamilyAttribute, + juceStringToNS (style), NSFontFaceAttribute, nil]; - if (font.isItalic()) - { - NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: nsFont - toHaveTrait: NSItalicFontMask]; - - if (newFont == nsFont) - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - - nsFont = newFont; - } - - if (font.isBold()) - nsFont = [[NSFontManager sharedFontManager] convertFont: nsFont toHaveTrait: NSBoldFontMask]; + NSFontDescriptor* nsFontDesc = [NSFontDescriptor fontDescriptorWithFontAttributes: nsDict]; + nsFont = [NSFont fontWithDescriptor: nsFontDesc size: 1024]; [nsFont retain]; @@ -662,12 +683,6 @@ public: pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); - if (needsItalicTransform) - { - pathTransform = pathTransform.sheared (-0.15f, 0.0f); - renderingTransform.c = 0.15f; - } - #if SUPPORT_ONLY_10_4_FONTS ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); @@ -910,7 +925,7 @@ private: #endif } -#if ! SUPPORT_ONLY_10_4_FONTS + #if ! SUPPORT_ONLY_10_4_FONTS // Reads a CGFontRef's character map table to convert unicode into glyph numbers class CharToGlyphMapper { @@ -1009,23 +1024,14 @@ private: }; ScopedPointer charToGlyphMapper; -#endif + #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface); }; -#endif - -//============================================================================== -Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) -{ - return new OSXTypeface (font); -} - StringArray Font::findAllTypefaceNames() { StringArray names; - JUCE_AUTORELEASEPOOL #if JUCE_IOS @@ -1041,6 +1047,43 @@ StringArray Font::findAllTypefaceNames() return names; } +StringArray Font::findAllTypefaceStyles (const String& family) +{ + if (FontStyleHelpers::isPlaceholderFamilyName (family)) + return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); + + StringArray results; + JUCE_AUTORELEASEPOOL + + #if JUCE_IOS + NSArray* styles = [UIFont fontNamesForFamilyName: juceStringToNS (family)]; + #else + NSArray* styles = [[NSFontManager sharedFontManager] availableMembersOfFontFamily: juceStringToNS (family)]; + #endif + + for (unsigned int i = 0; i < [styles count]; ++i) + { + #if JUCE_IOS + // Fonts are returned in the form of "Arial-BoldMT" + results.add (nsStringToJuce ((NSString*) [styles objectAtIndex: i])); + #else + NSArray* style = [styles objectAtIndex: i]; + results.add (nsStringToJuce ((NSString*) [style objectAtIndex: 1])); + #endif + + } + + return results; +} + +#endif + +//============================================================================== +Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new OSXTypeface (font); +} + struct DefaultFontNames { DefaultFontNames() @@ -1064,15 +1107,24 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; - String faceName (font.getTypefaceName()); + Font newFont (font); + const String& faceName = font.getTypefaceName(); - if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; + if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); + else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); + else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed); - Font f (font); - f.setTypefaceName (faceName); - return Typeface::createSystemTypefaceFor (f); + if (font.getTypefaceStyle() == getDefaultStyle()) + newFont.setTypefaceStyle ("Regular"); + + #if JUCE_IOS && ! JUCE_CORETEXT_AVAILABLE + // Fonts style names on Cocoa Touch are unusual like "Arial-BoldMT" + // No font will be found for the style of "Regular" so we must modify the style + if (newFont.getTypefaceStyle() == "Regular") + newFont.setTypefaceStyle (faceName); + #endif + + return Typeface::createSystemTypefaceFor (newFont); } bool TextLayout::createNativeLayout (const AttributedString& text) diff --git a/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index f63bf79f6c..af5b89cad2 100644 --- a/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -89,8 +89,8 @@ namespace DirectWriteTypeLayout glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, glyphRun)); glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, glyphRun)); - int styleFlags = 0; - const String fontName (getFontName (glyphRun, styleFlags)); + String fontFamily, fontStyle; + getFontFamilyAndStyle (glyphRun, fontFamily, fontStyle); TextLayout::Run* const glyphRunLayout = new TextLayout::Run (Range (runDescription->textPosition, runDescription->textPosition + runDescription->stringLength), @@ -102,7 +102,7 @@ namespace DirectWriteTypeLayout const float totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent); const float fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight; - glyphRunLayout->font = Font (fontName, glyphRun->fontEmSize / fontHeightToEmSizeFactor, styleFlags); + glyphRunLayout->font = Font (fontFamily, fontStyle, glyphRun->fontEmSize / fontHeightToEmSizeFactor); glyphRunLayout->colour = getColourOf (static_cast (clientDrawingEffect)); const Point lineOrigin (layout->getLine (currentLine).lineOrigin); @@ -145,62 +145,29 @@ namespace DirectWriteTypeLayout return Colour::fromFloatRGBA (colour.r, colour.g, colour.b, colour.a); } - String getFontName (DWRITE_GLYPH_RUN const* glyphRun, int& styleFlags) const + void getFontFamilyAndStyle (DWRITE_GLYPH_RUN const* glyphRun, String& family, String& style) const { ComSmartPtr dwFont; - HRESULT hr = fontCollection->GetFontFromFontFace (glyphRun->fontFace, dwFont.resetAndGetPointerAddress()); jassert (dwFont != nullptr); - if (dwFont->GetWeight() == DWRITE_FONT_WEIGHT_BOLD) styleFlags |= Font::bold; - if (dwFont->GetStyle() == DWRITE_FONT_STYLE_ITALIC) styleFlags |= Font::italic; + { + ComSmartPtr dwFontFamily; + hr = dwFont->GetFontFamily (dwFontFamily.resetAndGetPointerAddress()); + family = getFontFamilyName (dwFontFamily); + } - ComSmartPtr dwFontFamily; - hr = dwFont->GetFontFamily (dwFontFamily.resetAndGetPointerAddress()); - jassert (dwFontFamily != nullptr); - - // Get the Font Family Names - ComSmartPtr dwFamilyNames; - hr = dwFontFamily->GetFamilyNames (dwFamilyNames.resetAndGetPointerAddress()); - jassert (dwFamilyNames != nullptr); - - UINT32 index = 0; - BOOL exists = false; - hr = dwFamilyNames->FindLocaleName (L"en-us", &index, &exists); - if (! exists) - index = 0; - - UINT32 length = 0; - hr = dwFamilyNames->GetStringLength (index, &length); - - HeapBlock name (length + 1); - hr = dwFamilyNames->GetString (index, name, length + 1); - - return String (name); + style = getFontFaceName (dwFont); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomDirectWriteTextRenderer); }; //================================================================================================== - float getFontHeightToEmSizeFactor (const Font& font, IDWriteFontCollection& dwFontCollection) + float getFontHeightToEmSizeFactor (IDWriteFont* const dwFont) { - BOOL fontFound = false; - uint32 fontIndex; - dwFontCollection.FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound); - - if (! fontFound) - fontIndex = 0; - - ComSmartPtr dwFontFamily; - HRESULT hr = dwFontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); - - ComSmartPtr dwFont; - hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, - dwFont.resetAndGetPointerAddress()); - ComSmartPtr dwFontFace; - hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); + dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); DWRITE_FONT_METRICS dwFontMetrics; dwFontFace->GetMetrics (&dwFontMetrics); @@ -251,13 +218,35 @@ namespace DirectWriteTypeLayout if (font != nullptr) { - textLayout->SetFontFamilyName (font->getTypefaceName().toWideCharPointer(), range); + BOOL fontFound = false; + uint32 fontIndex; + fontCollection->FindFamilyName (font->getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound); - const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*font, *fontCollection); + if (! fontFound) + fontIndex = 0; + + ComSmartPtr fontFamily; + HRESULT hr = fontCollection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); + + ComSmartPtr dwFont; + uint32 fontFacesCount = 0; + fontFacesCount = fontFamily->GetFontCount(); + + for (uint32 i = 0; i < fontFacesCount; ++i) + { + hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); + + if (attr.getFont()->getTypefaceStyle() == getFontFaceName (dwFont)) + break; + } + + textLayout->SetFontFamilyName (attr.getFont()->getTypefaceName().toWideCharPointer(), range); + textLayout->SetFontWeight (dwFont->GetWeight(), range); + textLayout->SetFontStretch (dwFont->GetStretch(), range); + textLayout->SetFontStyle (dwFont->GetStyle(), range); + + const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); textLayout->SetFontSize (font->getHeight() * fontHeightToEmSizeFactor, range); - - if (font->isBold()) textLayout->SetFontWeight (DWRITE_FONT_WEIGHT_BOLD, range); - if (font->isItalic()) textLayout->SetFontStyle (DWRITE_FONT_STYLE_ITALIC, range); } if (attr.getColour() != nullptr) @@ -290,7 +279,21 @@ namespace DirectWriteTypeLayout HRESULT hr = direct2dFactory->CreateDCRenderTarget (&d2dRTProp, renderTarget.resetAndGetPointerAddress()); Font defaultFont; - const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (defaultFont, *fontCollection); + BOOL fontFound = false; + uint32 fontIndex; + fontCollection->FindFamilyName (defaultFont.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound); + + if (! fontFound) + fontIndex = 0; + + ComSmartPtr dwFontFamily; + hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); + + ComSmartPtr dwFont; + hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, + dwFont.resetAndGetPointerAddress()); + + const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); jassert (directWriteFactory != nullptr); diff --git a/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp b/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp index 0f1869c7fe..81ee555a6d 100644 --- a/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp +++ b/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp @@ -24,6 +24,47 @@ */ #if JUCE_USE_DIRECTWRITE +namespace +{ + static String getLocalisedName (IDWriteLocalizedStrings* names) + { + jassert (names != nullptr); + + uint32 index = 0; + BOOL exists = false; + HRESULT hr = names->FindLocaleName (L"en-us", &index, &exists); + if (! exists) + index = 0; + + uint32 length = 0; + hr = names->GetStringLength (index, &length); + + HeapBlock name (length + 1); + hr = names->GetString (index, name, length + 1); + + return static_cast (name); + } + + static String getFontFamilyName (IDWriteFontFamily* family) + { + jassert (family != nullptr); + ComSmartPtr familyNames; + HRESULT hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); + jassert (SUCCEEDED (hr)); (void) hr; + return getLocalisedName (familyNames); + } + + static String getFontFaceName (IDWriteFont* font) + { + jassert (font != nullptr); + ComSmartPtr faceNames; + HRESULT hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); + jassert (SUCCEEDED (hr)); (void) hr; + + return getLocalisedName (faceNames); + } +} + class Direct2DFactories { public: @@ -86,7 +127,7 @@ class WindowsDirectWriteTypeface : public Typeface { public: WindowsDirectWriteTypeface (const Font& font, IDWriteFontCollection* fontCollection) - : Typeface (font.getTypefaceName()), + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), ascent (0.0f) { jassert (fontCollection != nullptr); @@ -102,12 +143,22 @@ public: ComSmartPtr dwFontFamily; hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); - // Get a specific font in the font family using certain weight and style flags + // Get a specific font in the font family using typeface style ComSmartPtr dwFont; - DWRITE_FONT_WEIGHT dwWeight = font.isBold() ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; - DWRITE_FONT_STYLE dwStyle = font.isItalic() ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; + uint32 fontFacesCount = 0; + fontFacesCount = dwFontFamily->GetFontCount(); + + for (uint32 i = 0; i < fontFacesCount; ++i) + { + hr = dwFontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); + + ComSmartPtr faceNames; + hr = dwFont->GetFaceNames (faceNames.resetAndGetPointerAddress()); + + if (font.getTypefaceStyle() == getLocalisedName (faceNames)) + break; + } - hr = dwFontFamily->GetFirstMatchingFont (dwWeight, DWRITE_FONT_STRETCH_NORMAL, dwStyle, dwFont.resetAndGetPointerAddress()); hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); DWRITE_FONT_METRICS dwFontMetrics; diff --git a/modules/juce_graphics/native/juce_win32_Fonts.cpp b/modules/juce_graphics/native/juce_win32_Fonts.cpp index 8d692a30af..7838e7ae3d 100644 --- a/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -25,7 +25,7 @@ namespace FontEnumerators { - int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) + static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) { if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0) { @@ -36,7 +36,7 @@ namespace FontEnumerators return 1; } - int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) + static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) { if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0) { @@ -65,28 +65,99 @@ namespace FontEnumerators StringArray Font::findAllTypefaceNames() { StringArray results; - HDC dc = CreateCompatibleDC (0); + #if JUCE_USE_DIRECTWRITE + const Direct2DFactories& factories = Direct2DFactories::getInstance(); + + if (factories.systemFonts != nullptr) { - LOGFONTW lf = { 0 }; - lf.lfWeight = FW_DONTCARE; - lf.lfOutPrecision = OUT_OUTLINE_PRECIS; - lf.lfQuality = DEFAULT_QUALITY; - lf.lfCharSet = DEFAULT_CHARSET; - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lf.lfPitchAndFamily = FF_DONTCARE; + ComSmartPtr fontFamily; + uint32 fontFamilyCount = 0; + fontFamilyCount = factories.systemFonts->GetFontFamilyCount(); - EnumFontFamiliesEx (dc, &lf, - (FONTENUMPROCW) &FontEnumerators::fontEnum1, - (LPARAM) &results, 0); + for (uint32 i = 0; i < fontFamilyCount; ++i) + { + HRESULT hr = factories.systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress()); + + if (SUCCEEDED (hr)) + results.addIfNotAlreadyThere (getFontFamilyName (fontFamily)); + } + } + else + #endif + { + HDC dc = CreateCompatibleDC (0); + + { + LOGFONTW lf = { 0 }; + lf.lfWeight = FW_DONTCARE; + lf.lfOutPrecision = OUT_OUTLINE_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfPitchAndFamily = FF_DONTCARE; + + EnumFontFamiliesEx (dc, &lf, + (FONTENUMPROCW) &FontEnumerators::fontEnum1, + (LPARAM) &results, 0); + } + + DeleteDC (dc); } - DeleteDC (dc); - results.sort (true); return results; } +StringArray Font::findAllTypefaceStyles (const String& family) +{ + if (FontStyleHelpers::isPlaceholderFamilyName (family)) + return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); + + StringArray results; + + #if JUCE_USE_DIRECTWRITE + const Direct2DFactories& factories = Direct2DFactories::getInstance(); + + if (factories.systemFonts != nullptr) + { + BOOL fontFound = false; + uint32 fontIndex = 0; + HRESULT hr = factories.systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound); + if (! fontFound) + fontIndex = 0; + + // Get the font family using the search results + // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family + ComSmartPtr fontFamily; + hr = factories.systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); + + // Get the font faces + ComSmartPtr dwFont; + uint32 fontFacesCount = 0; + fontFacesCount = fontFamily->GetFontCount(); + + for (uint32 i = 0; i < fontFacesCount; ++i) + { + hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); + + // Ignore any algorithmically generated bold and oblique styles.. + if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE) + results.addIfNotAlreadyThere (getFontFaceName (dwFont)); + } + } + else + #endif + { + results.add ("Regular"); + results.add ("Italic"); + results.add ("Bold"); + results.add ("Bold Italic"); + } + + return results; +} + extern bool juce_IsRunningInWine(); struct DefaultFontNames @@ -116,15 +187,17 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; - String faceName (font.getTypefaceName()); + Font newFont (font); + const String& faceName = font.getTypefaceName(); - if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; + if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); + else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); + else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed); - Font f (font); - f.setTypefaceName (faceName); - return Typeface::createSystemTypefaceFor (f); + if (font.getTypefaceStyle() == getDefaultStyle()) + newFont.setTypefaceStyle ("Regular"); + + return Typeface::createSystemTypefaceFor (newFont); } //============================================================================== @@ -132,14 +205,13 @@ class WindowsTypeface : public Typeface { public: WindowsTypeface (const Font& font) - : Typeface (font.getTypefaceName()), + : Typeface (font.getTypefaceName(), + font.getTypefaceStyle()), fontH (0), previousFontH (0), dc (CreateCompatibleDC (0)), ascent (1.0f), - defaultGlyph (-1), - bold (font.isBold()), - italic (font.isItalic()) + defaultGlyph (-1) { loadFont(); @@ -282,7 +354,6 @@ private: TEXTMETRIC tm; float ascent; int defaultGlyph; - bool bold, italic; struct KerningPair { @@ -314,8 +385,8 @@ private: lf.lfOutPrecision = OUT_OUTLINE_PRECIS; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; lf.lfQuality = PROOF_QUALITY; - lf.lfItalic = (BYTE) (italic ? TRUE : FALSE); - lf.lfWeight = bold ? FW_BOLD : FW_NORMAL; + lf.lfItalic = (BYTE) (style == "Italic" ? TRUE : FALSE); + lf.lfWeight = style == "Bold" ? FW_BOLD : FW_NORMAL; lf.lfHeight = -256; name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));