Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/font/FontUtilities.java
38829 views
/*1* Copyright (c) 2008, 2013, 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.awt.Font;28import java.io.BufferedReader;29import java.io.File;30import java.io.FileInputStream;31import java.io.InputStreamReader;32import java.lang.ref.SoftReference;33import java.util.concurrent.ConcurrentHashMap;34import java.security.AccessController;3536import java.security.PrivilegedAction;37import javax.swing.plaf.FontUIResource;3839import sun.util.logging.PlatformLogger;4041/**42* A collection of utility methods.43*/44public final class FontUtilities {4546public static boolean isSolaris;4748public static boolean isLinux;4950public static boolean isMacOSX;5152public static boolean isSolaris8;5354public static boolean isSolaris9;5556public static boolean isOpenSolaris;5758public static boolean useT2K;5960public static boolean isWindows;6162public static boolean isOpenJDK;6364static final String LUCIDA_FILE_NAME = "LucidaSansRegular.ttf";6566private static boolean debugFonts = false;67private static PlatformLogger logger = null;68private static boolean logging;6970// This static initializer block figures out the OS constants.71static {7273AccessController.doPrivileged(new PrivilegedAction () {74public Object run() {75String osName = System.getProperty("os.name", "unknownOS");76isSolaris = osName.startsWith("SunOS");7778isLinux = osName.startsWith("Linux");7980isMacOSX = osName.contains("OS X"); // TODO: MacOSX8182String t2kStr = System.getProperty("sun.java2d.font.scaler");83if (t2kStr != null) {84useT2K = "t2k".equals(t2kStr);85} else {86useT2K = false;87}88if (isSolaris) {89String version = System.getProperty("os.version", "0.0");90isSolaris8 = version.startsWith("5.8");91isSolaris9 = version.startsWith("5.9");92float ver = Float.parseFloat(version);93if (ver > 5.10f) {94File f = new File("/etc/release");95String line = null;96try {97FileInputStream fis = new FileInputStream(f);98InputStreamReader isr = new InputStreamReader(99fis, "ISO-8859-1");100BufferedReader br = new BufferedReader(isr);101line = br.readLine();102fis.close();103} catch (Exception ex) {104// Nothing to do here.105}106if (line != null && line.indexOf("OpenSolaris") >= 0) {107isOpenSolaris = true;108} else {109isOpenSolaris = false;110}111} else {112isOpenSolaris = false;113}114} else {115isSolaris8 = false;116isSolaris9 = false;117isOpenSolaris = false;118}119isWindows = osName.startsWith("Windows");120String jreLibDirName = System.getProperty("java.home", "")121+ File.separator + "lib";122String jreFontDirName =123jreLibDirName + File.separator + "fonts";124File lucidaFile = new File(jreFontDirName + File.separator125+ LUCIDA_FILE_NAME);126isOpenJDK = !lucidaFile.exists();127128String debugLevel =129System.getProperty("sun.java2d.debugfonts");130131if (debugLevel != null && !debugLevel.equals("false")) {132debugFonts = true;133logger = PlatformLogger.getLogger("sun.java2d");134if (debugLevel.equals("warning")) {135logger.setLevel(PlatformLogger.Level.WARNING);136} else if (debugLevel.equals("severe")) {137logger.setLevel(PlatformLogger.Level.SEVERE);138}139}140141if (debugFonts) {142logger = PlatformLogger.getLogger("sun.java2d");143logging = logger.isEnabled();144}145146return null;147}148});149}150151/**152* Referenced by code in the JDK which wants to test for the153* minimum char code for which layout may be required.154* Note that even basic latin text can benefit from ligatures,155* eg "ffi" but we presently apply those only if explicitly156* requested with TextAttribute.LIGATURES_ON.157* The value here indicates the lowest char code for which failing158* to invoke layout would prevent acceptable rendering.159*/160public static final int MIN_LAYOUT_CHARCODE = 0x0300;161162/**163* Referenced by code in the JDK which wants to test for the164* maximum char code for which layout may be required.165* Note this does not account for supplementary characters166* where the caller interprets 'layout' to mean any case where167* one 'char' (ie the java type char) does not map to one glyph168*/169public static final int MAX_LAYOUT_CHARCODE = 0x206F;170171/**172* Calls the private getFont2D() method in java.awt.Font objects.173*174* @param font the font object to call175*176* @return the Font2D object returned by Font.getFont2D()177*/178public static Font2D getFont2D(Font font) {179return FontAccess.getFontAccess().getFont2D(font);180}181182/**183* If there is anything in the text which triggers a case184* where char->glyph does not map 1:1 in straightforward185* left->right ordering, then this method returns true.186* Scripts which might require it but are not treated as such187* due to JDK implementations will not return true.188* ie a 'true' return is an indication of the treatment by189* the implementation.190* Whether supplementary characters should be considered is dependent191* on the needs of the caller. Since this method accepts the 'char' type192* then such chars are always represented by a pair. From a rendering193* perspective these will all (in the cases I know of) still be one194* unicode character -> one glyph. But if a caller is using this to195* discover any case where it cannot make naive assumptions about196* the number of chars, and how to index through them, then it may197* need the option to have a 'true' return in such a case.198*/199public static boolean isComplexText(char [] chs, int start, int limit) {200201for (int i = start; i < limit; i++) {202if (chs[i] < MIN_LAYOUT_CHARCODE) {203continue;204}205else if (isNonSimpleChar(chs[i])) {206return true;207}208}209return false;210}211212/* This is almost the same as the method above, except it takes a213* char which means it may include undecoded surrogate pairs.214* The distinction is made so that code which needs to identify all215* cases in which we do not have a simple mapping from216* char->unicode character->glyph can be be identified.217* For example measurement cannot simply sum advances of 'chars',218* the caret in editable text cannot advance one 'char' at a time, etc.219* These callers really are asking for more than whether 'layout'220* needs to be run, they need to know if they can assume 1->1221* char->glyph mapping.222*/223public static boolean isNonSimpleChar(char ch) {224return225isComplexCharCode(ch) ||226(ch >= CharToGlyphMapper.HI_SURROGATE_START &&227ch <= CharToGlyphMapper.LO_SURROGATE_END);228}229230/* If the character code falls into any of a number of unicode ranges231* where we know that simple left->right layout mapping chars to glyphs232* 1:1 and accumulating advances is going to produce incorrect results,233* we want to know this so the caller can use a more intelligent layout234* approach. A caller who cares about optimum performance may want to235* check the first case and skip the method call if its in that range.236* Although there's a lot of tests in here, knowing you can skip237* CTL saves a great deal more. The rest of the checks are ordered238* so that rather than checking explicitly if (>= start & <= end)239* which would mean all ranges would need to be checked so be sure240* CTL is not needed, the method returns as soon as it recognises241* the code point is outside of a CTL ranges.242* NOTE: Since this method accepts an 'int' it is asssumed to properly243* represent a CHARACTER. ie it assumes the caller has already244* converted surrogate pairs into supplementary characters, and so245* can handle this case and doesn't need to be told such a case is246* 'complex'.247*/248public static boolean isComplexCharCode(int code) {249250if (code < MIN_LAYOUT_CHARCODE || code > MAX_LAYOUT_CHARCODE) {251return false;252}253else if (code <= 0x036f) {254// Trigger layout for combining diacriticals 0x0300->0x036f255return true;256}257else if (code < 0x0590) {258// No automatic layout for Greek, Cyrillic, Armenian.259return false;260}261else if (code <= 0x06ff) {262// Hebrew 0590 - 05ff263// Arabic 0600 - 06ff264return true;265}266else if (code < 0x0900) {267return false; // Syriac and Thaana268}269else if (code <= 0x0e7f) {270// if Indic, assume shaping for conjuncts, reordering:271// 0900 - 097F Devanagari272// 0980 - 09FF Bengali273// 0A00 - 0A7F Gurmukhi274// 0A80 - 0AFF Gujarati275// 0B00 - 0B7F Oriya276// 0B80 - 0BFF Tamil277// 0C00 - 0C7F Telugu278// 0C80 - 0CFF Kannada279// 0D00 - 0D7F Malayalam280// 0D80 - 0DFF Sinhala281// 0E00 - 0E7F if Thai, assume shaping for vowel, tone marks282return true;283}284else if (code < 0x0f00) {285return false;286}287else if (code <= 0x0fff) { // U+0F00 - U+0FFF Tibetan288return true;289}290else if (code < 0x1100) {291return false;292}293else if (code < 0x11ff) { // U+1100 - U+11FF Old Hangul294return true;295}296else if (code < 0x1780) {297return false;298}299else if (code <= 0x17ff) { // 1780 - 17FF Khmer300return true;301}302else if (code < 0x200c) {303return false;304}305else if (code <= 0x200d) { // zwj or zwnj306return true;307}308else if (code >= 0x202a && code <= 0x202e) { // directional control309return true;310}311else if (code >= 0x206a && code <= 0x206f) { // directional control312return true;313}314return false;315}316317public static PlatformLogger getLogger() {318return logger;319}320321public static boolean isLogging() {322return logging;323}324325public static boolean debugFonts() {326return debugFonts;327}328329330// The following methods are used by Swing.331332/* Revise the implementation to in fact mean "font is a composite font.333* This ensures that Swing components will always benefit from the334* fall back fonts335*/336public static boolean fontSupportsDefaultEncoding(Font font) {337return getFont2D(font) instanceof CompositeFont;338}339340/**341* This method is provided for internal and exclusive use by Swing.342*343* It may be used in conjunction with fontSupportsDefaultEncoding(Font)344* In the event that a desktop properties font doesn't directly345* support the default encoding, (ie because the host OS supports346* adding support for the current locale automatically for native apps),347* then Swing calls this method to get a font which uses the specified348* font for the code points it covers, but also supports this locale349* just as the standard composite fonts do.350* Note: this will over-ride any setting where an application351* specifies it prefers locale specific composite fonts.352* The logic for this, is that this method is used only where the user or353* application has specified that the native L&F be used, and that354* we should honour that request to use the same font as native apps use.355*356* The behaviour of this method is to construct a new composite357* Font object that uses the specified physical font as its first358* component, and adds all the components of "dialog" as fall back359* components.360* The method currently assumes that only the size and style attributes361* are set on the specified font. It doesn't copy the font transform or362* other attributes because they aren't set on a font created from363* the desktop. This will need to be fixed if use is broadened.364*365* Operations such as Font.deriveFont will work properly on the366* font returned by this method for deriving a different point size.367* Additionally it tries to support a different style by calling368* getNewComposite() below. That also supports replacing slot zero369* with a different physical font but that is expected to be "rare".370* Deriving with a different style is needed because its been shown371* that some applications try to do this for Swing FontUIResources.372* Also operations such as new Font(font.getFontName(..), Font.PLAIN, 14);373* will NOT yield the same result, as the new underlying CompositeFont374* cannot be "looked up" in the font registry.375* This returns a FontUIResource as that is the Font sub-class needed376* by Swing.377* Suggested usage is something like :378* FontUIResource fuir;379* Font desktopFont = getDesktopFont(..);380* // NOTE even if fontSupportsDefaultEncoding returns true because381* // you get Tahoma and are running in an English locale, you may382* // still want to just call getCompositeFontUIResource() anyway383* // as only then will you get fallback fonts - eg for CJK.384* if (FontManager.fontSupportsDefaultEncoding(desktopFont)) {385* fuir = new FontUIResource(..);386* } else {387* fuir = FontManager.getCompositeFontUIResource(desktopFont);388* }389* return fuir;390*/391private static volatile392SoftReference<ConcurrentHashMap<PhysicalFont, CompositeFont>>393compMapRef = new SoftReference(null);394395public static FontUIResource getCompositeFontUIResource(Font font) {396397FontUIResource fuir = new FontUIResource(font);398Font2D font2D = FontUtilities.getFont2D(font);399400if (!(font2D instanceof PhysicalFont)) {401/* Swing should only be calling this when a font is obtained402* from desktop properties, so should generally be a physical font,403* an exception might be for names like "MS Serif" which are404* automatically mapped to "Serif", so there's no need to do405* anything special in that case. But note that suggested usage406* is first to call fontSupportsDefaultEncoding(Font) and this407* method should not be called if that were to return true.408*/409return fuir;410}411412FontManager fm = FontManagerFactory.getInstance();413Font2D dialog = fm.findFont2D("dialog", font.getStyle(), FontManager.NO_FALLBACK);414// Should never be null, but MACOSX fonts are not CompositeFonts415if (dialog == null || !(dialog instanceof CompositeFont)) {416return fuir;417}418CompositeFont dialog2D = (CompositeFont)dialog;419PhysicalFont physicalFont = (PhysicalFont)font2D;420ConcurrentHashMap<PhysicalFont, CompositeFont> compMap = compMapRef.get();421if (compMap == null) { // Its been collected.422compMap = new ConcurrentHashMap<PhysicalFont, CompositeFont>();423compMapRef = new SoftReference(compMap);424}425CompositeFont compFont = compMap.get(physicalFont);426if (compFont == null) {427compFont = new CompositeFont(physicalFont, dialog2D);428compMap.put(physicalFont, compFont);429}430FontAccess.getFontAccess().setFont2D(fuir, compFont.handle);431/* marking this as a created font is needed as only created fonts432* copy their creator's handles.433*/434FontAccess.getFontAccess().setCreatedFont(fuir);435return fuir;436}437438/* A small "map" from GTK/fontconfig names to the equivalent JDK439* logical font name.440*/441private static final String[][] nameMap = {442{"sans", "sansserif"},443{"sans-serif", "sansserif"},444{"serif", "serif"},445{"monospace", "monospaced"}446};447448public static String mapFcName(String name) {449for (int i = 0; i < nameMap.length; i++) {450if (name.equals(nameMap[i][0])) {451return nameMap[i][1];452}453}454return null;455}456457458/* This is called by Swing passing in a fontconfig family name459* such as "sans". In return Swing gets a FontUIResource instance460* that has queried fontconfig to resolve the font(s) used for this.461* Fontconfig will if asked return a list of fonts to give the largest462* possible code point coverage.463* For now we use only the first font returned by fontconfig, and464* back it up with the most closely matching JDK logical font.465* Essentially this means pre-pending what we return now with fontconfig's466* preferred physical font. This could lead to some duplication in cases,467* if we already included that font later. We probably should remove such468* duplicates, but it is not a significant problem. It can be addressed469* later as part of creating a Composite which uses more of the470* same fonts as fontconfig. At that time we also should pay more471* attention to the special rendering instructions fontconfig returns,472* such as whether we should prefer embedded bitmaps over antialiasing.473* There's no way to express that via a Font at present.474*/475public static FontUIResource getFontConfigFUIR(String fcFamily,476int style, int size) {477478String mapped = mapFcName(fcFamily);479if (mapped == null) {480mapped = "sansserif";481}482483FontUIResource fuir;484FontManager fm = FontManagerFactory.getInstance();485if (fm instanceof SunFontManager) {486SunFontManager sfm = (SunFontManager) fm;487fuir = sfm.getFontConfigFUIR(mapped, style, size);488} else {489fuir = new FontUIResource(mapped, style, size);490}491return fuir;492}493494495/**496* Used by windows printing to assess if a font is likely to497* be layout compatible with JDK498* TrueType fonts should be, but if they have no GPOS table,499* but do have a GSUB table, then they are probably older500* fonts GDI handles differently.501*/502public static boolean textLayoutIsCompatible(Font font) {503504Font2D font2D = getFont2D(font);505if (font2D instanceof TrueTypeFont) {506TrueTypeFont ttf = (TrueTypeFont) font2D;507return508ttf.getDirectoryEntry(TrueTypeFont.GSUBTag) == null ||509ttf.getDirectoryEntry(TrueTypeFont.GPOSTag) != null;510} else {511return false;512}513}514515}516517518