Path: blob/master/src/java.desktop/share/classes/sun/font/FontFamily.java
41154 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.font;2627import java.io.File;28import java.awt.Font;29import java.io.IOException;30import java.util.Collection;31import java.util.HashMap;32import java.util.concurrent.ConcurrentHashMap;33import java.util.Locale;3435public class FontFamily {3637private static ConcurrentHashMap<String, FontFamily>38familyNameMap = new ConcurrentHashMap<String, FontFamily>();39private static HashMap<String, FontFamily> allLocaleNames;4041protected String familyName;42protected Font2D plain;43protected Font2D bold;44protected Font2D italic;45protected Font2D bolditalic;46protected boolean logicalFont = false;47protected int familyRank;4849public static FontFamily getFamily(String name) {50return familyNameMap.get(name.toLowerCase(Locale.ENGLISH));51}5253public static String[] getAllFamilyNames() {54return null;55}5657/* Only for use by FontManager.deRegisterBadFont(..).58* If this was the only font in the family, the family is removed59* from the map60*/61static void remove(Font2D font2D) {6263String name = font2D.getFamilyName(Locale.ENGLISH);64FontFamily family = getFamily(name);65if (family == null) {66return;67}68if (family.plain == font2D) {69family.plain = null;70}71if (family.bold == font2D) {72family.bold = null;73}74if (family.italic == font2D) {75family.italic = null;76}77if (family.bolditalic == font2D) {78family.bolditalic = null;79}80if (family.plain == null && family.bold == null &&81family.italic == null && family.bolditalic == null) {82familyNameMap.remove(name);83}84}8586public FontFamily(String name, boolean isLogFont, int rank) {87logicalFont = isLogFont;88familyName = name;89familyRank = rank;90familyNameMap.put(name.toLowerCase(Locale.ENGLISH), this);91}9293/* Create a family for created fonts which aren't listed in the94* main map.95*/96FontFamily(String name) {97logicalFont = false;98familyName = name;99familyRank = Font2D.DEFAULT_RANK;100}101102public String getFamilyName() {103return familyName;104}105106public int getRank() {107return familyRank;108}109110private boolean isFromSameSource(Font2D font) {111if (!(font instanceof FileFont)) {112return false;113}114115FileFont existingFont = null;116if (plain instanceof FileFont) {117existingFont = (FileFont)plain;118} else if (bold instanceof FileFont) {119existingFont = (FileFont)bold;120} else if (italic instanceof FileFont) {121existingFont = (FileFont)italic;122} else if (bolditalic instanceof FileFont) {123existingFont = (FileFont)bolditalic;124}125// A family isn't created until there's a font.126// So if we didn't find a file font it means this127// isn't a file-based family.128if (existingFont == null) {129return false;130}131File existDir = (new File(existingFont.platName)).getParentFile();132133FileFont newFont = (FileFont)font;134File newDir = (new File(newFont.platName)).getParentFile();135if (existDir != null) {136try {137existDir = existDir.getCanonicalFile();138} catch (IOException ignored) {}139}140if (newDir != null) {141try {142newDir = newDir.getCanonicalFile();143} catch (IOException ignored) {}144}145return java.util.Objects.equals(newDir, existDir);146}147148/*149* We want a family to be of the same width and prefer medium/normal width.150* Once we find a particular width we accept more of the same width151* until we find one closer to normal when we 'evict' all existing fonts.152* So once we see a 'normal' width font we evict all members that are not153* normal width and then accept only new ones that are normal width.154*155* Once a font passes the width test we subject it to the weight test.156* For Plain we target the weight the closest that is <= NORMAL (400)157* For Bold we target the weight that is closest to BOLD (700).158*159* In the future, rather than discarding these fonts, we should160* extend the family to include these so lookups on these properties161* can locate them, as presently they will only be located by full name162* based lookup.163*/164165private int familyWidth = 0;166private boolean preferredWidth(Font2D font) {167168int newWidth = font.getWidth();169170if (familyWidth == 0) {171familyWidth = newWidth;172return true;173}174175if (newWidth == familyWidth) {176return true;177}178179if (Math.abs(Font2D.FWIDTH_NORMAL - newWidth) <180Math.abs(Font2D.FWIDTH_NORMAL - familyWidth))181{182if (FontUtilities.debugFonts()) {183FontUtilities.logInfo(184"Found more preferred width. New width = " + newWidth +185" Old width = " + familyWidth + " in font " + font +186" nulling out fonts plain: " + plain + " bold: " + bold +187" italic: " + italic + " bolditalic: " + bolditalic);188}189familyWidth = newWidth;190plain = bold = italic = bolditalic = null;191return true;192} else if (FontUtilities.debugFonts()) {193FontUtilities.logInfo(194"Family rejecting font " + font +195" of less preferred width " + newWidth);196}197return false;198}199200private boolean closerWeight(Font2D currFont, Font2D font, int style) {201if (familyWidth != font.getWidth()) {202return false;203}204205if (currFont == null) {206return true;207}208209if (FontUtilities.debugFonts()) {210FontUtilities.logInfo(211"New weight for style " + style + ". Curr.font=" + currFont +212" New font="+font+" Curr.weight="+ + currFont.getWeight()+213" New weight="+font.getWeight());214}215216int newWeight = font.getWeight();217switch (style) {218case Font.PLAIN:219case Font.ITALIC:220return (newWeight <= Font2D.FWEIGHT_NORMAL &&221newWeight > currFont.getWeight());222223case Font.BOLD:224case Font.BOLD|Font.ITALIC:225return (Math.abs(newWeight - Font2D.FWEIGHT_BOLD) <226Math.abs(currFont.getWeight() - Font2D.FWEIGHT_BOLD));227228default:229return false;230}231}232233public void setFont(Font2D font, int style) {234235if (FontUtilities.isLogging()) {236String msg;237if (font instanceof CompositeFont) {238msg = "Request to add " + font.getFamilyName(null) +239" with style " + style + " to family " + familyName;240} else {241msg = "Request to add " + font +242" with style " + style + " to family " + this;243}244FontUtilities.logInfo(msg);245}246/* Allow a lower-rank font only if its a file font247* from the exact same source as any previous font.248*/249if ((font.getRank() > familyRank) && !isFromSameSource(font)) {250if (FontUtilities.isLogging()) {251FontUtilities.logWarning("Rejecting adding " + font +252" of lower rank " + font.getRank() +253" to family " + this +254" of rank " + familyRank);255}256return;257}258259switch (style) {260261case Font.PLAIN:262if (preferredWidth(font) && closerWeight(plain, font, style)) {263plain = font;264}265break;266267case Font.BOLD:268if (preferredWidth(font) && closerWeight(bold, font, style)) {269bold = font;270}271break;272273case Font.ITALIC:274if (preferredWidth(font) && closerWeight(italic, font, style)) {275italic = font;276}277break;278279case Font.BOLD|Font.ITALIC:280if (preferredWidth(font) && closerWeight(bolditalic, font, style)) {281bolditalic = font;282}283break;284285default:286break;287}288}289290public Font2D getFontWithExactStyleMatch(int style) {291292switch (style) {293294case Font.PLAIN:295return plain;296297case Font.BOLD:298return bold;299300case Font.ITALIC:301return italic;302303case Font.BOLD|Font.ITALIC:304return bolditalic;305306default:307return null;308}309}310311/* REMIND: if the callers of this method are operating in an312* environment in which not all fonts are registered, the returned313* font may be a algorithmically styled one, where in fact if loadfonts314* were executed, a styled font may be located. Our present "solution"315* to this is to register all fonts in a directory and assume that this316* registered all the styles of a font, since they would all be in the317* same location.318*/319public Font2D getFont(int style) {320321switch (style) {322323case Font.PLAIN:324return plain;325326case Font.BOLD:327if (bold != null) {328return bold;329} else if (plain != null && plain.canDoStyle(style)) {330return plain;331} else {332return null;333}334335case Font.ITALIC:336if (italic != null) {337return italic;338} else if (plain != null && plain.canDoStyle(style)) {339return plain;340} else {341return null;342}343344case Font.BOLD|Font.ITALIC:345if (bolditalic != null) {346return bolditalic;347} else if (bold != null && bold.canDoStyle(style)) {348return bold;349} else if (italic != null && italic.canDoStyle(style)) {350return italic;351} else if (plain != null && plain.canDoStyle(style)) {352return plain;353} else {354return null;355}356default:357return null;358}359}360361/* Only to be called if getFont(style) returns null362* This method will only return null if the family is completely empty!363* Note that it assumes the font of the style you need isn't in the364* family. The logic here is that if we must substitute something365* it might as well be from the same family.366*/367Font2D getClosestStyle(int style) {368369switch (style) {370/* if you ask for a plain font try to return a non-italic one,371* then a italic one, finally a bold italic one */372case Font.PLAIN:373if (bold != null) {374return bold;375} else if (italic != null) {376return italic;377} else {378return bolditalic;379}380381/* if you ask for a bold font try to return a non-italic one,382* then a bold italic one, finally an italic one */383case Font.BOLD:384if (plain != null) {385return plain;386} else if (bolditalic != null) {387return bolditalic;388} else {389return italic;390}391392/* if you ask for a italic font try to return a bold italic one,393* then a plain one, finally an bold one */394case Font.ITALIC:395if (bolditalic != null) {396return bolditalic;397} else if (plain != null) {398return plain;399} else {400return bold;401}402403case Font.BOLD|Font.ITALIC:404if (italic != null) {405return italic;406} else if (bold != null) {407return bold;408} else {409return plain;410}411}412return null;413}414415/* Font may have localized names. Store these in a separate map, so416* that only clients who use these names need be affected.417*/418static synchronized void addLocaleNames(FontFamily family, String[] names){419if (allLocaleNames == null) {420allLocaleNames = new HashMap<String, FontFamily>();421}422for (int i=0; i<names.length; i++) {423allLocaleNames.put(names[i].toLowerCase(), family);424}425}426427public static synchronized FontFamily getLocaleFamily(String name) {428if (allLocaleNames == null) {429return null;430}431return allLocaleNames.get(name.toLowerCase());432}433434public static FontFamily[] getAllFontFamilies() {435Collection<FontFamily> families = familyNameMap.values();436return families.toArray(new FontFamily[0]);437}438439public String toString() {440return441"Font family: " + familyName +442" plain="+plain+443" bold=" + bold +444" italic=" + italic +445" bolditalic=" + bolditalic;446447}448449}450451452