Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
84c7e7ae
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
84c7e7ae
编写于
8月 07, 2009
作者:
R
rkennke
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
6869705: Missing files of CR6795908, FontManager refactoring
Reviewed-by: prr, igor
上级
e43dcf4d
变更
8
显示空白变更内容
内联
并排
Showing
8 changed file
with
5956 addition
and
0 deletion
+5956
-0
src/share/classes/sun/font/FontAccess.java
src/share/classes/sun/font/FontAccess.java
+48
-0
src/share/classes/sun/font/FontManagerFactory.java
src/share/classes/sun/font/FontManagerFactory.java
+106
-0
src/share/classes/sun/font/FontManagerForSGE.java
src/share/classes/sun/font/FontManagerForSGE.java
+57
-0
src/share/classes/sun/font/FontUtilities.java
src/share/classes/sun/font/FontUtilities.java
+486
-0
src/share/classes/sun/font/SunFontManager.java
src/share/classes/sun/font/SunFontManager.java
+3675
-0
src/solaris/classes/sun/awt/X11FontManager.java
src/solaris/classes/sun/awt/X11FontManager.java
+850
-0
src/solaris/classes/sun/font/FontConfigManager.java
src/solaris/classes/sun/font/FontConfigManager.java
+454
-0
src/windows/classes/sun/awt/Win32FontManager.java
src/windows/classes/sun/awt/Win32FontManager.java
+280
-0
未找到文件。
src/share/classes/sun/font/FontAccess.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.font
;
import
java.awt.Font
;
public
abstract
class
FontAccess
{
private
static
FontAccess
access
;
public
static
synchronized
void
setFontAccess
(
FontAccess
acc
)
{
if
(
access
!=
null
)
{
throw
new
InternalError
(
"Attempt to set FontAccessor twice"
);
}
access
=
acc
;
}
public
static
synchronized
FontAccess
getFontAccess
()
{
return
access
;
}
public
abstract
Font2D
getFont2D
(
Font
f
);
public
abstract
void
setFont2D
(
Font
f
,
Font2DHandle
h
);
public
abstract
void
setCreatedFont
(
Font
f
);
public
abstract
boolean
isCreatedFont
(
Font
f
);
}
src/share/classes/sun/font/FontManagerFactory.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.font
;
import
java.awt.AWTError
;
import
java.awt.Font
;
import
java.awt.GraphicsEnvironment
;
import
java.awt.Toolkit
;
import
java.security.AccessController
;
import
java.security.PrivilegedAction
;
import
sun.security.action.GetPropertyAction
;
/**
* Factory class used to retrieve a valid FontManager instance for the current
* platform.
*
* A default implementation is given for Linux, Solaris and Windows.
* You can alter the behaviour of the {@link #getInstance()} method by setting
* the {@code sun.font.fontmanager} property. For example:
* {@code sun.font.fontmanager=sun.awt.X11FontManager}
*/
public
final
class
FontManagerFactory
{
/** Our singleton instance. */
private
static
FontManager
instance
=
null
;
private
static
final
String
DEFAULT_CLASS
;
static
{
if
(
FontUtilities
.
isWindows
)
DEFAULT_CLASS
=
"sun.awt.Win32FontManager"
;
else
DEFAULT_CLASS
=
"sun.awt.X11FontManager"
;
}
/**
* Get a valid FontManager implementation for the current platform.
*
* @return a valid FontManager instance for the current platform
*/
public
static
synchronized
FontManager
getInstance
()
{
if
(
instance
!=
null
)
{
return
instance
;
}
String
fmClassName
=
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"sun.font.fontmanager"
,
DEFAULT_CLASS
));
try
{
@SuppressWarnings
(
"unchecked"
)
ClassLoader
cl
=
(
ClassLoader
)
AccessController
.
doPrivileged
(
new
PrivilegedAction
()
{
public
Object
run
()
{
return
ClassLoader
.
getSystemClassLoader
();
}
});
@SuppressWarnings
(
"unchecked"
)
Class
fmClass
=
Class
.
forName
(
fmClassName
,
true
,
cl
);
instance
=
(
FontManager
)
fmClass
.
newInstance
();
}
catch
(
ClassNotFoundException
ex
)
{
InternalError
err
=
new
InternalError
();
err
.
initCause
(
ex
);
throw
err
;
}
catch
(
InstantiationException
ex
)
{
InternalError
err
=
new
InternalError
();
err
.
initCause
(
ex
);
throw
err
;
}
catch
(
IllegalAccessException
ex
)
{
InternalError
err
=
new
InternalError
();
err
.
initCause
(
ex
);
throw
err
;
}
return
instance
;
}
}
src/share/classes/sun/font/FontManagerForSGE.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.font
;
import
java.awt.Font
;
import
java.util.Locale
;
import
java.util.TreeMap
;
/**
* This is an extension of the {@link FontManager} interface which has to
* be implemented on systems that want to use SunGraphicsEnvironment. It
* adds a couple of methods that are only required by SGE. Graphics
* implementations that use their own GraphicsEnvironment are not required
* to implement this and can use plain FontManager instead.
*/
public
interface
FontManagerForSGE
extends
FontManager
{
/**
* Return an array of created Fonts, or null, if no fonts were created yet.
*/
public
Font
[]
getCreatedFonts
();
/**
* Similar to getCreatedFonts, but returns a TreeMap of fonts by family name.
*/
public
TreeMap
<
String
,
String
>
getCreatedFontFamilyNames
();
/**
* Returns all fonts installed in this environment.
*/
public
Font
[]
getAllInstalledFonts
();
public
String
[]
getInstalledFontFamilyNames
(
Locale
requestedLocale
);
}
src/share/classes/sun/font/FontUtilities.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.font
;
import
java.awt.Font
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.InputStreamReader
;
import
java.security.AccessController
;
import
java.util.logging.Level
;
import
java.util.logging.Logger
;
import
javax.swing.plaf.FontUIResource
;
import
sun.security.action.GetPropertyAction
;
/**
* A collection of utility methods.
*/
public
final
class
FontUtilities
{
public
static
final
boolean
isSolaris
;
public
static
final
boolean
isLinux
;
public
static
final
boolean
isSolaris8
;
public
static
final
boolean
isSolaris9
;
public
static
final
boolean
isOpenSolaris
;
public
static
final
boolean
useT2K
;
public
static
final
boolean
isWindows
;
public
static
final
boolean
isOpenJDK
;
static
final
String
LUCIDA_FILE_NAME
=
"LucidaSansRegular.ttf"
;
// This static initializer block figures out the OS constants.
static
{
String
osName
=
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"os.name"
,
"unknownOS"
));
isSolaris
=
osName
.
startsWith
(
"SunOS"
);
isLinux
=
osName
.
startsWith
(
"Linux"
);
String
t2kStr
=
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"sun.java2d.font.scaler"
));
if
(
t2kStr
!=
null
)
{
useT2K
=
"t2k"
.
equals
(
t2kStr
);
}
else
{
useT2K
=
false
;
}
if
(
isSolaris
)
{
String
version
=
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"os.version"
,
"0.0"
));
isSolaris8
=
version
.
startsWith
(
"5.8"
);
isSolaris9
=
version
.
startsWith
(
"5.9"
);
float
ver
=
Float
.
parseFloat
(
version
);
if
(
ver
>
5.10f
)
{
File
f
=
new
File
(
"/etc/release"
);
String
line
=
null
;
try
{
FileInputStream
fis
=
new
FileInputStream
(
f
);
InputStreamReader
isr
=
new
InputStreamReader
(
fis
,
"ISO-8859-1"
);
BufferedReader
br
=
new
BufferedReader
(
isr
);
line
=
br
.
readLine
();
fis
.
close
();
}
catch
(
Exception
ex
)
{
// Nothing to do here.
}
if
(
line
!=
null
&&
line
.
indexOf
(
"OpenSolaris"
)
>=
0
)
{
isOpenSolaris
=
true
;
}
else
{
isOpenSolaris
=
false
;
}
}
else
{
isOpenSolaris
=
false
;
}
}
else
{
isSolaris8
=
false
;
isSolaris9
=
false
;
isOpenSolaris
=
false
;
}
isWindows
=
osName
.
startsWith
(
"Windows"
);
String
jreLibDirName
=
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"java.home"
,
""
))
+
File
.
separator
+
"lib"
;
String
jreFontDirName
=
jreLibDirName
+
File
.
separator
+
"fonts"
;
File
lucidaFile
=
new
File
(
jreFontDirName
+
File
.
separator
+
LUCIDA_FILE_NAME
);
isOpenJDK
=
!
lucidaFile
.
exists
();
}
/**
* Referenced by code in the JDK which wants to test for the
* minimum char code for which layout may be required.
* Note that even basic latin text can benefit from ligatures,
* eg "ffi" but we presently apply those only if explicitly
* requested with TextAttribute.LIGATURES_ON.
* The value here indicates the lowest char code for which failing
* to invoke layout would prevent acceptable rendering.
*/
public
static
final
int
MIN_LAYOUT_CHARCODE
=
0x0300
;
/**
* Referenced by code in the JDK which wants to test for the
* maximum char code for which layout may be required.
* Note this does not account for supplementary characters
* where the caller interprets 'layout' to mean any case where
* one 'char' (ie the java type char) does not map to one glyph
*/
public
static
final
int
MAX_LAYOUT_CHARCODE
=
0x206F
;
private
static
boolean
debugFonts
=
false
;
private
static
Logger
logger
=
null
;
private
static
boolean
logging
;
static
{
String
debugLevel
=
System
.
getProperty
(
"sun.java2d.debugfonts"
);
if
(
debugLevel
!=
null
&&
!
debugLevel
.
equals
(
"false"
))
{
debugFonts
=
true
;
logger
=
Logger
.
getLogger
(
"sun.java2d"
);
if
(
debugLevel
.
equals
(
"warning"
))
{
logger
.
setLevel
(
Level
.
WARNING
);
}
else
if
(
debugLevel
.
equals
(
"severe"
))
{
logger
.
setLevel
(
Level
.
SEVERE
);
}
}
if
(
debugFonts
)
{
logger
=
Logger
.
getLogger
(
"sun.java2d"
,
null
);
logging
=
logger
.
getLevel
()
!=
Level
.
OFF
;
}
}
/**
* Calls the private getFont2D() method in java.awt.Font objects.
*
* @param font the font object to call
*
* @return the Font2D object returned by Font.getFont2D()
*/
public
static
Font2D
getFont2D
(
Font
font
)
{
return
FontAccess
.
getFontAccess
().
getFont2D
(
font
);
}
/**
* If there is anything in the text which triggers a case
* where char->glyph does not map 1:1 in straightforward
* left->right ordering, then this method returns true.
* Scripts which might require it but are not treated as such
* due to JDK implementations will not return true.
* ie a 'true' return is an indication of the treatment by
* the implementation.
* Whether supplementary characters should be considered is dependent
* on the needs of the caller. Since this method accepts the 'char' type
* then such chars are always represented by a pair. From a rendering
* perspective these will all (in the cases I know of) still be one
* unicode character -> one glyph. But if a caller is using this to
* discover any case where it cannot make naive assumptions about
* the number of chars, and how to index through them, then it may
* need the option to have a 'true' return in such a case.
*/
public
static
boolean
isComplexText
(
char
[]
chs
,
int
start
,
int
limit
)
{
for
(
int
i
=
start
;
i
<
limit
;
i
++)
{
if
(
chs
[
i
]
<
MIN_LAYOUT_CHARCODE
)
{
continue
;
}
else
if
(
isNonSimpleChar
(
chs
[
i
]))
{
return
true
;
}
}
return
false
;
}
/* This is almost the same as the method above, except it takes a
* char which means it may include undecoded surrogate pairs.
* The distinction is made so that code which needs to identify all
* cases in which we do not have a simple mapping from
* char->unicode character->glyph can be be identified.
* For example measurement cannot simply sum advances of 'chars',
* the caret in editable text cannot advance one 'char' at a time, etc.
* These callers really are asking for more than whether 'layout'
* needs to be run, they need to know if they can assume 1->1
* char->glyph mapping.
*/
public
static
boolean
isNonSimpleChar
(
char
ch
)
{
return
isComplexCharCode
(
ch
)
||
(
ch
>=
CharToGlyphMapper
.
HI_SURROGATE_START
&&
ch
<=
CharToGlyphMapper
.
LO_SURROGATE_END
);
}
/* If the character code falls into any of a number of unicode ranges
* where we know that simple left->right layout mapping chars to glyphs
* 1:1 and accumulating advances is going to produce incorrect results,
* we want to know this so the caller can use a more intelligent layout
* approach. A caller who cares about optimum performance may want to
* check the first case and skip the method call if its in that range.
* Although there's a lot of tests in here, knowing you can skip
* CTL saves a great deal more. The rest of the checks are ordered
* so that rather than checking explicitly if (>= start & <= end)
* which would mean all ranges would need to be checked so be sure
* CTL is not needed, the method returns as soon as it recognises
* the code point is outside of a CTL ranges.
* NOTE: Since this method accepts an 'int' it is asssumed to properly
* represent a CHARACTER. ie it assumes the caller has already
* converted surrogate pairs into supplementary characters, and so
* can handle this case and doesn't need to be told such a case is
* 'complex'.
*/
public
static
boolean
isComplexCharCode
(
int
code
)
{
if
(
code
<
MIN_LAYOUT_CHARCODE
||
code
>
MAX_LAYOUT_CHARCODE
)
{
return
false
;
}
else
if
(
code
<=
0x036f
)
{
// Trigger layout for combining diacriticals 0x0300->0x036f
return
true
;
}
else
if
(
code
<
0x0590
)
{
// No automatic layout for Greek, Cyrillic, Armenian.
return
false
;
}
else
if
(
code
<=
0x06ff
)
{
// Hebrew 0590 - 05ff
// Arabic 0600 - 06ff
return
true
;
}
else
if
(
code
<
0x0900
)
{
return
false
;
// Syriac and Thaana
}
else
if
(
code
<=
0x0e7f
)
{
// if Indic, assume shaping for conjuncts, reordering:
// 0900 - 097F Devanagari
// 0980 - 09FF Bengali
// 0A00 - 0A7F Gurmukhi
// 0A80 - 0AFF Gujarati
// 0B00 - 0B7F Oriya
// 0B80 - 0BFF Tamil
// 0C00 - 0C7F Telugu
// 0C80 - 0CFF Kannada
// 0D00 - 0D7F Malayalam
// 0D80 - 0DFF Sinhala
// 0E00 - 0E7F if Thai, assume shaping for vowel, tone marks
return
true
;
}
else
if
(
code
<
0x1780
)
{
return
false
;
}
else
if
(
code
<=
0x17ff
)
{
// 1780 - 17FF Khmer
return
true
;
}
else
if
(
code
<
0x200c
)
{
return
false
;
}
else
if
(
code
<=
0x200d
)
{
// zwj or zwnj
return
true
;
}
else
if
(
code
>=
0x202a
&&
code
<=
0x202e
)
{
// directional control
return
true
;
}
else
if
(
code
>=
0x206a
&&
code
<=
0x206f
)
{
// directional control
return
true
;
}
return
false
;
}
public
static
Logger
getLogger
()
{
return
logger
;
}
public
static
boolean
isLogging
()
{
return
logging
;
}
public
static
boolean
debugFonts
()
{
return
debugFonts
;
}
// The following methods are used by Swing.
/* Revise the implementation to in fact mean "font is a composite font.
* This ensures that Swing components will always benefit from the
* fall back fonts
*/
public
static
boolean
fontSupportsDefaultEncoding
(
Font
font
)
{
return
getFont2D
(
font
)
instanceof
CompositeFont
;
}
/**
* This method is provided for internal and exclusive use by Swing.
*
* It may be used in conjunction with fontSupportsDefaultEncoding(Font)
* In the event that a desktop properties font doesn't directly
* support the default encoding, (ie because the host OS supports
* adding support for the current locale automatically for native apps),
* then Swing calls this method to get a font which uses the specified
* font for the code points it covers, but also supports this locale
* just as the standard composite fonts do.
* Note: this will over-ride any setting where an application
* specifies it prefers locale specific composite fonts.
* The logic for this, is that this method is used only where the user or
* application has specified that the native L&F be used, and that
* we should honour that request to use the same font as native apps use.
*
* The behaviour of this method is to construct a new composite
* Font object that uses the specified physical font as its first
* component, and adds all the components of "dialog" as fall back
* components.
* The method currently assumes that only the size and style attributes
* are set on the specified font. It doesn't copy the font transform or
* other attributes because they aren't set on a font created from
* the desktop. This will need to be fixed if use is broadened.
*
* Operations such as Font.deriveFont will work properly on the
* font returned by this method for deriving a different point size.
* Additionally it tries to support a different style by calling
* getNewComposite() below. That also supports replacing slot zero
* with a different physical font but that is expected to be "rare".
* Deriving with a different style is needed because its been shown
* that some applications try to do this for Swing FontUIResources.
* Also operations such as new Font(font.getFontName(..), Font.PLAIN, 14);
* will NOT yield the same result, as the new underlying CompositeFont
* cannot be "looked up" in the font registry.
* This returns a FontUIResource as that is the Font sub-class needed
* by Swing.
* Suggested usage is something like :
* FontUIResource fuir;
* Font desktopFont = getDesktopFont(..);
* // NOTE even if fontSupportsDefaultEncoding returns true because
* // you get Tahoma and are running in an English locale, you may
* // still want to just call getCompositeFontUIResource() anyway
* // as only then will you get fallback fonts - eg for CJK.
* if (FontManager.fontSupportsDefaultEncoding(desktopFont)) {
* fuir = new FontUIResource(..);
* } else {
* fuir = FontManager.getCompositeFontUIResource(desktopFont);
* }
* return fuir;
*/
public
static
FontUIResource
getCompositeFontUIResource
(
Font
font
)
{
FontUIResource
fuir
=
new
FontUIResource
(
font
.
getName
(),
font
.
getStyle
(),
font
.
getSize
());
Font2D
font2D
=
FontUtilities
.
getFont2D
(
font
);
if
(!(
font2D
instanceof
PhysicalFont
))
{
/* Swing should only be calling this when a font is obtained
* from desktop properties, so should generally be a physical font,
* an exception might be for names like "MS Serif" which are
* automatically mapped to "Serif", so there's no need to do
* anything special in that case. But note that suggested usage
* is first to call fontSupportsDefaultEncoding(Font) and this
* method should not be called if that were to return true.
*/
return
fuir
;
}
FontManager
fm
=
FontManagerFactory
.
getInstance
();
CompositeFont
dialog2D
=
(
CompositeFont
)
fm
.
findFont2D
(
"dialog"
,
font
.
getStyle
(),
FontManager
.
NO_FALLBACK
);
if
(
dialog2D
==
null
)
{
/* shouldn't happen */
return
fuir
;
}
PhysicalFont
physicalFont
=
(
PhysicalFont
)
font2D
;
CompositeFont
compFont
=
new
CompositeFont
(
physicalFont
,
dialog2D
);
FontAccess
.
getFontAccess
().
setFont2D
(
fuir
,
compFont
.
handle
);
/* marking this as a created font is needed as only created fonts
* copy their creator's handles.
*/
FontAccess
.
getFontAccess
().
setCreatedFont
(
fuir
);
return
fuir
;
}
/* A small "map" from GTK/fontconfig names to the equivalent JDK
* logical font name.
*/
private
static
final
String
[][]
nameMap
=
{
{
"sans"
,
"sansserif"
},
{
"sans-serif"
,
"sansserif"
},
{
"serif"
,
"serif"
},
{
"monospace"
,
"monospaced"
}
};
public
static
String
mapFcName
(
String
name
)
{
for
(
int
i
=
0
;
i
<
nameMap
.
length
;
i
++)
{
if
(
name
.
equals
(
nameMap
[
i
][
0
]))
{
return
nameMap
[
i
][
1
];
}
}
return
null
;
}
/* This is called by Swing passing in a fontconfig family name
* such as "sans". In return Swing gets a FontUIResource instance
* that has queried fontconfig to resolve the font(s) used for this.
* Fontconfig will if asked return a list of fonts to give the largest
* possible code point coverage.
* For now we use only the first font returned by fontconfig, and
* back it up with the most closely matching JDK logical font.
* Essentially this means pre-pending what we return now with fontconfig's
* preferred physical font. This could lead to some duplication in cases,
* if we already included that font later. We probably should remove such
* duplicates, but it is not a significant problem. It can be addressed
* later as part of creating a Composite which uses more of the
* same fonts as fontconfig. At that time we also should pay more
* attention to the special rendering instructions fontconfig returns,
* such as whether we should prefer embedded bitmaps over antialiasing.
* There's no way to express that via a Font at present.
*/
public
static
FontUIResource
getFontConfigFUIR
(
String
fcFamily
,
int
style
,
int
size
)
{
String
mapped
=
mapFcName
(
fcFamily
);
if
(
mapped
==
null
)
{
mapped
=
"sansserif"
;
}
FontUIResource
fuir
;
FontManager
fm
=
FontManagerFactory
.
getInstance
();
if
(
fm
instanceof
SunFontManager
)
{
SunFontManager
sfm
=
(
SunFontManager
)
fm
;
fuir
=
sfm
.
getFontConfigFUIR
(
mapped
,
style
,
size
);
}
else
{
fuir
=
new
FontUIResource
(
mapped
,
style
,
size
);
}
return
fuir
;
}
/**
* Used by windows printing to assess if a font is likely to
* be layout compatible with JDK
* TrueType fonts should be, but if they have no GPOS table,
* but do have a GSUB table, then they are probably older
* fonts GDI handles differently.
*/
public
static
boolean
textLayoutIsCompatible
(
Font
font
)
{
Font2D
font2D
=
getFont2D
(
font
);
if
(
font2D
instanceof
TrueTypeFont
)
{
TrueTypeFont
ttf
=
(
TrueTypeFont
)
font2D
;
return
ttf
.
getDirectoryEntry
(
TrueTypeFont
.
GSUBTag
)
==
null
||
ttf
.
getDirectoryEntry
(
TrueTypeFont
.
GPOSTag
)
!=
null
;
}
else
{
return
false
;
}
}
}
src/share/classes/sun/font/SunFontManager.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.font
;
import
java.awt.Font
;
import
java.awt.FontFormatException
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FilenameFilter
;
import
java.io.IOException
;
import
java.io.InputStreamReader
;
import
java.security.AccessController
;
import
java.security.PrivilegedAction
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Hashtable
;
import
java.util.Iterator
;
import
java.util.Locale
;
import
java.util.Map
;
import
java.util.NoSuchElementException
;
import
java.util.StringTokenizer
;
import
java.util.TreeMap
;
import
java.util.Vector
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.logging.Level
;
import
java.util.logging.Logger
;
import
javax.swing.plaf.FontUIResource
;
import
sun.awt.AppContext
;
import
sun.awt.FontConfiguration
;
import
sun.awt.SunToolkit
;
import
sun.java2d.FontSupport
;
/**
* The base implementation of the {@link FontManager} interface. It implements
* the platform independent, shared parts of OpenJDK's FontManager
* implementations. The platform specific parts are declared as abstract
* methods that have to be implemented by specific implementations.
*/
public
abstract
class
SunFontManager
implements
FontSupport
,
FontManagerForSGE
{
private
static
class
TTFilter
implements
FilenameFilter
{
public
boolean
accept
(
File
dir
,
String
name
)
{
/* all conveniently have the same suffix length */
int
offset
=
name
.
length
()-
4
;
if
(
offset
<=
0
)
{
/* must be at least A.ttf */
return
false
;
}
else
{
return
(
name
.
startsWith
(
".ttf"
,
offset
)
||
name
.
startsWith
(
".TTF"
,
offset
)
||
name
.
startsWith
(
".ttc"
,
offset
)
||
name
.
startsWith
(
".TTC"
,
offset
));
}
}
}
private
static
class
T1Filter
implements
FilenameFilter
{
public
boolean
accept
(
File
dir
,
String
name
)
{
if
(
noType1Font
)
{
return
false
;
}
/* all conveniently have the same suffix length */
int
offset
=
name
.
length
()-
4
;
if
(
offset
<=
0
)
{
/* must be at least A.pfa */
return
false
;
}
else
{
return
(
name
.
startsWith
(
".pfa"
,
offset
)
||
name
.
startsWith
(
".pfb"
,
offset
)
||
name
.
startsWith
(
".PFA"
,
offset
)
||
name
.
startsWith
(
".PFB"
,
offset
));
}
}
}
private
static
class
TTorT1Filter
implements
FilenameFilter
{
public
boolean
accept
(
File
dir
,
String
name
)
{
/* all conveniently have the same suffix length */
int
offset
=
name
.
length
()-
4
;
if
(
offset
<=
0
)
{
/* must be at least A.ttf or A.pfa */
return
false
;
}
else
{
boolean
isTT
=
name
.
startsWith
(
".ttf"
,
offset
)
||
name
.
startsWith
(
".TTF"
,
offset
)
||
name
.
startsWith
(
".ttc"
,
offset
)
||
name
.
startsWith
(
".TTC"
,
offset
);
if
(
isTT
)
{
return
true
;
}
else
if
(
noType1Font
)
{
return
false
;
}
else
{
return
(
name
.
startsWith
(
".pfa"
,
offset
)
||
name
.
startsWith
(
".pfb"
,
offset
)
||
name
.
startsWith
(
".PFA"
,
offset
)
||
name
.
startsWith
(
".PFB"
,
offset
));
}
}
}
}
public
static
final
int
FONTFORMAT_NONE
=
-
1
;
public
static
final
int
FONTFORMAT_TRUETYPE
=
0
;
public
static
final
int
FONTFORMAT_TYPE1
=
1
;
public
static
final
int
FONTFORMAT_T2K
=
2
;
public
static
final
int
FONTFORMAT_TTC
=
3
;
public
static
final
int
FONTFORMAT_COMPOSITE
=
4
;
public
static
final
int
FONTFORMAT_NATIVE
=
5
;
/* Pool of 20 font file channels chosen because some UTF-8 locale
* composite fonts can use up to 16 platform fonts (including the
* Lucida fall back). This should prevent channel thrashing when
* dealing with one of these fonts.
* The pool array stores the fonts, rather than directly referencing
* the channels, as the font needs to do the open/close work.
*/
private
static
final
int
CHANNELPOOLSIZE
=
20
;
private
int
lastPoolIndex
=
0
;
private
FileFont
fontFileCache
[]
=
new
FileFont
[
CHANNELPOOLSIZE
];
/* Need to implement a simple linked list scheme for fast
* traversal and lookup.
* Also want to "fast path" dialog so there's minimal overhead.
*/
/* There are at exactly 20 composite fonts: 5 faces (but some are not
* usually different), in 4 styles. The array may be auto-expanded
* later if more are needed, eg for user-defined composites or locale
* variants.
*/
private
int
maxCompFont
=
0
;
private
CompositeFont
[]
compFonts
=
new
CompositeFont
[
20
];
private
ConcurrentHashMap
<
String
,
CompositeFont
>
compositeFonts
=
new
ConcurrentHashMap
<
String
,
CompositeFont
>();
private
ConcurrentHashMap
<
String
,
PhysicalFont
>
physicalFonts
=
new
ConcurrentHashMap
<
String
,
PhysicalFont
>();
private
ConcurrentHashMap
<
String
,
PhysicalFont
>
registeredFonts
=
new
ConcurrentHashMap
<
String
,
PhysicalFont
>();
/* given a full name find the Font. Remind: there's duplication
* here in that this contains the content of compositeFonts +
* physicalFonts.
*/
private
ConcurrentHashMap
<
String
,
Font2D
>
fullNameToFont
=
new
ConcurrentHashMap
<
String
,
Font2D
>();
/* TrueType fonts have localised names. Support searching all
* of these before giving up on a name.
*/
private
HashMap
<
String
,
TrueTypeFont
>
localeFullNamesToFont
;
private
PhysicalFont
defaultPhysicalFont
;
static
boolean
longAddresses
;
private
boolean
loaded1dot0Fonts
=
false
;
boolean
loadedAllFonts
=
false
;
boolean
loadedAllFontFiles
=
false
;
HashMap
<
String
,
String
>
jreFontMap
;
HashSet
<
String
>
jreLucidaFontFiles
;
String
[]
jreOtherFontFiles
;
boolean
noOtherJREFontFiles
=
false
;
// initial assumption.
public
static
final
String
lucidaFontName
=
"Lucida Sans Regular"
;
public
static
String
jreLibDirName
;
public
static
String
jreFontDirName
;
private
static
HashSet
<
String
>
missingFontFiles
=
null
;
private
String
defaultFontName
;
private
String
defaultFontFileName
;
protected
HashSet
registeredFontFiles
=
new
HashSet
();
private
ArrayList
badFonts
;
/* fontPath is the location of all fonts on the system, excluding the
* JRE's own font directory but including any path specified using the
* sun.java2d.fontpath property. Together with that property, it is
* initialised by the getPlatformFontPath() method
* This call must be followed by a call to registerFontDirs(fontPath)
* once any extra debugging path has been appended.
*/
protected
String
fontPath
;
private
FontConfiguration
fontConfig
;
/* discoveredAllFonts is set to true when all fonts on the font path are
* discovered. This usually also implies opening, validating and
* registering, but an implementation may be optimized to avold this.
* So see also "loadedAllFontFiles"
*/
private
boolean
discoveredAllFonts
=
false
;
/* No need to keep consing up new instances - reuse a singleton.
* The trade-off is that these objects don't get GC'd.
*/
private
static
final
FilenameFilter
ttFilter
=
new
TTFilter
();
private
static
final
FilenameFilter
t1Filter
=
new
T1Filter
();
private
Font
[]
allFonts
;
private
String
[]
allFamilies
;
// cache for default locale only
private
Locale
lastDefaultLocale
;
public
static
boolean
noType1Font
;
/* Used to indicate required return type from toArray(..); */
private
static
String
[]
STR_ARRAY
=
new
String
[
0
];
/**
* Deprecated, unsupported hack - actually invokes a bug!
* Left in for a customer, don't remove.
*/
private
boolean
usePlatformFontMetrics
=
false
;
/**
* Returns the global SunFontManager instance. This is similar to
* {@link FontManagerFactory#getInstance()} but it returns a
* SunFontManager instance instead. This is only used in internal classes
* where we can safely assume that a SunFontManager is to be used.
*
* @return the global SunFontManager instance
*/
public
static
SunFontManager
getInstance
()
{
FontManager
fm
=
FontManagerFactory
.
getInstance
();
return
(
SunFontManager
)
fm
;
}
public
FilenameFilter
getTrueTypeFilter
()
{
return
ttFilter
;
}
public
FilenameFilter
getType1Filter
()
{
return
t1Filter
;
}
@Override
public
boolean
usingPerAppContextComposites
()
{
return
_usingPerAppContextComposites
;
}
private
void
initJREFontMap
()
{
/* Key is familyname+style value as an int.
* Value is filename containing the font.
* If no mapping exists, it means there is no font file for the style
* If the mapping exists but the file doesn't exist in the deferred
* list then it means its not installed.
* This looks like a lot of code and strings but if it saves even
* a single file being opened at JRE start-up there's a big payoff.
* Lucida Sans is probably the only important case as the others
* are rarely used. Consider removing the other mappings if there's
* no evidence they are useful in practice.
*/
jreFontMap
=
new
HashMap
<
String
,
String
>();
jreLucidaFontFiles
=
new
HashSet
<
String
>();
if
(
isOpenJDK
())
{
return
;
}
/* Lucida Sans Family */
jreFontMap
.
put
(
"lucida sans0"
,
"LucidaSansRegular.ttf"
);
jreFontMap
.
put
(
"lucida sans1"
,
"LucidaSansDemiBold.ttf"
);
/* Lucida Sans full names (map Bold and DemiBold to same file) */
jreFontMap
.
put
(
"lucida sans regular0"
,
"LucidaSansRegular.ttf"
);
jreFontMap
.
put
(
"lucida sans regular1"
,
"LucidaSansDemiBold.ttf"
);
jreFontMap
.
put
(
"lucida sans bold1"
,
"LucidaSansDemiBold.ttf"
);
jreFontMap
.
put
(
"lucida sans demibold1"
,
"LucidaSansDemiBold.ttf"
);
/* Lucida Sans Typewriter Family */
jreFontMap
.
put
(
"lucida sans typewriter0"
,
"LucidaTypewriterRegular.ttf"
);
jreFontMap
.
put
(
"lucida sans typewriter1"
,
"LucidaTypewriterBold.ttf"
);
/* Typewriter full names (map Bold and DemiBold to same file) */
jreFontMap
.
put
(
"lucida sans typewriter regular0"
,
"LucidaTypewriter.ttf"
);
jreFontMap
.
put
(
"lucida sans typewriter regular1"
,
"LucidaTypewriterBold.ttf"
);
jreFontMap
.
put
(
"lucida sans typewriter bold1"
,
"LucidaTypewriterBold.ttf"
);
jreFontMap
.
put
(
"lucida sans typewriter demibold1"
,
"LucidaTypewriterBold.ttf"
);
/* Lucida Bright Family */
jreFontMap
.
put
(
"lucida bright0"
,
"LucidaBrightRegular.ttf"
);
jreFontMap
.
put
(
"lucida bright1"
,
"LucidaBrightDemiBold.ttf"
);
jreFontMap
.
put
(
"lucida bright2"
,
"LucidaBrightItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright3"
,
"LucidaBrightDemiItalic.ttf"
);
/* Lucida Bright full names (map Bold and DemiBold to same file) */
jreFontMap
.
put
(
"lucida bright regular0"
,
"LucidaBrightRegular.ttf"
);
jreFontMap
.
put
(
"lucida bright regular1"
,
"LucidaBrightDemiBold.ttf"
);
jreFontMap
.
put
(
"lucida bright regular2"
,
"LucidaBrightItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright regular3"
,
"LucidaBrightDemiItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright bold1"
,
"LucidaBrightDemiBold.ttf"
);
jreFontMap
.
put
(
"lucida bright bold3"
,
"LucidaBrightDemiItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright demibold1"
,
"LucidaBrightDemiBold.ttf"
);
jreFontMap
.
put
(
"lucida bright demibold3"
,
"LucidaBrightDemiItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright italic2"
,
"LucidaBrightItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright italic3"
,
"LucidaBrightDemiItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright bold italic3"
,
"LucidaBrightDemiItalic.ttf"
);
jreFontMap
.
put
(
"lucida bright demibold italic3"
,
"LucidaBrightDemiItalic.ttf"
);
for
(
String
ffile
:
jreFontMap
.
values
())
{
jreLucidaFontFiles
.
add
(
ffile
);
}
}
static
{
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
FontManagerNativeLibrary
.
load
();
// JNI throws an exception if a class/method/field is not found,
// so there's no need to do anything explicit here.
initIDs
();
switch
(
StrikeCache
.
nativeAddressSize
)
{
case
8
:
longAddresses
=
true
;
break
;
case
4
:
longAddresses
=
false
;
break
;
default
:
throw
new
RuntimeException
(
"Unexpected address size"
);
}
noType1Font
=
"true"
.
equals
(
System
.
getProperty
(
"sun.java2d.noType1Font"
));
jreLibDirName
=
System
.
getProperty
(
"java.home"
,
""
)
+
File
.
separator
+
"lib"
;
jreFontDirName
=
jreLibDirName
+
File
.
separator
+
"fonts"
;
File
lucidaFile
=
new
File
(
jreFontDirName
+
File
.
separator
+
FontUtilities
.
LUCIDA_FILE_NAME
);
return
null
;
}
});
}
public
TrueTypeFont
getEUDCFont
()
{
// Overridden in Windows.
return
null
;
}
/* Initialise ptrs used by JNI methods */
private
static
native
void
initIDs
();
@SuppressWarnings
(
"unchecked"
)
protected
SunFontManager
()
{
initJREFontMap
();
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
File
badFontFile
=
new
File
(
jreFontDirName
+
File
.
separator
+
"badfonts.txt"
);
if
(
badFontFile
.
exists
())
{
FileInputStream
fis
=
null
;
try
{
badFonts
=
new
ArrayList
();
fis
=
new
FileInputStream
(
badFontFile
);
InputStreamReader
isr
=
new
InputStreamReader
(
fis
);
BufferedReader
br
=
new
BufferedReader
(
isr
);
while
(
true
)
{
String
name
=
br
.
readLine
();
if
(
name
==
null
)
{
break
;
}
else
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
().
warning
(
"read bad font: "
+
name
);
}
badFonts
.
add
(
name
);
}
}
}
catch
(
IOException
e
)
{
try
{
if
(
fis
!=
null
)
{
fis
.
close
();
}
}
catch
(
IOException
ioe
)
{
}
}
}
/* Here we get the fonts in jre/lib/fonts and register
* them so they are always available and preferred over
* other fonts. This needs to be registered before the
* composite fonts as otherwise some native font that
* corresponds may be found as we don't have a way to
* handle two fonts of the same name, so the JRE one
* must be the first one registered. Pass "true" to
* registerFonts method as on-screen these JRE fonts
* always go through the T2K rasteriser.
*/
if
(
FontUtilities
.
isLinux
)
{
/* Linux font configuration uses these fonts */
registerFontDir
(
jreFontDirName
);
}
registerFontsInDir
(
jreFontDirName
,
true
,
Font2D
.
JRE_RANK
,
true
,
false
);
/* Create the font configuration and get any font path
* that might be specified.
*/
fontConfig
=
createFontConfiguration
();
if
(
isOpenJDK
())
{
String
[]
fontInfo
=
getDefaultPlatformFont
();
defaultFontName
=
fontInfo
[
0
];
defaultFontFileName
=
fontInfo
[
1
];
}
String
extraFontPath
=
fontConfig
.
getExtraFontPath
();
/* In prior releases the debugging font path replaced
* all normally located font directories except for the
* JRE fonts dir. This directory is still always located
* and placed at the head of the path but as an
* augmentation to the previous behaviour the
* changes below allow you to additionally append to
* the font path by starting with append: or prepend by
* starting with a prepend: sign. Eg: to append
* -Dsun.java2d.fontpath=append:/usr/local/myfonts
* and to prepend
* -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp
*
* If there is an appendedfontpath it in the font
* configuration it is used instead of searching the
* system for dirs.
* The behaviour of append and prepend is then similar
* to the normal case. ie it goes after what
* you prepend and * before what you append. If the
* sun.java2d.fontpath property is used, but it
* neither the append or prepend syntaxes is used then
* as except for the JRE dir the path is replaced and it
* is up to you to make sure that all the right
* directories are located. This is platform and
* locale-specific so its almost impossible to get
* right, so it should be used with caution.
*/
boolean
prependToPath
=
false
;
boolean
appendToPath
=
false
;
String
dbgFontPath
=
System
.
getProperty
(
"sun.java2d.fontpath"
);
if
(
dbgFontPath
!=
null
)
{
if
(
dbgFontPath
.
startsWith
(
"prepend:"
))
{
prependToPath
=
true
;
dbgFontPath
=
dbgFontPath
.
substring
(
"prepend:"
.
length
());
}
else
if
(
dbgFontPath
.
startsWith
(
"append:"
))
{
appendToPath
=
true
;
dbgFontPath
=
dbgFontPath
.
substring
(
"append:"
.
length
());
}
}
if
(
FontUtilities
.
debugFonts
())
{
Logger
logger
=
FontUtilities
.
getLogger
();
logger
.
info
(
"JRE font directory: "
+
jreFontDirName
);
logger
.
info
(
"Extra font path: "
+
extraFontPath
);
logger
.
info
(
"Debug font path: "
+
dbgFontPath
);
}
if
(
dbgFontPath
!=
null
)
{
/* In debugging mode we register all the paths
* Caution: this is a very expensive call on Solaris:-
*/
fontPath
=
getPlatformFontPath
(
noType1Font
);
if
(
extraFontPath
!=
null
)
{
fontPath
=
extraFontPath
+
File
.
pathSeparator
+
fontPath
;
}
if
(
appendToPath
)
{
fontPath
=
fontPath
+
File
.
pathSeparator
+
dbgFontPath
;
}
else
if
(
prependToPath
)
{
fontPath
=
dbgFontPath
+
File
.
pathSeparator
+
fontPath
;
}
else
{
fontPath
=
dbgFontPath
;
}
registerFontDirs
(
fontPath
);
}
else
if
(
extraFontPath
!=
null
)
{
/* If the font configuration contains an
* "appendedfontpath" entry, it is interpreted as a
* set of locations that should always be registered.
* It may be additional to locations normally found
* for that place, or it may be locations that need
* to have all their paths registered to locate all
* the needed platform names.
* This is typically when the same .TTF file is
* referenced from multiple font.dir files and all
* of these must be read to find all the native
* (XLFD) names for the font, so that X11 font APIs
* can be used for as many code points as possible.
*/
registerFontDirs
(
extraFontPath
);
}
/* On Solaris, we need to register the Japanese TrueType
* directory so that we can find the corresponding
* bitmap fonts. This could be done by listing the
* directory in the font configuration file, but we
* don't want to confuse users with this quirk. There
* are no bitmap fonts for other writing systems that
* correspond to TrueType fonts and have matching XLFDs.
* We need to register the bitmap fonts only in
* environments where they're on the X font path, i.e.,
* in the Japanese locale. Note that if the X Toolkit
* is in use the font path isn't set up by JDK, but
* users of a JA locale should have it
* set up already by their login environment.
*/
if
(
FontUtilities
.
isSolaris
&&
Locale
.
JAPAN
.
equals
(
Locale
.
getDefault
()))
{
registerFontDir
(
"/usr/openwin/lib/locale/ja/X11/fonts/TT"
);
}
initCompositeFonts
(
fontConfig
,
null
);
return
null
;
}
});
boolean
platformFont
=
AccessController
.
doPrivileged
(
new
PrivilegedAction
<
Boolean
>()
{
public
Boolean
run
()
{
String
prop
=
System
.
getProperty
(
"java2d.font.usePlatformFont"
);
String
env
=
System
.
getenv
(
"JAVA2D_USEPLATFORMFONT"
);
return
"true"
.
equals
(
prop
)
||
env
!=
null
;
}
});
if
(
platformFont
)
{
usePlatformFontMetrics
=
true
;
System
.
out
.
println
(
"Enabling platform font metrics for win32. This is an unsupported option."
);
System
.
out
.
println
(
"This yields incorrect composite font metrics as reported by 1.1.x releases."
);
System
.
out
.
println
(
"It is appropriate only for use by applications which do not use any Java 2"
);
System
.
out
.
println
(
"functionality. This property will be removed in a later release."
);
}
}
/**
* This method is provided for internal and exclusive use by Swing.
*
* @param font representing a physical font.
* @return true if the underlying font is a TrueType or OpenType font
* that claims to support the Microsoft Windows encoding corresponding to
* the default file.encoding property of this JRE instance.
* This narrow value is useful for Swing to decide if the font is useful
* for the the Windows Look and Feel, or, if a composite font should be
* used instead.
* The information used to make the decision is obtained from
* the ulCodePageRange fields in the font.
* A caller can use isLogicalFont(Font) in this class before calling
* this method and would not need to call this method if that
* returns true.
*/
// static boolean fontSupportsDefaultEncoding(Font font) {
// String encoding =
// (String) java.security.AccessController.doPrivileged(
// new sun.security.action.GetPropertyAction("file.encoding"));
// if (encoding == null || font == null) {
// return false;
// }
// encoding = encoding.toLowerCase(Locale.ENGLISH);
// return FontManager.fontSupportsEncoding(font, encoding);
// }
public
Font2DHandle
getNewComposite
(
String
family
,
int
style
,
Font2DHandle
handle
)
{
if
(!(
handle
.
font2D
instanceof
CompositeFont
))
{
return
handle
;
}
CompositeFont
oldComp
=
(
CompositeFont
)
handle
.
font2D
;
PhysicalFont
oldFont
=
oldComp
.
getSlotFont
(
0
);
if
(
family
==
null
)
{
family
=
oldFont
.
getFamilyName
(
null
);
}
if
(
style
==
-
1
)
{
style
=
oldComp
.
getStyle
();
}
Font2D
newFont
=
findFont2D
(
family
,
style
,
NO_FALLBACK
);
if
(!(
newFont
instanceof
PhysicalFont
))
{
newFont
=
oldFont
;
}
PhysicalFont
physicalFont
=
(
PhysicalFont
)
newFont
;
CompositeFont
dialog2D
=
(
CompositeFont
)
findFont2D
(
"dialog"
,
style
,
NO_FALLBACK
);
if
(
dialog2D
==
null
)
{
/* shouldn't happen */
return
handle
;
}
CompositeFont
compFont
=
new
CompositeFont
(
physicalFont
,
dialog2D
);
Font2DHandle
newHandle
=
new
Font2DHandle
(
compFont
);
return
newHandle
;
}
protected
void
registerCompositeFont
(
String
compositeName
,
String
[]
componentFileNames
,
String
[]
componentNames
,
int
numMetricsSlots
,
int
[]
exclusionRanges
,
int
[]
exclusionMaxIndex
,
boolean
defer
)
{
CompositeFont
cf
=
new
CompositeFont
(
compositeName
,
componentFileNames
,
componentNames
,
numMetricsSlots
,
exclusionRanges
,
exclusionMaxIndex
,
defer
,
this
);
addCompositeToFontList
(
cf
,
Font2D
.
FONT_CONFIG_RANK
);
synchronized
(
compFonts
)
{
compFonts
[
maxCompFont
++]
=
cf
;
}
}
/* This variant is used only when the application specifies
* a variant of composite fonts which prefers locale specific or
* proportional fonts.
*/
protected
static
void
registerCompositeFont
(
String
compositeName
,
String
[]
componentFileNames
,
String
[]
componentNames
,
int
numMetricsSlots
,
int
[]
exclusionRanges
,
int
[]
exclusionMaxIndex
,
boolean
defer
,
ConcurrentHashMap
<
String
,
Font2D
>
altNameCache
)
{
CompositeFont
cf
=
new
CompositeFont
(
compositeName
,
componentFileNames
,
componentNames
,
numMetricsSlots
,
exclusionRanges
,
exclusionMaxIndex
,
defer
,
SunFontManager
.
getInstance
());
/* if the cache has an existing composite for this case, make
* its handle point to this new font.
* This ensures that when the altNameCache that is passed in
* is the global mapNameCache - ie we are running as an application -
* that any statically created java.awt.Font instances which already
* have a Font2D instance will have that re-directed to the new Font
* on subsequent uses. This is particularly important for "the"
* default font instance, or similar cases where a UI toolkit (eg
* Swing) has cached a java.awt.Font. Note that if Swing is using
* a custom composite APIs which update the standard composites have
* no effect - this is typically the case only when using the Windows
* L&F where these APIs would conflict with that L&F anyway.
*/
Font2D
oldFont
=
(
Font2D
)
altNameCache
.
get
(
compositeName
.
toLowerCase
(
Locale
.
ENGLISH
));
if
(
oldFont
instanceof
CompositeFont
)
{
oldFont
.
handle
.
font2D
=
cf
;
}
altNameCache
.
put
(
compositeName
.
toLowerCase
(
Locale
.
ENGLISH
),
cf
);
}
private
void
addCompositeToFontList
(
CompositeFont
f
,
int
rank
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
().
info
(
"Add to Family "
+
f
.
familyName
+
", Font "
+
f
.
fullName
+
" rank="
+
rank
);
}
f
.
setRank
(
rank
);
compositeFonts
.
put
(
f
.
fullName
,
f
);
fullNameToFont
.
put
(
f
.
fullName
.
toLowerCase
(
Locale
.
ENGLISH
),
f
);
FontFamily
family
=
FontFamily
.
getFamily
(
f
.
familyName
);
if
(
family
==
null
)
{
family
=
new
FontFamily
(
f
.
familyName
,
true
,
rank
);
}
family
.
setFont
(
f
,
f
.
style
);
}
/*
* Systems may have fonts with the same name.
* We want to register only one of such fonts (at least until
* such time as there might be APIs which can accommodate > 1).
* Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts,
* 4) Type1 fonts, 5) native fonts.
*
* If the new font has the same name as the old font, the higher
* ranked font gets added, replacing the lower ranked one.
* If the fonts are of equal rank, then make a special case of
* font configuration rank fonts, which are on closer inspection,
* OT/TT fonts such that the larger font is registered. This is
* a heuristic since a font may be "larger" in the sense of more
* code points, or be a larger "file" because it has more bitmaps.
* So it is possible that using filesize may lead to less glyphs, and
* using glyphs may lead to lower quality display. Probably number
* of glyphs is the ideal, but filesize is information we already
* have and is good enough for the known cases.
* Also don't want to register fonts that match JRE font families
* but are coming from a source other than the JRE.
* This will ensure that we will algorithmically style the JRE
* plain font and get the same set of glyphs for all styles.
*
* Note that this method returns a value
* if it returns the same object as its argument that means this
* font was newly registered.
* If it returns a different object it means this font already exists,
* and you should use that one.
* If it returns null means this font was not registered and none
* in that name is registered. The caller must find a substitute
*/
private
PhysicalFont
addToFontList
(
PhysicalFont
f
,
int
rank
)
{
String
fontName
=
f
.
fullName
;
String
familyName
=
f
.
familyName
;
if
(
fontName
==
null
||
""
.
equals
(
fontName
))
{
return
null
;
}
if
(
compositeFonts
.
containsKey
(
fontName
))
{
/* Don't register any font that has the same name as a composite */
return
null
;
}
f
.
setRank
(
rank
);
if
(!
physicalFonts
.
containsKey
(
fontName
))
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
().
info
(
"Add to Family "
+
familyName
+
", Font "
+
fontName
+
" rank="
+
rank
);
}
physicalFonts
.
put
(
fontName
,
f
);
FontFamily
family
=
FontFamily
.
getFamily
(
familyName
);
if
(
family
==
null
)
{
family
=
new
FontFamily
(
familyName
,
false
,
rank
);
family
.
setFont
(
f
,
f
.
style
);
}
else
if
(
family
.
getRank
()
>=
rank
)
{
family
.
setFont
(
f
,
f
.
style
);
}
fullNameToFont
.
put
(
fontName
.
toLowerCase
(
Locale
.
ENGLISH
),
f
);
return
f
;
}
else
{
PhysicalFont
newFont
=
f
;
PhysicalFont
oldFont
=
physicalFonts
.
get
(
fontName
);
if
(
oldFont
==
null
)
{
return
null
;
}
/* If the new font is of an equal or higher rank, it is a
* candidate to replace the current one, subject to further tests.
*/
if
(
oldFont
.
getRank
()
>=
rank
)
{
/* All fonts initialise their mapper when first
* used. If the mapper is non-null then this font
* has been accessed at least once. In that case
* do not replace it. This may be overly stringent,
* but its probably better not to replace a font that
* someone is already using without a compelling reason.
* Additionally the primary case where it is known
* this behaviour is important is in certain composite
* fonts, and since all the components of a given
* composite are usually initialised together this
* is unlikely. For this to be a problem, there would
* have to be a case where two different composites used
* different versions of the same-named font, and they
* were initialised and used at separate times.
* In that case we continue on and allow the new font to
* be installed, but replaceFont will continue to allow
* the original font to be used in Composite fonts.
*/
if
(
oldFont
.
mapper
!=
null
&&
rank
>
Font2D
.
FONT_CONFIG_RANK
)
{
return
oldFont
;
}
/* Normally we require a higher rank to replace a font,
* but as a special case, if the two fonts are the same rank,
* and are instances of TrueTypeFont we want the
* more complete (larger) one.
*/
if
(
oldFont
.
getRank
()
==
rank
)
{
if
(
oldFont
instanceof
TrueTypeFont
&&
newFont
instanceof
TrueTypeFont
)
{
TrueTypeFont
oldTTFont
=
(
TrueTypeFont
)
oldFont
;
TrueTypeFont
newTTFont
=
(
TrueTypeFont
)
newFont
;
if
(
oldTTFont
.
fileSize
>=
newTTFont
.
fileSize
)
{
return
oldFont
;
}
}
else
{
return
oldFont
;
}
}
/* Don't replace ever JRE fonts.
* This test is in case a font configuration references
* a Lucida font, which has been mapped to a Lucida
* from the host O/S. The assumption here is that any
* such font configuration file is probably incorrect, or
* the host O/S version is for the use of AWT.
* In other words if we reach here, there's a possible
* problem with our choice of font configuration fonts.
*/
if
(
oldFont
.
platName
.
startsWith
(
jreFontDirName
))
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
warning
(
"Unexpected attempt to replace a JRE "
+
" font "
+
fontName
+
" from "
+
oldFont
.
platName
+
" with "
+
newFont
.
platName
);
}
return
oldFont
;
}
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Replace in Family "
+
familyName
+
",Font "
+
fontName
+
" new rank="
+
rank
+
" from "
+
oldFont
.
platName
+
" with "
+
newFont
.
platName
);
}
replaceFont
(
oldFont
,
newFont
);
physicalFonts
.
put
(
fontName
,
newFont
);
fullNameToFont
.
put
(
fontName
.
toLowerCase
(
Locale
.
ENGLISH
),
newFont
);
FontFamily
family
=
FontFamily
.
getFamily
(
familyName
);
if
(
family
==
null
)
{
family
=
new
FontFamily
(
familyName
,
false
,
rank
);
family
.
setFont
(
newFont
,
newFont
.
style
);
}
else
if
(
family
.
getRank
()
>=
rank
)
{
family
.
setFont
(
newFont
,
newFont
.
style
);
}
return
newFont
;
}
else
{
return
oldFont
;
}
}
}
public
Font2D
[]
getRegisteredFonts
()
{
PhysicalFont
[]
physFonts
=
getPhysicalFonts
();
int
mcf
=
maxCompFont
;
/* for MT-safety */
Font2D
[]
regFonts
=
new
Font2D
[
physFonts
.
length
+
mcf
];
System
.
arraycopy
(
compFonts
,
0
,
regFonts
,
0
,
mcf
);
System
.
arraycopy
(
physFonts
,
0
,
regFonts
,
mcf
,
physFonts
.
length
);
return
regFonts
;
}
protected
PhysicalFont
[]
getPhysicalFonts
()
{
return
physicalFonts
.
values
().
toArray
(
new
PhysicalFont
[
0
]);
}
/* The class FontRegistrationInfo is used when a client says not
* to register a font immediately. This mechanism is used to defer
* initialisation of all the components of composite fonts at JRE
* start-up. The CompositeFont class is "aware" of this and when it
* is first used it asks for the registration of its components.
* Also in the event that any physical font is requested the
* deferred fonts are initialised before triggering a search of the
* system.
* Two maps are used. One to track the deferred fonts. The
* other to track the fonts that have been initialised through this
* mechanism.
*/
private
static
final
class
FontRegistrationInfo
{
String
fontFilePath
;
String
[]
nativeNames
;
int
fontFormat
;
boolean
javaRasterizer
;
int
fontRank
;
FontRegistrationInfo
(
String
fontPath
,
String
[]
names
,
int
format
,
boolean
useJavaRasterizer
,
int
rank
)
{
this
.
fontFilePath
=
fontPath
;
this
.
nativeNames
=
names
;
this
.
fontFormat
=
format
;
this
.
javaRasterizer
=
useJavaRasterizer
;
this
.
fontRank
=
rank
;
}
}
private
final
ConcurrentHashMap
<
String
,
FontRegistrationInfo
>
deferredFontFiles
=
new
ConcurrentHashMap
<
String
,
FontRegistrationInfo
>();
private
final
ConcurrentHashMap
<
String
,
Font2DHandle
>
initialisedFonts
=
new
ConcurrentHashMap
<
String
,
Font2DHandle
>();
/* Remind: possibly enhance initialiseDeferredFonts() to be
* optionally given a name and a style and it could stop when it
* finds that font - but this would be a problem if two of the
* fonts reference the same font face name (cf the Solaris
* euro fonts).
*/
protected
synchronized
void
initialiseDeferredFonts
()
{
for
(
String
fileName
:
deferredFontFiles
.
keySet
())
{
initialiseDeferredFont
(
fileName
);
}
}
protected
synchronized
void
registerDeferredJREFonts
(
String
jreDir
)
{
for
(
FontRegistrationInfo
info
:
deferredFontFiles
.
values
())
{
if
(
info
.
fontFilePath
!=
null
&&
info
.
fontFilePath
.
startsWith
(
jreDir
))
{
initialiseDeferredFont
(
info
.
fontFilePath
);
}
}
}
public
boolean
isDeferredFont
(
String
fileName
)
{
return
deferredFontFiles
.
containsKey
(
fileName
);
}
/* We keep a map of the files which contain the Lucida fonts so we
* don't need to search for them.
* But since we know what fonts these files contain, we can also avoid
* opening them to look for a font name we don't recognise - see
* findDeferredFont().
* For typical cases where the font isn't a JRE one the overhead is
* this method call, HashMap.get() and null reference test, then
* a boolean test of noOtherJREFontFiles.
*/
public
/*private*/
PhysicalFont
findJREDeferredFont
(
String
name
,
int
style
)
{
PhysicalFont
physicalFont
;
String
nameAndStyle
=
name
.
toLowerCase
(
Locale
.
ENGLISH
)
+
style
;
String
fileName
=
jreFontMap
.
get
(
nameAndStyle
);
if
(
fileName
!=
null
)
{
fileName
=
jreFontDirName
+
File
.
separator
+
fileName
;
if
(
deferredFontFiles
.
get
(
fileName
)
!=
null
)
{
physicalFont
=
initialiseDeferredFont
(
fileName
);
if
(
physicalFont
!=
null
&&
(
physicalFont
.
getFontName
(
null
).
equalsIgnoreCase
(
name
)
||
physicalFont
.
getFamilyName
(
null
).
equalsIgnoreCase
(
name
))
&&
physicalFont
.
style
==
style
)
{
return
physicalFont
;
}
}
}
/* Iterate over the deferred font files looking for any in the
* jre directory that we didn't recognise, open each of these.
* In almost all installations this will quickly fall through
* because only the Lucidas will be present and jreOtherFontFiles
* will be empty.
* noOtherJREFontFiles is used so we can skip this block as soon
* as its determined that its not needed - almost always after the
* very first time through.
*/
if
(
noOtherJREFontFiles
)
{
return
null
;
}
synchronized
(
jreLucidaFontFiles
)
{
if
(
jreOtherFontFiles
==
null
)
{
HashSet
<
String
>
otherFontFiles
=
new
HashSet
<
String
>();
for
(
String
deferredFile
:
deferredFontFiles
.
keySet
())
{
File
file
=
new
File
(
deferredFile
);
String
dir
=
file
.
getParent
();
String
fname
=
file
.
getName
();
/* skip names which aren't absolute, aren't in the JRE
* directory, or are known Lucida fonts.
*/
if
(
dir
==
null
||
!
dir
.
equals
(
jreFontDirName
)
||
jreLucidaFontFiles
.
contains
(
fname
))
{
continue
;
}
otherFontFiles
.
add
(
deferredFile
);
}
jreOtherFontFiles
=
otherFontFiles
.
toArray
(
STR_ARRAY
);
if
(
jreOtherFontFiles
.
length
==
0
)
{
noOtherJREFontFiles
=
true
;
}
}
for
(
int
i
=
0
;
i
<
jreOtherFontFiles
.
length
;
i
++)
{
fileName
=
jreOtherFontFiles
[
i
];
if
(
fileName
==
null
)
{
continue
;
}
jreOtherFontFiles
[
i
]
=
null
;
physicalFont
=
initialiseDeferredFont
(
fileName
);
if
(
physicalFont
!=
null
&&
(
physicalFont
.
getFontName
(
null
).
equalsIgnoreCase
(
name
)
||
physicalFont
.
getFamilyName
(
null
).
equalsIgnoreCase
(
name
))
&&
physicalFont
.
style
==
style
)
{
return
physicalFont
;
}
}
}
return
null
;
}
/* This skips JRE installed fonts. */
private
PhysicalFont
findOtherDeferredFont
(
String
name
,
int
style
)
{
for
(
String
fileName
:
deferredFontFiles
.
keySet
())
{
File
file
=
new
File
(
fileName
);
String
dir
=
file
.
getParent
();
String
fname
=
file
.
getName
();
if
(
dir
!=
null
&&
dir
.
equals
(
jreFontDirName
)
&&
jreLucidaFontFiles
.
contains
(
fname
))
{
continue
;
}
PhysicalFont
physicalFont
=
initialiseDeferredFont
(
fileName
);
if
(
physicalFont
!=
null
&&
(
physicalFont
.
getFontName
(
null
).
equalsIgnoreCase
(
name
)
||
physicalFont
.
getFamilyName
(
null
).
equalsIgnoreCase
(
name
))
&&
physicalFont
.
style
==
style
)
{
return
physicalFont
;
}
}
return
null
;
}
private
PhysicalFont
findDeferredFont
(
String
name
,
int
style
)
{
PhysicalFont
physicalFont
=
findJREDeferredFont
(
name
,
style
);
if
(
physicalFont
!=
null
)
{
return
physicalFont
;
}
else
{
return
findOtherDeferredFont
(
name
,
style
);
}
}
public
void
registerDeferredFont
(
String
fileNameKey
,
String
fullPathName
,
String
[]
nativeNames
,
int
fontFormat
,
boolean
useJavaRasterizer
,
int
fontRank
)
{
FontRegistrationInfo
regInfo
=
new
FontRegistrationInfo
(
fullPathName
,
nativeNames
,
fontFormat
,
useJavaRasterizer
,
fontRank
);
deferredFontFiles
.
put
(
fileNameKey
,
regInfo
);
}
public
synchronized
PhysicalFont
initialiseDeferredFont
(
String
fileNameKey
)
{
if
(
fileNameKey
==
null
)
{
return
null
;
}
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Opening deferred font file "
+
fileNameKey
);
}
PhysicalFont
physicalFont
;
FontRegistrationInfo
regInfo
=
deferredFontFiles
.
get
(
fileNameKey
);
if
(
regInfo
!=
null
)
{
deferredFontFiles
.
remove
(
fileNameKey
);
physicalFont
=
registerFontFile
(
regInfo
.
fontFilePath
,
regInfo
.
nativeNames
,
regInfo
.
fontFormat
,
regInfo
.
javaRasterizer
,
regInfo
.
fontRank
);
if
(
physicalFont
!=
null
)
{
/* Store the handle, so that if a font is bad, we
* retrieve the substituted font.
*/
initialisedFonts
.
put
(
fileNameKey
,
physicalFont
.
handle
);
}
else
{
initialisedFonts
.
put
(
fileNameKey
,
getDefaultPhysicalFont
().
handle
);
}
}
else
{
Font2DHandle
handle
=
initialisedFonts
.
get
(
fileNameKey
);
if
(
handle
==
null
)
{
/* Probably shouldn't happen, but just in case */
physicalFont
=
getDefaultPhysicalFont
();
}
else
{
physicalFont
=
(
PhysicalFont
)(
handle
.
font2D
);
}
}
return
physicalFont
;
}
public
boolean
isRegisteredFontFile
(
String
name
)
{
return
registeredFonts
.
containsKey
(
name
);
}
public
PhysicalFont
getRegisteredFontFile
(
String
name
)
{
return
registeredFonts
.
get
(
name
);
}
/* Note that the return value from this method is not always
* derived from this file, and may be null. See addToFontList for
* some explanation of this.
*/
public
PhysicalFont
registerFontFile
(
String
fileName
,
String
[]
nativeNames
,
int
fontFormat
,
boolean
useJavaRasterizer
,
int
fontRank
)
{
PhysicalFont
regFont
=
registeredFonts
.
get
(
fileName
);
if
(
regFont
!=
null
)
{
return
regFont
;
}
PhysicalFont
physicalFont
=
null
;
try
{
String
name
;
switch
(
fontFormat
)
{
case
FONTFORMAT_TRUETYPE:
int
fn
=
0
;
TrueTypeFont
ttf
;
do
{
ttf
=
new
TrueTypeFont
(
fileName
,
nativeNames
,
fn
++,
useJavaRasterizer
);
PhysicalFont
pf
=
addToFontList
(
ttf
,
fontRank
);
if
(
physicalFont
==
null
)
{
physicalFont
=
pf
;
}
}
while
(
fn
<
ttf
.
getFontCount
());
break
;
case
FONTFORMAT_TYPE1:
Type1Font
t1f
=
new
Type1Font
(
fileName
,
nativeNames
);
physicalFont
=
addToFontList
(
t1f
,
fontRank
);
break
;
case
FONTFORMAT_NATIVE:
NativeFont
nf
=
new
NativeFont
(
fileName
,
false
);
physicalFont
=
addToFontList
(
nf
,
fontRank
);
default
:
}
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Registered file "
+
fileName
+
" as font "
+
physicalFont
+
" rank="
+
fontRank
);
}
}
catch
(
FontFormatException
ffe
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
().
warning
(
"Unusable font: "
+
fileName
+
" "
+
ffe
.
toString
());
}
}
if
(
physicalFont
!=
null
&&
fontFormat
!=
FONTFORMAT_NATIVE
)
{
registeredFonts
.
put
(
fileName
,
physicalFont
);
}
return
physicalFont
;
}
public
void
registerFonts
(
String
[]
fileNames
,
String
[][]
nativeNames
,
int
fontCount
,
int
fontFormat
,
boolean
useJavaRasterizer
,
int
fontRank
,
boolean
defer
)
{
for
(
int
i
=
0
;
i
<
fontCount
;
i
++)
{
if
(
defer
)
{
registerDeferredFont
(
fileNames
[
i
],
fileNames
[
i
],
nativeNames
[
i
],
fontFormat
,
useJavaRasterizer
,
fontRank
);
}
else
{
registerFontFile
(
fileNames
[
i
],
nativeNames
[
i
],
fontFormat
,
useJavaRasterizer
,
fontRank
);
}
}
}
/*
* This is the Physical font used when some other font on the system
* can't be located. There has to be at least one font or the font
* system is not useful and the graphics environment cannot sustain
* the Java platform.
*/
public
PhysicalFont
getDefaultPhysicalFont
()
{
if
(
defaultPhysicalFont
==
null
)
{
/* findFont2D will load all fonts before giving up the search.
* If the JRE Lucida isn't found (eg because the JRE fonts
* directory is missing), it could find another version of Lucida
* from the host system. This is OK because at that point we are
* trying to gracefully handle/recover from a system
* misconfiguration and this is probably a reasonable substitution.
*/
defaultPhysicalFont
=
(
PhysicalFont
)
findFont2D
(
"Lucida Sans Regular"
,
Font
.
PLAIN
,
NO_FALLBACK
);
if
(
defaultPhysicalFont
==
null
)
{
defaultPhysicalFont
=
(
PhysicalFont
)
findFont2D
(
"Arial"
,
Font
.
PLAIN
,
NO_FALLBACK
);
}
if
(
defaultPhysicalFont
==
null
)
{
/* Because of the findFont2D call above, if we reach here, we
* know all fonts have already been loaded, just accept any
* match at this point. If this fails we are in real trouble
* and I don't know how to recover from there being absolutely
* no fonts anywhere on the system.
*/
Iterator
i
=
physicalFonts
.
values
().
iterator
();
if
(
i
.
hasNext
())
{
defaultPhysicalFont
=
(
PhysicalFont
)
i
.
next
();
}
else
{
throw
new
Error
(
"Probable fatal error:No fonts found."
);
}
}
}
return
defaultPhysicalFont
;
}
public
CompositeFont
getDefaultLogicalFont
(
int
style
)
{
return
(
CompositeFont
)
findFont2D
(
"dialog"
,
style
,
NO_FALLBACK
);
}
/*
* return String representation of style prepended with "."
* This is useful for performance to avoid unnecessary string operations.
*/
private
static
String
dotStyleStr
(
int
num
)
{
switch
(
num
){
case
Font
.
BOLD
:
return
".bold"
;
case
Font
.
ITALIC
:
return
".italic"
;
case
Font
.
ITALIC
|
Font
.
BOLD
:
return
".bolditalic"
;
default
:
return
".plain"
;
}
}
/* This is implemented only on windows and is called from code that
* executes only on windows. This isn't pretty but its not a precedent
* in this file. This very probably should be cleaned up at some point.
*/
protected
void
populateFontFileNameMap
(
HashMap
<
String
,
String
>
fontToFileMap
,
HashMap
<
String
,
String
>
fontToFamilyNameMap
,
HashMap
<
String
,
ArrayList
<
String
>>
familyToFontListMap
,
Locale
locale
)
{
}
/* Obtained from Platform APIs (windows only)
* Map from lower-case font full name to basename of font file.
* Eg "arial bold" -> ARIALBD.TTF.
* For TTC files, there is a mapping for each font in the file.
*/
private
HashMap
<
String
,
String
>
fontToFileMap
=
null
;
/* Obtained from Platform APIs (windows only)
* Map from lower-case font full name to the name of its font family
* Eg "arial bold" -> "Arial"
*/
private
HashMap
<
String
,
String
>
fontToFamilyNameMap
=
null
;
/* Obtained from Platform APIs (windows only)
* Map from a lower-case family name to a list of full names of
* the member fonts, eg:
* "arial" -> ["Arial", "Arial Bold", "Arial Italic","Arial Bold Italic"]
*/
private
HashMap
<
String
,
ArrayList
<
String
>>
familyToFontListMap
=
null
;
/* The directories which contain platform fonts */
private
String
[]
pathDirs
=
null
;
private
boolean
haveCheckedUnreferencedFontFiles
;
private
String
[]
getFontFilesFromPath
(
boolean
noType1
)
{
final
FilenameFilter
filter
;
if
(
noType1
)
{
filter
=
ttFilter
;
}
else
{
filter
=
new
TTorT1Filter
();
}
return
(
String
[])
AccessController
.
doPrivileged
(
new
PrivilegedAction
()
{
public
Object
run
()
{
if
(
pathDirs
.
length
==
1
)
{
File
dir
=
new
File
(
pathDirs
[
0
]);
String
[]
files
=
dir
.
list
(
filter
);
if
(
files
==
null
)
{
return
new
String
[
0
];
}
for
(
int
f
=
0
;
f
<
files
.
length
;
f
++)
{
files
[
f
]
=
files
[
f
].
toLowerCase
();
}
return
files
;
}
else
{
ArrayList
<
String
>
fileList
=
new
ArrayList
<
String
>();
for
(
int
i
=
0
;
i
<
pathDirs
.
length
;
i
++)
{
File
dir
=
new
File
(
pathDirs
[
i
]);
String
[]
files
=
dir
.
list
(
filter
);
if
(
files
==
null
)
{
continue
;
}
for
(
int
f
=
0
;
f
<
files
.
length
;
f
++)
{
fileList
.
add
(
files
[
f
].
toLowerCase
());
}
}
return
fileList
.
toArray
(
STR_ARRAY
);
}
}
});
}
/* This is needed since some windows registry names don't match
* the font names.
* - UPC styled font names have a double space, but the
* registry entry mapping to a file doesn't.
* - Marlett is in a hidden file not listed in the registry
* - The registry advertises that the file david.ttf contains a
* font with the full name "David Regular" when in fact its
* just "David".
* Directly fix up these known cases as this is faster.
* If a font which doesn't match these known cases has no file,
* it may be a font that has been temporarily added to the known set
* or it may be an installed font with a missing registry entry.
* Installed fonts are those in the windows font directories.
* Make a best effort attempt to locate these.
* We obtain the list of TrueType fonts in these directories and
* filter out all the font files we already know about from the registry.
* What remains may be "bad" fonts, duplicate fonts, or perhaps the
* missing font(s) we are looking for.
* Open each of these files to find out.
*/
private
void
resolveWindowsFonts
()
{
ArrayList
<
String
>
unmappedFontNames
=
null
;
for
(
String
font
:
fontToFamilyNameMap
.
keySet
())
{
String
file
=
fontToFileMap
.
get
(
font
);
if
(
file
==
null
)
{
if
(
font
.
indexOf
(
" "
)
>
0
)
{
String
newName
=
font
.
replaceFirst
(
" "
,
" "
);
file
=
fontToFileMap
.
get
(
newName
);
/* If this name exists and isn't for a valid name
* replace the mapping to the file with this font
*/
if
(
file
!=
null
&&
!
fontToFamilyNameMap
.
containsKey
(
newName
))
{
fontToFileMap
.
remove
(
newName
);
fontToFileMap
.
put
(
font
,
file
);
}
}
else
if
(
font
.
equals
(
"marlett"
))
{
fontToFileMap
.
put
(
font
,
"marlett.ttf"
);
}
else
if
(
font
.
equals
(
"david"
))
{
file
=
fontToFileMap
.
get
(
"david regular"
);
if
(
file
!=
null
)
{
fontToFileMap
.
remove
(
"david regular"
);
fontToFileMap
.
put
(
"david"
,
file
);
}
}
else
{
if
(
unmappedFontNames
==
null
)
{
unmappedFontNames
=
new
ArrayList
<
String
>();
}
unmappedFontNames
.
add
(
font
);
}
}
}
if
(
unmappedFontNames
!=
null
)
{
HashSet
<
String
>
unmappedFontFiles
=
new
HashSet
<
String
>();
/* Every font key in fontToFileMap ought to correspond to a
* font key in fontToFamilyNameMap. Entries that don't seem
* to correspond are likely fonts that were named differently
* by GDI than in the registry. One known cause of this is when
* Windows has had its regional settings changed so that from
* GDI we get a localised (eg Chinese or Japanese) name for the
* font, but the registry retains the English version of the name
* that corresponded to the "install" locale for windows.
* Since we are in this code block because there are unmapped
* font names, we can look to find unused font->file mappings
* and then open the files to read the names. We don't generally
* want to open font files, as its a performance hit, but this
* occurs only for a small number of fonts on specific system
* configs - ie is believed that a "true" Japanese windows would
* have JA names in the registry too.
* Clone fontToFileMap and remove from the clone all keys which
* match a fontToFamilyNameMap key. What remains maps to the
* files we want to open to find the fonts GDI returned.
* A font in such a file is added to the fontToFileMap after
* checking its one of the unmappedFontNames we are looking for.
* The original name that didn't map is removed from fontToFileMap
* so essentially this "fixes up" fontToFileMap to use the same
* name as GDI.
* Also note that typically the fonts for which this occurs in
* CJK locales are TTC fonts and not all fonts in a TTC may have
* localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of
* them "MS UI Gothic" has no JA name whereas the other two do.
* So not every font in these files is unmapped or new.
*/
HashMap
<
String
,
String
>
ffmapCopy
=
(
HashMap
<
String
,
String
>)(
fontToFileMap
.
clone
());
for
(
String
key
:
fontToFamilyNameMap
.
keySet
())
{
ffmapCopy
.
remove
(
key
);
}
for
(
String
key
:
ffmapCopy
.
keySet
())
{
unmappedFontFiles
.
add
(
ffmapCopy
.
get
(
key
));
fontToFileMap
.
remove
(
key
);
}
resolveFontFiles
(
unmappedFontFiles
,
unmappedFontNames
);
/* If there are still unmapped font names, this means there's
* something that wasn't in the registry. We need to get all
* the font files directly and look at the ones that weren't
* found in the registry.
*/
if
(
unmappedFontNames
.
size
()
>
0
)
{
/* getFontFilesFromPath() returns all lower case names.
* To compare we also need lower case
* versions of the names from the registry.
*/
ArrayList
<
String
>
registryFiles
=
new
ArrayList
<
String
>();
for
(
String
regFile
:
fontToFileMap
.
values
())
{
registryFiles
.
add
(
regFile
.
toLowerCase
());
}
/* We don't look for Type1 files here as windows will
* not enumerate these, so aren't useful in reconciling
* GDI's unmapped files. We do find these later when
* we enumerate all fonts.
*/
for
(
String
pathFile
:
getFontFilesFromPath
(
true
))
{
if
(!
registryFiles
.
contains
(
pathFile
))
{
unmappedFontFiles
.
add
(
pathFile
);
}
}
resolveFontFiles
(
unmappedFontFiles
,
unmappedFontNames
);
}
/* remove from the set of names that will be returned to the
* user any fonts that can't be mapped to files.
*/
if
(
unmappedFontNames
.
size
()
>
0
)
{
int
sz
=
unmappedFontNames
.
size
();
for
(
int
i
=
0
;
i
<
sz
;
i
++)
{
String
name
=
unmappedFontNames
.
get
(
i
);
String
familyName
=
fontToFamilyNameMap
.
get
(
name
);
if
(
familyName
!=
null
)
{
ArrayList
family
=
familyToFontListMap
.
get
(
familyName
);
if
(
family
!=
null
)
{
if
(
family
.
size
()
<=
1
)
{
familyToFontListMap
.
remove
(
familyName
);
}
}
}
fontToFamilyNameMap
.
remove
(
name
);
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"No file for font:"
+
name
);
}
}
}
}
}
/**
* In some cases windows may have fonts in the fonts folder that
* don't show up in the registry or in the GDI calls to enumerate fonts.
* The only way to find these is to list the directory. We invoke this
* only in getAllFonts/Families, so most searches for a specific
* font that is satisfied by the GDI/registry calls don't take the
* additional hit of listing the directory. This hit is small enough
* that its not significant in these 'enumerate all the fonts' cases.
* The basic approach is to cross-reference the files windows found
* with the ones in the directory listing approach, and for each
* in the latter list that is missing from the former list, register it.
*/
private
synchronized
void
checkForUnreferencedFontFiles
()
{
if
(
haveCheckedUnreferencedFontFiles
)
{
return
;
}
haveCheckedUnreferencedFontFiles
=
true
;
if
(!
FontUtilities
.
isWindows
)
{
return
;
}
/* getFontFilesFromPath() returns all lower case names.
* To compare we also need lower case
* versions of the names from the registry.
*/
ArrayList
<
String
>
registryFiles
=
new
ArrayList
<
String
>();
for
(
String
regFile
:
fontToFileMap
.
values
())
{
registryFiles
.
add
(
regFile
.
toLowerCase
());
}
/* To avoid any issues with concurrent modification, create
* copies of the existing maps, add the new fonts into these
* and then replace the references to the old ones with the
* new maps. ConcurrentHashmap is another option but its a lot
* more changes and with this exception, these maps are intended
* to be static.
*/
HashMap
<
String
,
String
>
fontToFileMap2
=
null
;
HashMap
<
String
,
String
>
fontToFamilyNameMap2
=
null
;
HashMap
<
String
,
ArrayList
<
String
>>
familyToFontListMap2
=
null
;;
for
(
String
pathFile
:
getFontFilesFromPath
(
false
))
{
if
(!
registryFiles
.
contains
(
pathFile
))
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Found non-registry file : "
+
pathFile
);
}
PhysicalFont
f
=
registerFontFile
(
getPathName
(
pathFile
));
if
(
f
==
null
)
{
continue
;
}
if
(
fontToFileMap2
==
null
)
{
fontToFileMap2
=
new
HashMap
<
String
,
String
>(
fontToFileMap
);
fontToFamilyNameMap2
=
new
HashMap
<
String
,
String
>(
fontToFamilyNameMap
);
familyToFontListMap2
=
new
HashMap
<
String
,
ArrayList
<
String
>>(
familyToFontListMap
);
}
String
fontName
=
f
.
getFontName
(
null
);
String
family
=
f
.
getFamilyName
(
null
);
String
familyLC
=
family
.
toLowerCase
();
fontToFamilyNameMap2
.
put
(
fontName
,
family
);
fontToFileMap2
.
put
(
fontName
,
pathFile
);
ArrayList
<
String
>
fonts
=
familyToFontListMap2
.
get
(
familyLC
);
if
(
fonts
==
null
)
{
fonts
=
new
ArrayList
<
String
>();
}
else
{
fonts
=
new
ArrayList
<
String
>(
fonts
);
}
fonts
.
add
(
fontName
);
familyToFontListMap2
.
put
(
familyLC
,
fonts
);
}
}
if
(
fontToFileMap2
!=
null
)
{
fontToFileMap
=
fontToFileMap2
;
familyToFontListMap
=
familyToFontListMap2
;
fontToFamilyNameMap
=
fontToFamilyNameMap2
;
}
}
private
void
resolveFontFiles
(
HashSet
<
String
>
unmappedFiles
,
ArrayList
<
String
>
unmappedFonts
)
{
Locale
l
=
SunToolkit
.
getStartupLocale
();
for
(
String
file
:
unmappedFiles
)
{
try
{
int
fn
=
0
;
TrueTypeFont
ttf
;
String
fullPath
=
getPathName
(
file
);
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Trying to resolve file "
+
fullPath
);
}
do
{
ttf
=
new
TrueTypeFont
(
fullPath
,
null
,
fn
++,
true
);
// prefer the font's locale name.
String
fontName
=
ttf
.
getFontName
(
l
).
toLowerCase
();
if
(
unmappedFonts
.
contains
(
fontName
))
{
fontToFileMap
.
put
(
fontName
,
file
);
unmappedFonts
.
remove
(
fontName
);
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Resolved absent registry entry for "
+
fontName
+
" located in "
+
fullPath
);
}
}
}
while
(
fn
<
ttf
.
getFontCount
());
}
catch
(
Exception
e
)
{
}
}
}
private
synchronized
HashMap
<
String
,
String
>
getFullNameToFileMap
()
{
if
(
fontToFileMap
==
null
)
{
pathDirs
=
getPlatformFontDirs
(
noType1Font
);
fontToFileMap
=
new
HashMap
<
String
,
String
>(
100
);
fontToFamilyNameMap
=
new
HashMap
<
String
,
String
>(
100
);
familyToFontListMap
=
new
HashMap
<
String
,
ArrayList
<
String
>>(
50
);
populateFontFileNameMap
(
fontToFileMap
,
fontToFamilyNameMap
,
familyToFontListMap
,
Locale
.
ENGLISH
);
if
(
FontUtilities
.
isWindows
)
{
resolveWindowsFonts
();
}
if
(
FontUtilities
.
isLogging
())
{
logPlatformFontInfo
();
}
}
return
fontToFileMap
;
}
private
void
logPlatformFontInfo
()
{
Logger
logger
=
FontUtilities
.
getLogger
();
for
(
int
i
=
0
;
i
<
pathDirs
.
length
;
i
++)
{
logger
.
info
(
"fontdir="
+
pathDirs
[
i
]);
}
for
(
String
keyName
:
fontToFileMap
.
keySet
())
{
logger
.
info
(
"font="
+
keyName
+
" file="
+
fontToFileMap
.
get
(
keyName
));
}
for
(
String
keyName
:
fontToFamilyNameMap
.
keySet
())
{
logger
.
info
(
"font="
+
keyName
+
" family="
+
fontToFamilyNameMap
.
get
(
keyName
));
}
for
(
String
keyName
:
familyToFontListMap
.
keySet
())
{
logger
.
info
(
"family="
+
keyName
+
" fonts="
+
familyToFontListMap
.
get
(
keyName
));
}
}
/* Note this return list excludes logical fonts and JRE fonts */
protected
String
[]
getFontNamesFromPlatform
()
{
if
(
getFullNameToFileMap
().
size
()
==
0
)
{
return
null
;
}
checkForUnreferencedFontFiles
();
/* This odd code with TreeMap is used to preserve a historical
* behaviour wrt the sorting order .. */
ArrayList
<
String
>
fontNames
=
new
ArrayList
<
String
>();
for
(
ArrayList
<
String
>
a
:
familyToFontListMap
.
values
())
{
for
(
String
s
:
a
)
{
fontNames
.
add
(
s
);
}
}
return
fontNames
.
toArray
(
STR_ARRAY
);
}
public
boolean
gotFontsFromPlatform
()
{
return
getFullNameToFileMap
().
size
()
!=
0
;
}
public
String
getFileNameForFontName
(
String
fontName
)
{
String
fontNameLC
=
fontName
.
toLowerCase
(
Locale
.
ENGLISH
);
return
fontToFileMap
.
get
(
fontNameLC
);
}
private
PhysicalFont
registerFontFile
(
String
file
)
{
if
(
new
File
(
file
).
isAbsolute
()
&&
!
registeredFonts
.
contains
(
file
))
{
int
fontFormat
=
FONTFORMAT_NONE
;
int
fontRank
=
Font2D
.
UNKNOWN_RANK
;
if
(
ttFilter
.
accept
(
null
,
file
))
{
fontFormat
=
FONTFORMAT_TRUETYPE
;
fontRank
=
Font2D
.
TTF_RANK
;
}
else
if
(
t1Filter
.
accept
(
null
,
file
))
{
fontFormat
=
FONTFORMAT_TYPE1
;
fontRank
=
Font2D
.
TYPE1_RANK
;
}
if
(
fontFormat
==
FONTFORMAT_NONE
)
{
return
null
;
}
return
registerFontFile
(
file
,
null
,
fontFormat
,
false
,
fontRank
);
}
return
null
;
}
/* Used to register any font files that are found by platform APIs
* that weren't previously found in the standard font locations.
* the isAbsolute() check is needed since that's whats stored in the
* set, and on windows, the fonts in the system font directory that
* are in the fontToFileMap are just basenames. We don't want to try
* to register those again, but we do want to register other registry
* installed fonts.
*/
protected
void
registerOtherFontFiles
(
HashSet
registeredFontFiles
)
{
if
(
getFullNameToFileMap
().
size
()
==
0
)
{
return
;
}
for
(
String
file
:
fontToFileMap
.
values
())
{
registerFontFile
(
file
);
}
}
public
boolean
getFamilyNamesFromPlatform
(
TreeMap
<
String
,
String
>
familyNames
,
Locale
requestedLocale
)
{
if
(
getFullNameToFileMap
().
size
()
==
0
)
{
return
false
;
}
checkForUnreferencedFontFiles
();
for
(
String
name
:
fontToFamilyNameMap
.
values
())
{
familyNames
.
put
(
name
.
toLowerCase
(
requestedLocale
),
name
);
}
return
true
;
}
/* Path may be absolute or a base file name relative to one of
* the platform font directories
*/
private
String
getPathName
(
final
String
s
)
{
File
f
=
new
File
(
s
);
if
(
f
.
isAbsolute
())
{
return
s
;
}
else
if
(
pathDirs
.
length
==
1
)
{
return
pathDirs
[
0
]
+
File
.
separator
+
s
;
}
else
{
String
path
=
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
<
String
>()
{
public
String
run
()
{
for
(
int
p
=
0
;
p
<
pathDirs
.
length
;
p
++)
{
File
f
=
new
File
(
pathDirs
[
p
]
+
File
.
separator
+
s
);
if
(
f
.
exists
())
{
return
f
.
getAbsolutePath
();
}
}
return
null
;
}
});
if
(
path
!=
null
)
{
return
path
;
}
}
return
s
;
// shouldn't happen, but harmless
}
/* lcName is required to be lower case for use as a key.
* lcName may be a full name, or a family name, and style may
* be specified in addition to either of these. So be sure to
* get the right one. Since an app *could* ask for "Foo Regular"
* and later ask for "Foo Italic", if we don't register all the
* styles, then logic in findFont2D may try to style the original
* so we register the entire family if we get a match here.
* This is still a big win because this code is invoked where
* otherwise we would register all fonts.
* It's also useful for the case where "Foo Bold" was specified with
* style Font.ITALIC, as we would want in that case to try to return
* "Foo Bold Italic" if it exists, and it is only by locating "Foo Bold"
* and opening it that we really "know" it's Bold, and can look for
* a font that supports that and the italic style.
* The code in here is not overtly windows-specific but in fact it
* is unlikely to be useful as is on other platforms. It is maintained
* in this shared source file to be close to its sole client and
* because so much of the logic is intertwined with the logic in
* findFont2D.
*/
private
Font2D
findFontFromPlatform
(
String
lcName
,
int
style
)
{
if
(
getFullNameToFileMap
().
size
()
==
0
)
{
return
null
;
}
ArrayList
<
String
>
family
=
null
;
String
fontFile
=
null
;
String
familyName
=
fontToFamilyNameMap
.
get
(
lcName
);
if
(
familyName
!=
null
)
{
fontFile
=
fontToFileMap
.
get
(
lcName
);
family
=
familyToFontListMap
.
get
(
familyName
.
toLowerCase
(
Locale
.
ENGLISH
));
}
else
{
family
=
familyToFontListMap
.
get
(
lcName
);
// is lcName is a family?
if
(
family
!=
null
&&
family
.
size
()
>
0
)
{
String
lcFontName
=
family
.
get
(
0
).
toLowerCase
(
Locale
.
ENGLISH
);
if
(
lcFontName
!=
null
)
{
familyName
=
fontToFamilyNameMap
.
get
(
lcFontName
);
}
}
}
if
(
family
==
null
||
familyName
==
null
)
{
return
null
;
}
String
[]
fontList
=
(
String
[])
family
.
toArray
(
STR_ARRAY
);
if
(
fontList
.
length
==
0
)
{
return
null
;
}
/* first check that for every font in this family we can find
* a font file. The specific reason for doing this is that
* in at least one case on Windows a font has the face name "David"
* but the registry entry is "David Regular". That is the "unique"
* name of the font but in other cases the registry contains the
* "full" name. See the specifications of name ids 3 and 4 in the
* TrueType 'name' table.
* In general this could cause a problem that we fail to register
* if we all members of a family that we may end up mapping to
* the wrong font member: eg return Bold when Plain is needed.
*/
for
(
int
f
=
0
;
f
<
fontList
.
length
;
f
++)
{
String
fontNameLC
=
fontList
[
f
].
toLowerCase
(
Locale
.
ENGLISH
);
String
fileName
=
fontToFileMap
.
get
(
fontNameLC
);
if
(
fileName
==
null
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Platform lookup : No file for font "
+
fontList
[
f
]
+
" in family "
+
familyName
);
}
return
null
;
}
}
/* Currently this code only looks for TrueType fonts, so format
* and rank can be specified without looking at the filename.
*/
PhysicalFont
physicalFont
=
null
;
if
(
fontFile
!=
null
)
{
physicalFont
=
registerFontFile
(
getPathName
(
fontFile
),
null
,
FONTFORMAT_TRUETYPE
,
false
,
Font2D
.
TTF_RANK
);
}
/* Register all fonts in this family. */
for
(
int
f
=
0
;
f
<
fontList
.
length
;
f
++)
{
String
fontNameLC
=
fontList
[
f
].
toLowerCase
(
Locale
.
ENGLISH
);
String
fileName
=
fontToFileMap
.
get
(
fontNameLC
);
if
(
fontFile
!=
null
&&
fontFile
.
equals
(
fileName
))
{
continue
;
}
/* Currently this code only looks for TrueType fonts, so format
* and rank can be specified without looking at the filename.
*/
registerFontFile
(
getPathName
(
fileName
),
null
,
FONTFORMAT_TRUETYPE
,
false
,
Font2D
.
TTF_RANK
);
}
Font2D
font
=
null
;
FontFamily
fontFamily
=
FontFamily
.
getFamily
(
familyName
);
/* Handle case where request "MyFont Bold", style=Font.ITALIC */
if
(
physicalFont
!=
null
)
{
style
|=
physicalFont
.
style
;
}
if
(
fontFamily
!=
null
)
{
font
=
fontFamily
.
getFont
(
style
);
if
(
font
==
null
)
{
font
=
fontFamily
.
getClosestStyle
(
style
);
}
}
return
font
;
}
private
ConcurrentHashMap
<
String
,
Font2D
>
fontNameCache
=
new
ConcurrentHashMap
<
String
,
Font2D
>();
/*
* The client supplies a name and a style.
* The name could be a family name, or a full name.
* A font may exist with the specified style, or it may
* exist only in some other style. For non-native fonts the scaler
* may be able to emulate the required style.
*/
public
Font2D
findFont2D
(
String
name
,
int
style
,
int
fallback
)
{
String
lowerCaseName
=
name
.
toLowerCase
(
Locale
.
ENGLISH
);
String
mapName
=
lowerCaseName
+
dotStyleStr
(
style
);
Font2D
font
;
/* If preferLocaleFonts() or preferProportionalFonts() has been
* called we may be using an alternate set of composite fonts in this
* app context. The presence of a pre-built name map indicates whether
* this is so, and gives access to the alternate composite for the
* name.
*/
if
(
_usingPerAppContextComposites
)
{
ConcurrentHashMap
<
String
,
Font2D
>
altNameCache
=
(
ConcurrentHashMap
<
String
,
Font2D
>)
AppContext
.
getAppContext
().
get
(
CompositeFont
.
class
);
if
(
altNameCache
!=
null
)
{
font
=
(
Font2D
)
altNameCache
.
get
(
mapName
);
}
else
{
font
=
null
;
}
}
else
{
font
=
fontNameCache
.
get
(
mapName
);
}
if
(
font
!=
null
)
{
return
font
;
}
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
().
info
(
"Search for font: "
+
name
);
}
// The check below is just so that the bitmap fonts being set by
// AWT and Swing thru the desktop properties do not trigger the
// the load fonts case. The two bitmap fonts are now mapped to
// appropriate equivalents for serif and sansserif.
// Note that the cost of this comparison is only for the first
// call until the map is filled.
if
(
FontUtilities
.
isWindows
)
{
if
(
lowerCaseName
.
equals
(
"ms sans serif"
))
{
name
=
"sansserif"
;
}
else
if
(
lowerCaseName
.
equals
(
"ms serif"
))
{
name
=
"serif"
;
}
}
/* This isn't intended to support a client passing in the
* string default, but if a client passes in null for the name
* the java.awt.Font class internally substitutes this name.
* So we need to recognise it here to prevent a loadFonts
* on the unrecognised name. The only potential problem with
* this is it would hide any real font called "default"!
* But that seems like a potential problem we can ignore for now.
*/
if
(
lowerCaseName
.
equals
(
"default"
))
{
name
=
"dialog"
;
}
/* First see if its a family name. */
FontFamily
family
=
FontFamily
.
getFamily
(
name
);
if
(
family
!=
null
)
{
font
=
family
.
getFontWithExactStyleMatch
(
style
);
if
(
font
==
null
)
{
font
=
findDeferredFont
(
name
,
style
);
}
if
(
font
==
null
)
{
font
=
family
.
getFont
(
style
);
}
if
(
font
==
null
)
{
font
=
family
.
getClosestStyle
(
style
);
}
if
(
font
!=
null
)
{
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
}
/* If it wasn't a family name, it should be a full name of
* either a composite, or a physical font
*/
font
=
fullNameToFont
.
get
(
lowerCaseName
);
if
(
font
!=
null
)
{
/* Check that the requested style matches the matched font's style.
* But also match style automatically if the requested style is
* "plain". This because the existing behaviour is that the fonts
* listed via getAllFonts etc always list their style as PLAIN.
* This does lead to non-commutative behaviours where you might
* start with "Lucida Sans Regular" and ask for a BOLD version
* and get "Lucida Sans DemiBold" but if you ask for the PLAIN
* style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold".
* This consistent however with what happens if you have a bold
* version of a font and no plain version exists - alg. styling
* doesn't "unbolden" the font.
*/
if
(
font
.
style
==
style
||
style
==
Font
.
PLAIN
)
{
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
else
{
/* If it was a full name like "Lucida Sans Regular", but
* the style requested is "bold", then we want to see if
* there's the appropriate match against another font in
* that family before trying to load all fonts, or applying a
* algorithmic styling
*/
family
=
FontFamily
.
getFamily
(
font
.
getFamilyName
(
null
));
if
(
family
!=
null
)
{
Font2D
familyFont
=
family
.
getFont
(
style
|
font
.
style
);
/* We exactly matched the requested style, use it! */
if
(
familyFont
!=
null
)
{
fontNameCache
.
put
(
mapName
,
familyFont
);
return
familyFont
;
}
else
{
/* This next call is designed to support the case
* where bold italic is requested, and if we must
* style, then base it on either bold or italic -
* not on plain!
*/
familyFont
=
family
.
getClosestStyle
(
style
|
font
.
style
);
if
(
familyFont
!=
null
)
{
/* The next check is perhaps one
* that shouldn't be done. ie if we get this
* far we have probably as close a match as we
* are going to get. We could load all fonts to
* see if somehow some parts of the family are
* loaded but not all of it.
*/
if
(
familyFont
.
canDoStyle
(
style
|
font
.
style
))
{
fontNameCache
.
put
(
mapName
,
familyFont
);
return
familyFont
;
}
}
}
}
}
}
if
(
FontUtilities
.
isWindows
)
{
/* Don't want Windows to return a Lucida Sans font from
* C:\Windows\Fonts
*/
if
(
deferredFontFiles
.
size
()
>
0
)
{
font
=
findJREDeferredFont
(
lowerCaseName
,
style
);
if
(
font
!=
null
)
{
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
}
font
=
findFontFromPlatform
(
lowerCaseName
,
style
);
if
(
font
!=
null
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Found font via platform API for request:\""
+
name
+
"\":, style="
+
style
+
" found font: "
+
font
);
}
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
}
/* If reach here and no match has been located, then if there are
* uninitialised deferred fonts, load as many of those as needed
* to find the deferred font. If none is found through that
* search continue on.
* There is possibly a minor issue when more than one
* deferred font implements the same font face. Since deferred
* fonts are only those in font configuration files, this is a
* controlled situation, the known case being Solaris euro_fonts
* versions of Arial, Times New Roman, Courier New. However
* the larger font will transparently replace the smaller one
* - see addToFontList() - when it is needed by the composite font.
*/
if
(
deferredFontFiles
.
size
()
>
0
)
{
font
=
findDeferredFont
(
name
,
style
);
if
(
font
!=
null
)
{
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
}
/* Some apps use deprecated 1.0 names such as helvetica and courier. On
* Solaris these are Type1 fonts in /usr/openwin/lib/X11/fonts/Type1.
* If running on Solaris will register all the fonts in this
* directory.
* May as well register the whole directory without actually testing
* the font name is one of the deprecated names as the next step would
* load all fonts which are in this directory anyway.
* In the event that this lookup is successful it potentially "hides"
* TrueType versions of such fonts that are elsewhere but since they
* do not exist on Solaris this is not a problem.
* Set a flag to indicate we've done this registration to avoid
* repetition and more seriously, to avoid recursion.
*/
if
(
FontUtilities
.
isSolaris
&&!
loaded1dot0Fonts
)
{
/* "timesroman" is a special case since that's not the
* name of any known font on Solaris or elsewhere.
*/
if
(
lowerCaseName
.
equals
(
"timesroman"
))
{
font
=
findFont2D
(
"serif"
,
style
,
fallback
);
fontNameCache
.
put
(
mapName
,
font
);
}
register1dot0Fonts
();
loaded1dot0Fonts
=
true
;
Font2D
ff
=
findFont2D
(
name
,
style
,
fallback
);
return
ff
;
}
/* We check for application registered fonts before
* explicitly loading all fonts as if necessary the registration
* code will have done so anyway. And we don't want to needlessly
* load the actual files for all fonts.
* Just as for installed fonts we check for family before fullname.
* We do not add these fonts to fontNameCache for the
* app context case which eliminates the overhead of a per context
* cache for these.
*/
if
(
fontsAreRegistered
||
fontsAreRegisteredPerAppContext
)
{
Hashtable
<
String
,
FontFamily
>
familyTable
=
null
;
Hashtable
<
String
,
Font2D
>
nameTable
;
if
(
fontsAreRegistered
)
{
familyTable
=
createdByFamilyName
;
nameTable
=
createdByFullName
;
}
else
{
AppContext
appContext
=
AppContext
.
getAppContext
();
familyTable
=
(
Hashtable
<
String
,
FontFamily
>)
appContext
.
get
(
regFamilyKey
);
nameTable
=
(
Hashtable
<
String
,
Font2D
>)
appContext
.
get
(
regFullNameKey
);
}
family
=
familyTable
.
get
(
lowerCaseName
);
if
(
family
!=
null
)
{
font
=
family
.
getFontWithExactStyleMatch
(
style
);
if
(
font
==
null
)
{
font
=
family
.
getFont
(
style
);
}
if
(
font
==
null
)
{
font
=
family
.
getClosestStyle
(
style
);
}
if
(
font
!=
null
)
{
if
(
fontsAreRegistered
)
{
fontNameCache
.
put
(
mapName
,
font
);
}
return
font
;
}
}
font
=
nameTable
.
get
(
lowerCaseName
);
if
(
font
!=
null
)
{
if
(
fontsAreRegistered
)
{
fontNameCache
.
put
(
mapName
,
font
);
}
return
font
;
}
}
/* If reach here and no match has been located, then if all fonts
* are not yet loaded, do so, and then recurse.
*/
if
(!
loadedAllFonts
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Load fonts looking for:"
+
name
);
}
loadFonts
();
loadedAllFonts
=
true
;
return
findFont2D
(
name
,
style
,
fallback
);
}
if
(!
loadedAllFontFiles
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Load font files looking for:"
+
name
);
}
loadFontFiles
();
loadedAllFontFiles
=
true
;
return
findFont2D
(
name
,
style
,
fallback
);
}
/* The primary name is the locale default - ie not US/English but
* whatever is the default in this locale. This is the way it always
* has been but may be surprising to some developers if "Arial Regular"
* were hard-coded in their app and yet "Arial Regular" was not the
* default name. Fortunately for them, as a consequence of the JDK
* supporting returning names and family names for arbitrary locales,
* we also need to support searching all localised names for a match.
* But because this case of the name used to reference a font is not
* the same as the default for this locale is rare, it makes sense to
* search a much shorter list of default locale names and only go to
* a longer list of names in the event that no match was found.
* So add here code which searches localised names too.
* As in 1.4.x this happens only after loading all fonts, which
* is probably the right order.
*/
if
((
font
=
findFont2DAllLocales
(
name
,
style
))
!=
null
)
{
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
/* Perhaps its a "compatibility" name - timesroman, helvetica,
* or courier, which 1.0 apps used for logical fonts.
* We look for these "late" after a loadFonts as we must not
* hide real fonts of these names.
* Map these appropriately:
* On windows this means according to the rules specified by the
* FontConfiguration : do it only for encoding==Cp1252
*
* REMIND: this is something we plan to remove.
*/
if
(
FontUtilities
.
isWindows
)
{
String
compatName
=
getFontConfiguration
().
getFallbackFamilyName
(
name
,
null
);
if
(
compatName
!=
null
)
{
font
=
findFont2D
(
compatName
,
style
,
fallback
);
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
}
else
if
(
lowerCaseName
.
equals
(
"timesroman"
))
{
font
=
findFont2D
(
"serif"
,
style
,
fallback
);
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
else
if
(
lowerCaseName
.
equals
(
"helvetica"
))
{
font
=
findFont2D
(
"sansserif"
,
style
,
fallback
);
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
else
if
(
lowerCaseName
.
equals
(
"courier"
))
{
font
=
findFont2D
(
"monospaced"
,
style
,
fallback
);
fontNameCache
.
put
(
mapName
,
font
);
return
font
;
}
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
().
info
(
"No font found for:"
+
name
);
}
switch
(
fallback
)
{
case
PHYSICAL_FALLBACK:
return
getDefaultPhysicalFont
();
case
LOGICAL_FALLBACK:
return
getDefaultLogicalFont
(
style
);
default
:
return
null
;
}
}
/*
* Workaround for apps which are dependent on a font metrics bug
* in JDK 1.1. This is an unsupported win32 private setting.
* Left in for a customer - do not remove.
*/
public
boolean
usePlatformFontMetrics
()
{
return
usePlatformFontMetrics
;
}
public
int
getNumFonts
()
{
return
physicalFonts
.
size
()+
maxCompFont
;
}
private
static
boolean
fontSupportsEncoding
(
Font
font
,
String
encoding
)
{
return
FontUtilities
.
getFont2D
(
font
).
supportsEncoding
(
encoding
);
}
public
abstract
String
getFontPath
(
boolean
noType1Fonts
);
private
Thread
fileCloser
=
null
;
Vector
<
File
>
tmpFontFiles
=
null
;
public
Font2D
createFont2D
(
File
fontFile
,
int
fontFormat
,
boolean
isCopy
,
CreatedFontTracker
tracker
)
throws
FontFormatException
{
String
fontFilePath
=
fontFile
.
getPath
();
FileFont
font2D
=
null
;
final
File
fFile
=
fontFile
;
final
CreatedFontTracker
_tracker
=
tracker
;
try
{
switch
(
fontFormat
)
{
case
Font
.
TRUETYPE_FONT
:
font2D
=
new
TrueTypeFont
(
fontFilePath
,
null
,
0
,
true
);
break
;
case
Font
.
TYPE1_FONT
:
font2D
=
new
Type1Font
(
fontFilePath
,
null
,
isCopy
);
break
;
default
:
throw
new
FontFormatException
(
"Unrecognised Font Format"
);
}
}
catch
(
FontFormatException
e
)
{
if
(
isCopy
)
{
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
if
(
_tracker
!=
null
)
{
_tracker
.
subBytes
((
int
)
fFile
.
length
());
}
fFile
.
delete
();
return
null
;
}
});
}
throw
(
e
);
}
if
(
isCopy
)
{
font2D
.
setFileToRemove
(
fontFile
,
tracker
);
synchronized
(
FontManager
.
class
)
{
if
(
tmpFontFiles
==
null
)
{
tmpFontFiles
=
new
Vector
<
File
>();
}
tmpFontFiles
.
add
(
fontFile
);
if
(
fileCloser
==
null
)
{
final
Runnable
fileCloserRunnable
=
new
Runnable
()
{
public
void
run
()
{
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
for
(
int
i
=
0
;
i
<
CHANNELPOOLSIZE
;
i
++)
{
if
(
fontFileCache
[
i
]
!=
null
)
{
try
{
fontFileCache
[
i
].
close
();
}
catch
(
Exception
e
)
{
}
}
}
if
(
tmpFontFiles
!=
null
)
{
File
[]
files
=
new
File
[
tmpFontFiles
.
size
()];
files
=
tmpFontFiles
.
toArray
(
files
);
for
(
int
f
=
0
;
f
<
files
.
length
;
f
++)
{
try
{
files
[
f
].
delete
();
}
catch
(
Exception
e
)
{
}
}
}
return
null
;
}
});
}
};
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
/* The thread must be a member of a thread group
* which will not get GCed before VM exit.
* Make its parent the top-level thread group.
*/
ThreadGroup
tg
=
Thread
.
currentThread
().
getThreadGroup
();
for
(
ThreadGroup
tgn
=
tg
;
tgn
!=
null
;
tg
=
tgn
,
tgn
=
tg
.
getParent
());
fileCloser
=
new
Thread
(
tg
,
fileCloserRunnable
);
Runtime
.
getRuntime
().
addShutdownHook
(
fileCloser
);
return
null
;
}
});
}
}
}
return
font2D
;
}
/* remind: used in X11GraphicsEnvironment and called often enough
* that we ought to obsolete this code
*/
public
synchronized
String
getFullNameByFileName
(
String
fileName
)
{
PhysicalFont
[]
physFonts
=
getPhysicalFonts
();
for
(
int
i
=
0
;
i
<
physFonts
.
length
;
i
++)
{
if
(
physFonts
[
i
].
platName
.
equals
(
fileName
))
{
return
(
physFonts
[
i
].
getFontName
(
null
));
}
}
return
null
;
}
/*
* This is called when font is determined to be invalid/bad.
* It designed to be called (for example) by the font scaler
* when in processing a font file it is discovered to be incorrect.
* This is different than the case where fonts are discovered to
* be incorrect during initial verification, as such fonts are
* never registered.
* Handles to this font held are re-directed to a default font.
* This default may not be an ideal substitute buts it better than
* crashing This code assumes a PhysicalFont parameter as it doesn't
* make sense for a Composite to be "bad".
*/
public
synchronized
void
deRegisterBadFont
(
Font2D
font2D
)
{
if
(!(
font2D
instanceof
PhysicalFont
))
{
/* We should never reach here, but just in case */
return
;
}
else
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
severe
(
"Deregister bad font: "
+
font2D
);
}
replaceFont
((
PhysicalFont
)
font2D
,
getDefaultPhysicalFont
());
}
}
/*
* This encapsulates all the work that needs to be done when a
* Font2D is replaced by a different Font2D.
*/
public
synchronized
void
replaceFont
(
PhysicalFont
oldFont
,
PhysicalFont
newFont
)
{
if
(
oldFont
.
handle
.
font2D
!=
oldFont
)
{
/* already done */
return
;
}
/* If we try to replace the font with itself, that won't work,
* so pick any alternative physical font
*/
if
(
oldFont
==
newFont
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
severe
(
"Can't replace bad font with itself "
+
oldFont
);
}
PhysicalFont
[]
physFonts
=
getPhysicalFonts
();
for
(
int
i
=
0
;
i
<
physFonts
.
length
;
i
++)
{
if
(
physFonts
[
i
]
!=
newFont
)
{
newFont
=
physFonts
[
i
];
break
;
}
}
if
(
oldFont
==
newFont
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
severe
(
"This is bad. No good physicalFonts found."
);
}
return
;
}
}
/* eliminate references to this font, so it won't be located
* by future callers, and will be eligible for GC when all
* references are removed
*/
oldFont
.
handle
.
font2D
=
newFont
;
physicalFonts
.
remove
(
oldFont
.
fullName
);
fullNameToFont
.
remove
(
oldFont
.
fullName
.
toLowerCase
(
Locale
.
ENGLISH
));
FontFamily
.
remove
(
oldFont
);
if
(
localeFullNamesToFont
!=
null
)
{
Map
.
Entry
[]
mapEntries
=
(
Map
.
Entry
[])
localeFullNamesToFont
.
entrySet
().
toArray
(
new
Map
.
Entry
[
0
]);
/* Should I be replacing these, or just I just remove
* the names from the map?
*/
for
(
int
i
=
0
;
i
<
mapEntries
.
length
;
i
++)
{
if
(
mapEntries
[
i
].
getValue
()
==
oldFont
)
{
try
{
mapEntries
[
i
].
setValue
(
newFont
);
}
catch
(
Exception
e
)
{
/* some maps don't support this operation.
* In this case just give up and remove the entry.
*/
localeFullNamesToFont
.
remove
(
mapEntries
[
i
].
getKey
());
}
}
}
}
for
(
int
i
=
0
;
i
<
maxCompFont
;
i
++)
{
/* Deferred initialization of composites shouldn't be
* a problem for this case, since a font must have been
* initialised to be discovered to be bad.
* Some JRE composites on Solaris use two versions of the same
* font. The replaced font isn't bad, just "smaller" so there's
* no need to make the slot point to the new font.
* Since composites have a direct reference to the Font2D (not
* via a handle) making this substitution is not safe and could
* cause an additional problem and so this substitution is
* warranted only when a font is truly "bad" and could cause
* a crash. So we now replace it only if its being substituted
* with some font other than a fontconfig rank font
* Since in practice a substitution will have the same rank
* this may never happen, but the code is safer even if its
* also now a no-op.
* The only obvious "glitch" from this stems from the current
* implementation that when asked for the number of glyphs in a
* composite it lies and returns the number in slot 0 because
* composite glyphs aren't contiguous. Since we live with that
* we can live with the glitch that depending on how it was
* initialised a composite may return different values for this.
* Fixing the issues with composite glyph ids is tricky as
* there are exclusion ranges and unlike other fonts even the
* true "numGlyphs" isn't a contiguous range. Likely the only
* solution is an API that returns an array of glyph ranges
* which takes precedence over the existing API. That might
* also need to address excluding ranges which represent a
* code point supported by an earlier component.
*/
if
(
newFont
.
getRank
()
>
Font2D
.
FONT_CONFIG_RANK
)
{
compFonts
[
i
].
replaceComponentFont
(
oldFont
,
newFont
);
}
}
}
private
synchronized
void
loadLocaleNames
()
{
if
(
localeFullNamesToFont
!=
null
)
{
return
;
}
localeFullNamesToFont
=
new
HashMap
<
String
,
TrueTypeFont
>();
Font2D
[]
fonts
=
getRegisteredFonts
();
for
(
int
i
=
0
;
i
<
fonts
.
length
;
i
++)
{
if
(
fonts
[
i
]
instanceof
TrueTypeFont
)
{
TrueTypeFont
ttf
=
(
TrueTypeFont
)
fonts
[
i
];
String
[]
fullNames
=
ttf
.
getAllFullNames
();
for
(
int
n
=
0
;
n
<
fullNames
.
length
;
n
++)
{
localeFullNamesToFont
.
put
(
fullNames
[
n
],
ttf
);
}
FontFamily
family
=
FontFamily
.
getFamily
(
ttf
.
familyName
);
if
(
family
!=
null
)
{
FontFamily
.
addLocaleNames
(
family
,
ttf
.
getAllFamilyNames
());
}
}
}
}
/* This replicate the core logic of findFont2D but operates on
* all the locale names. This hasn't been merged into findFont2D to
* keep the logic simpler and reduce overhead, since this case is
* almost never used. The main case in which it is called is when
* a bogus font name is used and we need to check all possible names
* before returning the default case.
*/
private
Font2D
findFont2DAllLocales
(
String
name
,
int
style
)
{
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"Searching localised font names for:"
+
name
);
}
/* If reach here and no match has been located, then if we have
* not yet built the map of localeFullNamesToFont for TT fonts, do so
* now. This method must be called after all fonts have been loaded.
*/
if
(
localeFullNamesToFont
==
null
)
{
loadLocaleNames
();
}
String
lowerCaseName
=
name
.
toLowerCase
();
Font2D
font
=
null
;
/* First see if its a family name. */
FontFamily
family
=
FontFamily
.
getLocaleFamily
(
lowerCaseName
);
if
(
family
!=
null
)
{
font
=
family
.
getFont
(
style
);
if
(
font
==
null
)
{
font
=
family
.
getClosestStyle
(
style
);
}
if
(
font
!=
null
)
{
return
font
;
}
}
/* If it wasn't a family name, it should be a full name. */
synchronized
(
this
)
{
font
=
localeFullNamesToFont
.
get
(
name
);
}
if
(
font
!=
null
)
{
if
(
font
.
style
==
style
||
style
==
Font
.
PLAIN
)
{
return
font
;
}
else
{
family
=
FontFamily
.
getFamily
(
font
.
getFamilyName
(
null
));
if
(
family
!=
null
)
{
Font2D
familyFont
=
family
.
getFont
(
style
);
/* We exactly matched the requested style, use it! */
if
(
familyFont
!=
null
)
{
return
familyFont
;
}
else
{
familyFont
=
family
.
getClosestStyle
(
style
);
if
(
familyFont
!=
null
)
{
/* The next check is perhaps one
* that shouldn't be done. ie if we get this
* far we have probably as close a match as we
* are going to get. We could load all fonts to
* see if somehow some parts of the family are
* loaded but not all of it.
* This check is commented out for now.
*/
if
(!
familyFont
.
canDoStyle
(
style
))
{
familyFont
=
null
;
}
return
familyFont
;
}
}
}
}
}
return
font
;
}
/* Supporting "alternate" composite fonts on 2D graphics objects
* is accessed by the application by calling methods on the local
* GraphicsEnvironment. The overall implementation is described
* in one place, here, since otherwise the implementation is spread
* around it may be difficult to track.
* The methods below call into SunGraphicsEnvironment which creates a
* new FontConfiguration instance. The FontConfiguration class,
* and its platform sub-classes are updated to take parameters requesting
* these behaviours. This is then used to create new composite font
* instances. Since this calls the initCompositeFont method in
* SunGraphicsEnvironment it performs the same initialization as is
* performed normally. There may be some duplication of effort, but
* that code is already written to be able to perform properly if called
* to duplicate work. The main difference is that if we detect we are
* running in an applet/browser/Java plugin environment these new fonts
* are not placed in the "default" maps but into an AppContext instance.
* The font lookup mechanism in java.awt.Font.getFont2D() is also updated
* so that look-up for composite fonts will in that case always
* do a lookup rather than returning a cached result.
* This is inefficient but necessary else singleton java.awt.Font
* instances would not retrieve the correct Font2D for the appcontext.
* sun.font.FontManager.findFont2D is also updated to that it uses
* a name map cache specific to that appcontext.
*
* Getting an AppContext is expensive, so there is a global variable
* that records whether these methods have ever been called and can
* avoid the expense for almost all applications. Once the correct
* CompositeFont is associated with the Font, everything should work
* through existing mechanisms.
* A special case is that GraphicsEnvironment.getAllFonts() must
* return an AppContext specific list.
*
* Calling the methods below is "heavyweight" but it is expected that
* these methods will be called very rarely.
*
* If _usingPerAppContextComposites is true, we are in "applet"
* (eg browser) enviroment and at least one context has selected
* an alternate composite font behaviour.
* If _usingAlternateComposites is true, we are not in an "applet"
* environment and the (single) application has selected
* an alternate composite font behaviour.
*
* - Printing: The implementation delegates logical fonts to an AWT
* mechanism which cannot use these alternate configurations.
* We can detect that alternate fonts are in use and back-off to 2D, but
* that uses outlines. Much of this can be fixed with additional work
* but that may have to wait. The results should be correct, just not
* optimal.
*/
private
static
final
Object
altJAFontKey
=
new
Object
();
private
static
final
Object
localeFontKey
=
new
Object
();
private
static
final
Object
proportionalFontKey
=
new
Object
();
private
boolean
_usingPerAppContextComposites
=
false
;
private
boolean
_usingAlternateComposites
=
false
;
/* These values are used only if we are running as a standalone
* application, as determined by maybeMultiAppContext();
*/
private
static
boolean
gAltJAFont
=
false
;
private
boolean
gLocalePref
=
false
;
private
boolean
gPropPref
=
false
;
/* This method doesn't check if alternates are selected in this app
* context. Its used by the FontMetrics caching code which in such
* a case cannot retrieve a cached metrics solely on the basis of
* the Font.equals() method since it needs to also check if the Font2D
* is the same.
* We also use non-standard composites for Swing native L&F fonts on
* Windows. In that case the policy is that the metrics reported are
* based solely on the physical font in the first slot which is the
* visible java.awt.Font. So in that case the metrics cache which tests
* the Font does what we want. In the near future when we expand the GTK
* logical font definitions we may need to revisit this if GTK reports
* combined metrics instead. For now though this test can be simple.
*/
public
boolean
maybeUsingAlternateCompositeFonts
()
{
return
_usingAlternateComposites
||
_usingPerAppContextComposites
;
}
public
boolean
usingAlternateCompositeFonts
()
{
return
(
_usingAlternateComposites
||
(
_usingPerAppContextComposites
&&
AppContext
.
getAppContext
().
get
(
CompositeFont
.
class
)
!=
null
));
}
private
static
boolean
maybeMultiAppContext
()
{
Boolean
appletSM
=
(
Boolean
)
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
SecurityManager
sm
=
System
.
getSecurityManager
();
return
new
Boolean
(
sm
instanceof
sun
.
applet
.
AppletSecurity
);
}
});
return
appletSM
.
booleanValue
();
}
/* Modifies the behaviour of a subsequent call to preferLocaleFonts()
* to use Mincho instead of Gothic for dialoginput in JA locales
* on windows. Not needed on other platforms.
*/
public
synchronized
void
useAlternateFontforJALocales
()
{
if
(!
FontUtilities
.
isWindows
)
{
return
;
}
if
(!
maybeMultiAppContext
())
{
gAltJAFont
=
true
;
}
else
{
AppContext
appContext
=
AppContext
.
getAppContext
();
appContext
.
put
(
altJAFontKey
,
altJAFontKey
);
}
}
public
boolean
usingAlternateFontforJALocales
()
{
if
(!
maybeMultiAppContext
())
{
return
gAltJAFont
;
}
else
{
AppContext
appContext
=
AppContext
.
getAppContext
();
return
appContext
.
get
(
altJAFontKey
)
==
altJAFontKey
;
}
}
public
synchronized
void
preferLocaleFonts
()
{
/* Test if re-ordering will have any effect */
if
(!
FontConfiguration
.
willReorderForStartupLocale
())
{
return
;
}
if
(!
maybeMultiAppContext
())
{
if
(
gLocalePref
==
true
)
{
return
;
}
gLocalePref
=
true
;
createCompositeFonts
(
fontNameCache
,
gLocalePref
,
gPropPref
);
_usingAlternateComposites
=
true
;
}
else
{
AppContext
appContext
=
AppContext
.
getAppContext
();
if
(
appContext
.
get
(
localeFontKey
)
==
localeFontKey
)
{
return
;
}
appContext
.
put
(
localeFontKey
,
localeFontKey
);
boolean
acPropPref
=
appContext
.
get
(
proportionalFontKey
)
==
proportionalFontKey
;
ConcurrentHashMap
<
String
,
Font2D
>
altNameCache
=
new
ConcurrentHashMap
<
String
,
Font2D
>
();
/* If there is an existing hashtable, we can drop it. */
appContext
.
put
(
CompositeFont
.
class
,
altNameCache
);
_usingPerAppContextComposites
=
true
;
createCompositeFonts
(
altNameCache
,
true
,
acPropPref
);
}
}
public
synchronized
void
preferProportionalFonts
()
{
/* If no proportional fonts are configured, there's no need
* to take any action.
*/
if
(!
FontConfiguration
.
hasMonoToPropMap
())
{
return
;
}
if
(!
maybeMultiAppContext
())
{
if
(
gPropPref
==
true
)
{
return
;
}
gPropPref
=
true
;
createCompositeFonts
(
fontNameCache
,
gLocalePref
,
gPropPref
);
_usingAlternateComposites
=
true
;
}
else
{
AppContext
appContext
=
AppContext
.
getAppContext
();
if
(
appContext
.
get
(
proportionalFontKey
)
==
proportionalFontKey
)
{
return
;
}
appContext
.
put
(
proportionalFontKey
,
proportionalFontKey
);
boolean
acLocalePref
=
appContext
.
get
(
localeFontKey
)
==
localeFontKey
;
ConcurrentHashMap
<
String
,
Font2D
>
altNameCache
=
new
ConcurrentHashMap
<
String
,
Font2D
>
();
/* If there is an existing hashtable, we can drop it. */
appContext
.
put
(
CompositeFont
.
class
,
altNameCache
);
_usingPerAppContextComposites
=
true
;
createCompositeFonts
(
altNameCache
,
acLocalePref
,
true
);
}
}
private
static
HashSet
<
String
>
installedNames
=
null
;
private
static
HashSet
<
String
>
getInstalledNames
()
{
if
(
installedNames
==
null
)
{
Locale
l
=
getSystemStartupLocale
();
SunFontManager
fontManager
=
SunFontManager
.
getInstance
();
String
[]
installedFamilies
=
fontManager
.
getInstalledFontFamilyNames
(
l
);
Font
[]
installedFonts
=
fontManager
.
getAllInstalledFonts
();
HashSet
<
String
>
names
=
new
HashSet
<
String
>();
for
(
int
i
=
0
;
i
<
installedFamilies
.
length
;
i
++)
{
names
.
add
(
installedFamilies
[
i
].
toLowerCase
(
l
));
}
for
(
int
i
=
0
;
i
<
installedFonts
.
length
;
i
++)
{
names
.
add
(
installedFonts
[
i
].
getFontName
(
l
).
toLowerCase
(
l
));
}
installedNames
=
names
;
}
return
installedNames
;
}
/* Keys are used to lookup per-AppContext Hashtables */
private
static
final
Object
regFamilyKey
=
new
Object
();
private
static
final
Object
regFullNameKey
=
new
Object
();
private
Hashtable
<
String
,
FontFamily
>
createdByFamilyName
;
private
Hashtable
<
String
,
Font2D
>
createdByFullName
;
private
boolean
fontsAreRegistered
=
false
;
private
boolean
fontsAreRegisteredPerAppContext
=
false
;
public
boolean
registerFont
(
Font
font
)
{
/* This method should not be called with "null".
* It is the caller's responsibility to ensure that.
*/
if
(
font
==
null
)
{
return
false
;
}
/* Initialise these objects only once we start to use this API */
synchronized
(
regFamilyKey
)
{
if
(
createdByFamilyName
==
null
)
{
createdByFamilyName
=
new
Hashtable
<
String
,
FontFamily
>();
createdByFullName
=
new
Hashtable
<
String
,
Font2D
>();
}
}
if
(!
FontAccess
.
getFontAccess
().
isCreatedFont
(
font
))
{
return
false
;
}
/* We want to ensure that this font cannot override existing
* installed fonts. Check these conditions :
* - family name is not that of an installed font
* - full name is not that of an installed font
* - family name is not the same as the full name of an installed font
* - full name is not the same as the family name of an installed font
* The last two of these may initially look odd but the reason is
* that (unfortunately) Font constructors do not distinuguish these.
* An extreme example of such a problem would be a font which has
* family name "Dialog.Plain" and full name of "Dialog".
* The one arguably overly stringent restriction here is that if an
* application wants to supply a new member of an existing family
* It will get rejected. But since the JRE can perform synthetic
* styling in many cases its not necessary.
* We don't apply the same logic to registered fonts. If apps want
* to do this lets assume they have a reason. It won't cause problems
* except for themselves.
*/
HashSet
<
String
>
names
=
getInstalledNames
();
Locale
l
=
getSystemStartupLocale
();
String
familyName
=
font
.
getFamily
(
l
).
toLowerCase
();
String
fullName
=
font
.
getFontName
(
l
).
toLowerCase
();
if
(
names
.
contains
(
familyName
)
||
names
.
contains
(
fullName
))
{
return
false
;
}
/* Checks passed, now register the font */
Hashtable
<
String
,
FontFamily
>
familyTable
;
Hashtable
<
String
,
Font2D
>
fullNameTable
;
if
(!
maybeMultiAppContext
())
{
familyTable
=
createdByFamilyName
;
fullNameTable
=
createdByFullName
;
fontsAreRegistered
=
true
;
}
else
{
AppContext
appContext
=
AppContext
.
getAppContext
();
familyTable
=
(
Hashtable
<
String
,
FontFamily
>)
appContext
.
get
(
regFamilyKey
);
fullNameTable
=
(
Hashtable
<
String
,
Font2D
>)
appContext
.
get
(
regFullNameKey
);
if
(
familyTable
==
null
)
{
familyTable
=
new
Hashtable
<
String
,
FontFamily
>();
fullNameTable
=
new
Hashtable
<
String
,
Font2D
>();
appContext
.
put
(
regFamilyKey
,
familyTable
);
appContext
.
put
(
regFullNameKey
,
fullNameTable
);
}
fontsAreRegisteredPerAppContext
=
true
;
}
/* Create the FontFamily and add font to the tables */
Font2D
font2D
=
FontUtilities
.
getFont2D
(
font
);
int
style
=
font2D
.
getStyle
();
FontFamily
family
=
familyTable
.
get
(
familyName
);
if
(
family
==
null
)
{
family
=
new
FontFamily
(
font
.
getFamily
(
l
));
familyTable
.
put
(
familyName
,
family
);
}
/* Remove name cache entries if not using app contexts.
* To accommodate a case where code may have registered first a plain
* family member and then used it and is now registering a bold family
* member, we need to remove all members of the family, so that the
* new style can get picked up rather than continuing to synthesise.
*/
if
(
fontsAreRegistered
)
{
removeFromCache
(
family
.
getFont
(
Font
.
PLAIN
));
removeFromCache
(
family
.
getFont
(
Font
.
BOLD
));
removeFromCache
(
family
.
getFont
(
Font
.
ITALIC
));
removeFromCache
(
family
.
getFont
(
Font
.
BOLD
|
Font
.
ITALIC
));
removeFromCache
(
fullNameTable
.
get
(
fullName
));
}
family
.
setFont
(
font2D
,
style
);
fullNameTable
.
put
(
fullName
,
font2D
);
return
true
;
}
/* Remove from the name cache all references to the Font2D */
private
void
removeFromCache
(
Font2D
font
)
{
if
(
font
==
null
)
{
return
;
}
String
[]
keys
=
(
String
[])(
fontNameCache
.
keySet
().
toArray
(
STR_ARRAY
));
for
(
int
k
=
0
;
k
<
keys
.
length
;
k
++)
{
if
(
fontNameCache
.
get
(
keys
[
k
])
==
font
)
{
fontNameCache
.
remove
(
keys
[
k
]);
}
}
}
// It may look odd to use TreeMap but its more convenient to the caller.
public
TreeMap
<
String
,
String
>
getCreatedFontFamilyNames
()
{
Hashtable
<
String
,
FontFamily
>
familyTable
;
if
(
fontsAreRegistered
)
{
familyTable
=
createdByFamilyName
;
}
else
if
(
fontsAreRegisteredPerAppContext
)
{
AppContext
appContext
=
AppContext
.
getAppContext
();
familyTable
=
(
Hashtable
<
String
,
FontFamily
>)
appContext
.
get
(
regFamilyKey
);
}
else
{
return
null
;
}
Locale
l
=
getSystemStartupLocale
();
synchronized
(
familyTable
)
{
TreeMap
<
String
,
String
>
map
=
new
TreeMap
<
String
,
String
>();
for
(
FontFamily
f
:
familyTable
.
values
())
{
Font2D
font2D
=
f
.
getFont
(
Font
.
PLAIN
);
if
(
font2D
==
null
)
{
font2D
=
f
.
getClosestStyle
(
Font
.
PLAIN
);
}
String
name
=
font2D
.
getFamilyName
(
l
);
map
.
put
(
name
.
toLowerCase
(
l
),
name
);
}
return
map
;
}
}
public
Font
[]
getCreatedFonts
()
{
Hashtable
<
String
,
Font2D
>
nameTable
;
if
(
fontsAreRegistered
)
{
nameTable
=
createdByFullName
;
}
else
if
(
fontsAreRegisteredPerAppContext
)
{
AppContext
appContext
=
AppContext
.
getAppContext
();
nameTable
=
(
Hashtable
<
String
,
Font2D
>)
appContext
.
get
(
regFullNameKey
);
}
else
{
return
null
;
}
Locale
l
=
getSystemStartupLocale
();
synchronized
(
nameTable
)
{
Font
[]
fonts
=
new
Font
[
nameTable
.
size
()];
int
i
=
0
;
for
(
Font2D
font2D
:
nameTable
.
values
())
{
fonts
[
i
++]
=
new
Font
(
font2D
.
getFontName
(
l
),
Font
.
PLAIN
,
1
);
}
return
fonts
;
}
}
protected
String
[]
getPlatformFontDirs
(
boolean
noType1Fonts
)
{
String
path
=
getFontPath
(
true
);
StringTokenizer
parser
=
new
StringTokenizer
(
path
,
File
.
pathSeparator
);
ArrayList
<
String
>
pathList
=
new
ArrayList
<
String
>();
try
{
while
(
parser
.
hasMoreTokens
())
{
pathList
.
add
(
parser
.
nextToken
());
}
}
catch
(
NoSuchElementException
e
)
{
}
return
pathList
.
toArray
(
new
String
[
0
]);
}
/**
* Returns an array of two strings. The first element is the
* name of the font. The second element is the file name.
*/
public
abstract
String
[]
getDefaultPlatformFont
();
// Begin: Refactored from SunGraphicsEnviroment.
/*
* helper function for registerFonts
*/
private
void
addDirFonts
(
String
dirName
,
File
dirFile
,
FilenameFilter
filter
,
int
fontFormat
,
boolean
useJavaRasterizer
,
int
fontRank
,
boolean
defer
,
boolean
resolveSymLinks
)
{
String
[]
ls
=
dirFile
.
list
(
filter
);
if
(
ls
==
null
||
ls
.
length
==
0
)
{
return
;
}
String
[]
fontNames
=
new
String
[
ls
.
length
];
String
[][]
nativeNames
=
new
String
[
ls
.
length
][];
int
fontCount
=
0
;
for
(
int
i
=
0
;
i
<
ls
.
length
;
i
++
)
{
File
theFile
=
new
File
(
dirFile
,
ls
[
i
]);
String
fullName
=
null
;
if
(
resolveSymLinks
)
{
try
{
fullName
=
theFile
.
getCanonicalPath
();
}
catch
(
IOException
e
)
{
}
}
if
(
fullName
==
null
)
{
fullName
=
dirName
+
File
.
separator
+
ls
[
i
];
}
// REMIND: case compare depends on platform
if
(
registeredFontFiles
.
contains
(
fullName
))
{
continue
;
}
if
(
badFonts
!=
null
&&
badFonts
.
contains
(
fullName
))
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
warning
(
"skip bad font "
+
fullName
);
}
continue
;
// skip this font file.
}
registeredFontFiles
.
add
(
fullName
);
if
(
FontUtilities
.
debugFonts
()
&&
FontUtilities
.
getLogger
().
isLoggable
(
Level
.
INFO
))
{
String
message
=
"Registering font "
+
fullName
;
String
[]
natNames
=
getNativeNames
(
fullName
,
null
);
if
(
natNames
==
null
)
{
message
+=
" with no native name"
;
}
else
{
message
+=
" with native name(s) "
+
natNames
[
0
];
for
(
int
nn
=
1
;
nn
<
natNames
.
length
;
nn
++)
{
message
+=
", "
+
natNames
[
nn
];
}
}
FontUtilities
.
getLogger
().
info
(
message
);
}
fontNames
[
fontCount
]
=
fullName
;
nativeNames
[
fontCount
++]
=
getNativeNames
(
fullName
,
null
);
}
registerFonts
(
fontNames
,
nativeNames
,
fontCount
,
fontFormat
,
useJavaRasterizer
,
fontRank
,
defer
);
return
;
}
protected
String
[]
getNativeNames
(
String
fontFileName
,
String
platformName
)
{
return
null
;
}
/**
* Returns a file name for the physical font represented by this platform
* font name. The default implementation tries to obtain the file name
* from the font configuration.
* Subclasses may override to provide information from other sources.
*/
protected
String
getFileNameFromPlatformName
(
String
platformFontName
)
{
return
fontConfig
.
getFileNameFromPlatformName
(
platformFontName
);
}
/**
* Return the default font configuration.
*/
public
FontConfiguration
getFontConfiguration
()
{
return
fontConfig
;
}
/* A call to this method should be followed by a call to
* registerFontDirs(..)
*/
protected
String
getPlatformFontPath
(
boolean
noType1Font
)
{
if
(
fontPath
==
null
)
{
fontPath
=
getFontPath
(
noType1Font
);
}
return
fontPath
;
}
public
static
boolean
isOpenJDK
()
{
return
FontUtilities
.
isOpenJDK
;
}
protected
void
loadFonts
()
{
if
(
discoveredAllFonts
)
{
return
;
}
/* Use lock specific to the font system */
synchronized
(
lucidaFontName
)
{
if
(
FontUtilities
.
debugFonts
())
{
Thread
.
dumpStack
();
FontUtilities
.
getLogger
()
.
info
(
"SunGraphicsEnvironment.loadFonts() called"
);
}
initialiseDeferredFonts
();
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
if
(
fontPath
==
null
)
{
fontPath
=
getPlatformFontPath
(
noType1Font
);
registerFontDirs
(
fontPath
);
}
if
(
fontPath
!=
null
)
{
// this will find all fonts including those already
// registered. But we have checks in place to prevent
// double registration.
if
(!
gotFontsFromPlatform
())
{
registerFontsOnPath
(
fontPath
,
false
,
Font2D
.
UNKNOWN_RANK
,
false
,
true
);
loadedAllFontFiles
=
true
;
}
}
registerOtherFontFiles
(
registeredFontFiles
);
discoveredAllFonts
=
true
;
return
null
;
}
});
}
}
protected
void
registerFontDirs
(
String
pathName
)
{
return
;
}
private
void
registerFontsOnPath
(
String
pathName
,
boolean
useJavaRasterizer
,
int
fontRank
,
boolean
defer
,
boolean
resolveSymLinks
)
{
StringTokenizer
parser
=
new
StringTokenizer
(
pathName
,
File
.
pathSeparator
);
try
{
while
(
parser
.
hasMoreTokens
())
{
registerFontsInDir
(
parser
.
nextToken
(),
useJavaRasterizer
,
fontRank
,
defer
,
resolveSymLinks
);
}
}
catch
(
NoSuchElementException
e
)
{
}
}
/* Called to register fall back fonts */
public
void
registerFontsInDir
(
String
dirName
)
{
registerFontsInDir
(
dirName
,
true
,
Font2D
.
JRE_RANK
,
true
,
false
);
}
private
void
registerFontsInDir
(
String
dirName
,
boolean
useJavaRasterizer
,
int
fontRank
,
boolean
defer
,
boolean
resolveSymLinks
)
{
File
pathFile
=
new
File
(
dirName
);
addDirFonts
(
dirName
,
pathFile
,
ttFilter
,
FONTFORMAT_TRUETYPE
,
useJavaRasterizer
,
fontRank
==
Font2D
.
UNKNOWN_RANK
?
Font2D
.
TTF_RANK
:
fontRank
,
defer
,
resolveSymLinks
);
addDirFonts
(
dirName
,
pathFile
,
t1Filter
,
FONTFORMAT_TYPE1
,
useJavaRasterizer
,
fontRank
==
Font2D
.
UNKNOWN_RANK
?
Font2D
.
TYPE1_RANK
:
fontRank
,
defer
,
resolveSymLinks
);
}
protected
void
registerFontDir
(
String
path
)
{
}
/**
* Returns file name for default font, either absolute
* or relative as needed by registerFontFile.
*/
public
synchronized
String
getDefaultFontFile
()
{
if
(
defaultFontFileName
==
null
)
{
initDefaultFonts
();
}
return
defaultFontFileName
;
}
private
void
initDefaultFonts
()
{
if
(!
isOpenJDK
())
{
defaultFontName
=
lucidaFontName
;
if
(
useAbsoluteFontFileNames
())
{
defaultFontFileName
=
jreFontDirName
+
File
.
separator
+
FontUtilities
.
LUCIDA_FILE_NAME
;
}
else
{
defaultFontFileName
=
FontUtilities
.
LUCIDA_FILE_NAME
;
}
}
}
/**
* Whether registerFontFile expects absolute or relative
* font file names.
*/
protected
boolean
useAbsoluteFontFileNames
()
{
return
true
;
}
/**
* Creates this environment's FontConfiguration.
*/
protected
abstract
FontConfiguration
createFontConfiguration
();
public
abstract
FontConfiguration
createFontConfiguration
(
boolean
preferLocaleFonts
,
boolean
preferPropFonts
);
/**
* Returns face name for default font, or null if
* no face names are used for CompositeFontDescriptors
* for this platform.
*/
public
synchronized
String
getDefaultFontFaceName
()
{
if
(
defaultFontName
==
null
)
{
initDefaultFonts
();
}
return
defaultFontName
;
}
public
void
loadFontFiles
()
{
loadFonts
();
if
(
loadedAllFontFiles
)
{
return
;
}
/* Use lock specific to the font system */
synchronized
(
lucidaFontName
)
{
if
(
FontUtilities
.
debugFonts
())
{
Thread
.
dumpStack
();
FontUtilities
.
getLogger
().
info
(
"loadAllFontFiles() called"
);
}
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
if
(
fontPath
==
null
)
{
fontPath
=
getPlatformFontPath
(
noType1Font
);
}
if
(
fontPath
!=
null
)
{
// this will find all fonts including those already
// registered. But we have checks in place to prevent
// double registration.
registerFontsOnPath
(
fontPath
,
false
,
Font2D
.
UNKNOWN_RANK
,
false
,
true
);
}
loadedAllFontFiles
=
true
;
return
null
;
}
});
}
}
/*
* This method asks the font configuration API for all platform names
* used as components of composite/logical fonts and iterates over these
* looking up their corresponding file name and registers these fonts.
* It also ensures that the fonts are accessible via platform APIs.
* The composites themselves are then registered.
*/
private
void
initCompositeFonts
(
FontConfiguration
fontConfig
,
ConcurrentHashMap
<
String
,
Font2D
>
altNameCache
)
{
int
numCoreFonts
=
fontConfig
.
getNumberCoreFonts
();
String
[]
fcFonts
=
fontConfig
.
getPlatformFontNames
();
for
(
int
f
=
0
;
f
<
fcFonts
.
length
;
f
++)
{
String
platformFontName
=
fcFonts
[
f
];
String
fontFileName
=
getFileNameFromPlatformName
(
platformFontName
);
String
[]
nativeNames
=
null
;
if
(
fontFileName
==
null
||
fontFileName
.
equals
(
platformFontName
))
{
/* No file located, so register using the platform name,
* i.e. as a native font.
*/
fontFileName
=
platformFontName
;
}
else
{
if
(
f
<
numCoreFonts
)
{
/* If platform APIs also need to access the font, add it
* to a set to be registered with the platform too.
* This may be used to add the parent directory to the X11
* font path if its not already there. See the docs for the
* subclass implementation.
* This is now mainly for the benefit of X11-based AWT
* But for historical reasons, 2D initialisation code
* makes these calls.
* If the fontconfiguration file is properly set up
* so that all fonts are mapped to files and all their
* appropriate directories are specified, then this
* method will be low cost as it will return after
* a test that finds a null lookup map.
*/
addFontToPlatformFontPath
(
platformFontName
);
}
nativeNames
=
getNativeNames
(
fontFileName
,
platformFontName
);
}
/* Uncomment these two lines to "generate" the XLFD->filename
* mappings needed to speed start-up on Solaris.
* Augment this with the appendedpathname and the mappings
* for native (F3) fonts
*/
//String platName = platformFontName.replaceAll(" ", "_");
//System.out.println("filename."+platName+"="+fontFileName);
registerFontFile
(
fontFileName
,
nativeNames
,
Font2D
.
FONT_CONFIG_RANK
,
true
);
}
/* This registers accumulated paths from the calls to
* addFontToPlatformFontPath(..) and any specified by
* the font configuration. Rather than registering
* the fonts it puts them in a place and form suitable for
* the Toolkit to pick up and use if a toolkit is initialised,
* and if it uses X11 fonts.
*/
registerPlatformFontsUsedByFontConfiguration
();
CompositeFontDescriptor
[]
compositeFontInfo
=
fontConfig
.
get2DCompositeFontInfo
();
for
(
int
i
=
0
;
i
<
compositeFontInfo
.
length
;
i
++)
{
CompositeFontDescriptor
descriptor
=
compositeFontInfo
[
i
];
String
[]
componentFileNames
=
descriptor
.
getComponentFileNames
();
String
[]
componentFaceNames
=
descriptor
.
getComponentFaceNames
();
/* It would be better eventually to handle this in the
* FontConfiguration code which should also remove duplicate slots
*/
if
(
missingFontFiles
!=
null
)
{
for
(
int
ii
=
0
;
ii
<
componentFileNames
.
length
;
ii
++)
{
if
(
missingFontFiles
.
contains
(
componentFileNames
[
ii
]))
{
componentFileNames
[
ii
]
=
getDefaultFontFile
();
componentFaceNames
[
ii
]
=
getDefaultFontFaceName
();
}
}
}
/* FontConfiguration needs to convey how many fonts it has added
* as fallback component fonts which should not affect metrics.
* The core component count will be the number of metrics slots.
* This does not preclude other mechanisms for adding
* fall back component fonts to the composite.
*/
if
(
altNameCache
!=
null
)
{
SunFontManager
.
registerCompositeFont
(
descriptor
.
getFaceName
(),
componentFileNames
,
componentFaceNames
,
descriptor
.
getCoreComponentCount
(),
descriptor
.
getExclusionRanges
(),
descriptor
.
getExclusionRangeLimits
(),
true
,
altNameCache
);
}
else
{
registerCompositeFont
(
descriptor
.
getFaceName
(),
componentFileNames
,
componentFaceNames
,
descriptor
.
getCoreComponentCount
(),
descriptor
.
getExclusionRanges
(),
descriptor
.
getExclusionRangeLimits
(),
true
);
}
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
info
(
"registered "
+
descriptor
.
getFaceName
());
}
}
}
/**
* Notifies graphics environment that the logical font configuration
* uses the given platform font name. The graphics environment may
* use this for platform specific initialization.
*/
protected
void
addFontToPlatformFontPath
(
String
platformFontName
)
{
}
protected
void
registerFontFile
(
String
fontFileName
,
String
[]
nativeNames
,
int
fontRank
,
boolean
defer
)
{
// REMIND: case compare depends on platform
if
(
registeredFontFiles
.
contains
(
fontFileName
))
{
return
;
}
int
fontFormat
;
if
(
ttFilter
.
accept
(
null
,
fontFileName
))
{
fontFormat
=
FONTFORMAT_TRUETYPE
;
}
else
if
(
t1Filter
.
accept
(
null
,
fontFileName
))
{
fontFormat
=
FONTFORMAT_TYPE1
;
}
else
{
fontFormat
=
FONTFORMAT_NATIVE
;
}
registeredFontFiles
.
add
(
fontFileName
);
if
(
defer
)
{
registerDeferredFont
(
fontFileName
,
fontFileName
,
nativeNames
,
fontFormat
,
false
,
fontRank
);
}
else
{
registerFontFile
(
fontFileName
,
nativeNames
,
fontFormat
,
false
,
fontRank
);
}
}
protected
void
registerPlatformFontsUsedByFontConfiguration
()
{
}
/*
* A GE may verify whether a font file used in a fontconfiguration
* exists. If it doesn't then either we may substitute the default
* font, or perhaps elide it altogether from the composite font.
* This makes some sense on windows where the font file is only
* likely to be in one place. But on other OSes, eg Linux, the file
* can move around depending. So there we probably don't want to assume
* its missing and so won't add it to this list.
* If this list - missingFontFiles - is non-null then the composite
* font initialisation logic tests to see if a font file is in that
* set.
* Only one thread should be able to add to this set so we don't
* synchronize.
*/
protected
void
addToMissingFontFileList
(
String
fileName
)
{
if
(
missingFontFiles
==
null
)
{
missingFontFiles
=
new
HashSet
<
String
>();
}
missingFontFiles
.
add
(
fileName
);
}
/*
* This is for use only within getAllFonts().
* Fonts listed in the fontconfig files for windows were all
* on the "deferred" initialisation list. They were registered
* either in the course of the application, or in the call to
* loadFonts() within getAllFonts(). The fontconfig file specifies
* the names of the fonts using the English names. If there's a
* different name in the execution locale, then the platform will
* report that, and we will construct the font with both names, and
* thereby enumerate it twice. This happens for Japanese fonts listed
* in the windows fontconfig, when run in the JA locale. The solution
* is to rely (in this case) on the platform's font->file mapping to
* determine that this name corresponds to a file we already registered.
* This works because
* - we know when we get here all deferred fonts are already initialised
* - when we register a font file, we register all fonts in it.
* - we know the fontconfig fonts are all in the windows registry
*/
private
boolean
isNameForRegisteredFile
(
String
fontName
)
{
String
fileName
=
getFileNameForFontName
(
fontName
);
if
(
fileName
==
null
)
{
return
false
;
}
return
registeredFontFiles
.
contains
(
fileName
);
}
/*
* This invocation is not in a privileged block because
* all privileged operations (reading files and properties)
* was conducted on the creation of the GE
*/
public
void
createCompositeFonts
(
ConcurrentHashMap
<
String
,
Font2D
>
altNameCache
,
boolean
preferLocale
,
boolean
preferProportional
)
{
FontConfiguration
fontConfig
=
createFontConfiguration
(
preferLocale
,
preferProportional
);
initCompositeFonts
(
fontConfig
,
altNameCache
);
}
/**
* Returns all fonts installed in this environment.
*/
public
Font
[]
getAllInstalledFonts
()
{
if
(
allFonts
==
null
)
{
loadFonts
();
TreeMap
fontMapNames
=
new
TreeMap
();
/* warning: the number of composite fonts could change dynamically
* if applications are allowed to create them. "allfonts" could
* then be stale.
*/
Font2D
[]
allfonts
=
getRegisteredFonts
();
for
(
int
i
=
0
;
i
<
allfonts
.
length
;
i
++)
{
if
(!(
allfonts
[
i
]
instanceof
NativeFont
))
{
fontMapNames
.
put
(
allfonts
[
i
].
getFontName
(
null
),
allfonts
[
i
]);
}
}
String
[]
platformNames
=
getFontNamesFromPlatform
();
if
(
platformNames
!=
null
)
{
for
(
int
i
=
0
;
i
<
platformNames
.
length
;
i
++)
{
if
(!
isNameForRegisteredFile
(
platformNames
[
i
]))
{
fontMapNames
.
put
(
platformNames
[
i
],
null
);
}
}
}
String
[]
fontNames
=
null
;
if
(
fontMapNames
.
size
()
>
0
)
{
fontNames
=
new
String
[
fontMapNames
.
size
()];
Object
[]
keyNames
=
fontMapNames
.
keySet
().
toArray
();
for
(
int
i
=
0
;
i
<
keyNames
.
length
;
i
++)
{
fontNames
[
i
]
=
(
String
)
keyNames
[
i
];
}
}
Font
[]
fonts
=
new
Font
[
fontNames
.
length
];
for
(
int
i
=
0
;
i
<
fontNames
.
length
;
i
++)
{
fonts
[
i
]
=
new
Font
(
fontNames
[
i
],
Font
.
PLAIN
,
1
);
Font2D
f2d
=
(
Font2D
)
fontMapNames
.
get
(
fontNames
[
i
]);
if
(
f2d
!=
null
)
{
FontAccess
.
getFontAccess
().
setFont2D
(
fonts
[
i
],
f2d
.
handle
);
}
}
allFonts
=
fonts
;
}
Font
[]
copyFonts
=
new
Font
[
allFonts
.
length
];
System
.
arraycopy
(
allFonts
,
0
,
copyFonts
,
0
,
allFonts
.
length
);
return
copyFonts
;
}
/**
* Get a list of installed fonts in the requested {@link Locale}.
* The list contains the fonts Family Names.
* If Locale is null, the default locale is used.
*
* @param requestedLocale, if null the default locale is used.
* @return list of installed fonts in the system.
*/
public
String
[]
getInstalledFontFamilyNames
(
Locale
requestedLocale
)
{
if
(
requestedLocale
==
null
)
{
requestedLocale
=
Locale
.
getDefault
();
}
if
(
allFamilies
!=
null
&&
lastDefaultLocale
!=
null
&&
requestedLocale
.
equals
(
lastDefaultLocale
))
{
String
[]
copyFamilies
=
new
String
[
allFamilies
.
length
];
System
.
arraycopy
(
allFamilies
,
0
,
copyFamilies
,
0
,
allFamilies
.
length
);
return
copyFamilies
;
}
TreeMap
<
String
,
String
>
familyNames
=
new
TreeMap
<
String
,
String
>();
// these names are always there and aren't localised
String
str
;
str
=
Font
.
SERIF
;
familyNames
.
put
(
str
.
toLowerCase
(),
str
);
str
=
Font
.
SANS_SERIF
;
familyNames
.
put
(
str
.
toLowerCase
(),
str
);
str
=
Font
.
MONOSPACED
;
familyNames
.
put
(
str
.
toLowerCase
(),
str
);
str
=
Font
.
DIALOG
;
familyNames
.
put
(
str
.
toLowerCase
(),
str
);
str
=
Font
.
DIALOG_INPUT
;
familyNames
.
put
(
str
.
toLowerCase
(),
str
);
/* Platform APIs may be used to get the set of available family
* names for the current default locale so long as it is the same
* as the start-up system locale, rather than loading all fonts.
*/
if
(
requestedLocale
.
equals
(
getSystemStartupLocale
())
&&
getFamilyNamesFromPlatform
(
familyNames
,
requestedLocale
))
{
/* Augment platform names with JRE font family names */
getJREFontFamilyNames
(
familyNames
,
requestedLocale
);
}
else
{
loadFontFiles
();
Font2D
[]
physicalfonts
=
getPhysicalFonts
();
for
(
int
i
=
0
;
i
<
physicalfonts
.
length
;
i
++)
{
if
(!(
physicalfonts
[
i
]
instanceof
NativeFont
))
{
String
name
=
physicalfonts
[
i
].
getFamilyName
(
requestedLocale
);
familyNames
.
put
(
name
.
toLowerCase
(
requestedLocale
),
name
);
}
}
}
String
[]
retval
=
new
String
[
familyNames
.
size
()];
Object
[]
keyNames
=
familyNames
.
keySet
().
toArray
();
for
(
int
i
=
0
;
i
<
keyNames
.
length
;
i
++)
{
retval
[
i
]
=
(
String
)
familyNames
.
get
(
keyNames
[
i
]);
}
if
(
requestedLocale
.
equals
(
Locale
.
getDefault
()))
{
lastDefaultLocale
=
requestedLocale
;
allFamilies
=
new
String
[
retval
.
length
];
System
.
arraycopy
(
retval
,
0
,
allFamilies
,
0
,
allFamilies
.
length
);
}
return
retval
;
}
public
void
register1dot0Fonts
()
{
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
String
type1Dir
=
"/usr/openwin/lib/X11/fonts/Type1"
;
registerFontsInDir
(
type1Dir
,
true
,
Font2D
.
TYPE1_RANK
,
false
,
false
);
return
null
;
}
});
}
/* Really we need only the JRE fonts family names, but there's little
* overhead in doing this the easy way by adding all the currently
* known fonts.
*/
protected
void
getJREFontFamilyNames
(
TreeMap
<
String
,
String
>
familyNames
,
Locale
requestedLocale
)
{
registerDeferredJREFonts
(
jreFontDirName
);
Font2D
[]
physicalfonts
=
getPhysicalFonts
();
for
(
int
i
=
0
;
i
<
physicalfonts
.
length
;
i
++)
{
if
(!(
physicalfonts
[
i
]
instanceof
NativeFont
))
{
String
name
=
physicalfonts
[
i
].
getFamilyName
(
requestedLocale
);
familyNames
.
put
(
name
.
toLowerCase
(
requestedLocale
),
name
);
}
}
}
/**
* Default locale can be changed but we need to know the initial locale
* as that is what is used by native code. Changing Java default locale
* doesn't affect that.
* Returns the locale in use when using native code to communicate
* with platform APIs. On windows this is known as the "system" locale,
* and it is usually the same as the platform locale, but not always,
* so this method also checks an implementation property used only
* on windows and uses that if set.
*/
private
static
Locale
systemLocale
=
null
;
private
static
Locale
getSystemStartupLocale
()
{
if
(
systemLocale
==
null
)
{
systemLocale
=
(
Locale
)
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
/* On windows the system locale may be different than the
* user locale. This is an unsupported configuration, but
* in that case we want to return a dummy locale that will
* never cause a match in the usage of this API. This is
* important because Windows documents that the family
* names of fonts are enumerated using the language of
* the system locale. BY returning a dummy locale in that
* case we do not use the platform API which would not
* return us the names we want.
*/
String
fileEncoding
=
System
.
getProperty
(
"file.encoding"
,
""
);
String
sysEncoding
=
System
.
getProperty
(
"sun.jnu.encoding"
);
if
(
sysEncoding
!=
null
&&
!
sysEncoding
.
equals
(
fileEncoding
))
{
return
Locale
.
ROOT
;
}
String
language
=
System
.
getProperty
(
"user.language"
,
"en"
);
String
country
=
System
.
getProperty
(
"user.country"
,
""
);
String
variant
=
System
.
getProperty
(
"user.variant"
,
""
);
return
new
Locale
(
language
,
country
,
variant
);
}
});
}
return
systemLocale
;
}
void
addToPool
(
FileFont
font
)
{
FileFont
fontFileToClose
=
null
;
int
freeSlot
=
-
1
;
synchronized
(
fontFileCache
)
{
/* Avoid duplicate entries in the pool, and don't close() it,
* since this method is called only from within open().
* Seeing a duplicate is most likely to happen if the thread
* was interrupted during a read, forcing perhaps repeated
* close and open calls and it eventually it ends up pointing
* at the same slot.
*/
for
(
int
i
=
0
;
i
<
CHANNELPOOLSIZE
;
i
++)
{
if
(
fontFileCache
[
i
]
==
font
)
{
return
;
}
if
(
fontFileCache
[
i
]
==
null
&&
freeSlot
<
0
)
{
freeSlot
=
i
;
}
}
if
(
freeSlot
>=
0
)
{
fontFileCache
[
freeSlot
]
=
font
;
return
;
}
else
{
/* replace with new font. */
fontFileToClose
=
fontFileCache
[
lastPoolIndex
];
fontFileCache
[
lastPoolIndex
]
=
font
;
/* lastPoolIndex is updated so that the least recently opened
* file will be closed next.
*/
lastPoolIndex
=
(
lastPoolIndex
+
1
)
%
CHANNELPOOLSIZE
;
}
}
/* Need to close the font file outside of the synchronized block,
* since its possible some other thread is in an open() call on
* this font file, and could be holding its lock and the pool lock.
* Releasing the pool lock allows that thread to continue, so it can
* then release the lock on this font, allowing the close() call
* below to proceed.
* Also, calling close() is safe because any other thread using
* the font we are closing() synchronizes all reading, so we
* will not close the file while its in use.
*/
if
(
fontFileToClose
!=
null
)
{
fontFileToClose
.
close
();
}
}
protected
FontUIResource
getFontConfigFUIR
(
String
family
,
int
style
,
int
size
)
{
return
new
FontUIResource
(
family
,
style
,
size
);
}
}
src/solaris/classes/sun/awt/X11FontManager.java
0 → 100644
浏览文件 @
84c7e7ae
package
sun.awt
;
import
java.awt.GraphicsEnvironment
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileReader
;
import
java.io.IOException
;
import
java.io.StreamTokenizer
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Locale
;
import
java.util.Map
;
import
java.util.NoSuchElementException
;
import
java.util.StringTokenizer
;
import
java.util.Vector
;
import
java.util.logging.Logger
;
import
javax.swing.plaf.FontUIResource
;
import
sun.awt.motif.MFontConfiguration
;
import
sun.font.CompositeFont
;
import
sun.font.FontManager
;
import
sun.font.SunFontManager
;
import
sun.font.FontConfigManager
;
import
sun.font.FcFontConfiguration
;
import
sun.font.FontAccess
;
import
sun.font.FontUtilities
;
import
sun.font.NativeFont
;
/**
* The X11 implementation of {@link FontManager}.
*/
public
class
X11FontManager
extends
SunFontManager
{
// constants identifying XLFD and font ID fields
private
static
final
int
FOUNDRY_FIELD
=
1
;
private
static
final
int
FAMILY_NAME_FIELD
=
2
;
private
static
final
int
WEIGHT_NAME_FIELD
=
3
;
private
static
final
int
SLANT_FIELD
=
4
;
private
static
final
int
SETWIDTH_NAME_FIELD
=
5
;
private
static
final
int
ADD_STYLE_NAME_FIELD
=
6
;
private
static
final
int
PIXEL_SIZE_FIELD
=
7
;
private
static
final
int
POINT_SIZE_FIELD
=
8
;
private
static
final
int
RESOLUTION_X_FIELD
=
9
;
private
static
final
int
RESOLUTION_Y_FIELD
=
10
;
private
static
final
int
SPACING_FIELD
=
11
;
private
static
final
int
AVERAGE_WIDTH_FIELD
=
12
;
private
static
final
int
CHARSET_REGISTRY_FIELD
=
13
;
private
static
final
int
CHARSET_ENCODING_FIELD
=
14
;
/*
* fontNameMap is a map from a fontID (which is a substring of an XLFD like
* "-monotype-arial-bold-r-normal-iso8859-7")
* to font file path like
* /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf
* It's used in a couple of methods like
* getFileNameFomPlatformName(..) to help locate the font file.
* We use this substring of a full XLFD because the font configuration files
* define the XLFDs in a way that's easier to make into a request.
* E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font
* configuration files. We need to remove that part for comparisons.
*/
private
static
Map
fontNameMap
=
new
HashMap
();
/*
* xlfdMap is a map from a platform path like
* /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like
* "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
* Because there may be multiple native names, because the font is used
* to support multiple X encodings for example, the value of an entry in
* this map is always a vector where we store all the native names.
* For fonts which we don't understand the key isn't a pathname, its
* the full XLFD string like :-
* "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
*/
private
static
Map
xlfdMap
=
new
HashMap
();
/* xFontDirsMap is also a map from a font ID to a font filepath.
* The difference from fontNameMap is just that it does not have
* resolved symbolic links. Normally this is not interesting except
* that we need to know the directory in which a font was found to
* add it to the X font server path, since although the files may
* be linked, the fonts.dir is different and specific to the encoding
* handled by that directory. This map is nulled out after use to free
* heap space. If the optimal path is taken, such that all fonts in
* font configuration files are referenced by filename, then the font
* dir can be directly derived as its parent directory.
* If a font is used by two XLFDs, each corresponding to a different
* X11 font directory, then precautions must be taken to include both
* directories.
*/
private
static
Map
xFontDirsMap
;
/*
* This is the set of font directories needed to be on the X font path
* to enable AWT heavyweights to find all of the font configuration fonts.
* It is populated by :
* - awtfontpath entries in the fontconfig.properties
* - parent directories of "core" fonts used in the fontconfig.properties
* - looking up font dirs in the xFontDirsMap where the key is a fontID
* (cut down version of the XLFD read from the font configuration file).
* This set is nulled out after use to free heap space.
*/
private
static
HashSet
<
String
>
fontConfigDirs
=
null
;
/* These maps are used on Linux where we reference the Lucida oblique
* fonts in fontconfig files even though they aren't in the standard
* font directory. This explicitly remaps the XLFDs for these to the
* correct base font. This is needed to prevent composite fonts from
* defaulting to the Lucida Sans which is a bad substitute for the
* monospaced Lucida Sans Typewriter. Also these maps prevent the
* JRE from doing wasted work at start up.
*/
HashMap
<
String
,
String
>
oblmap
=
null
;
/*
* Used to eliminate redundant work. When a font directory is
* registered it added to this list. Subsequent registrations for the
* same directory can then be skipped by checking this Map.
* Access to this map is not synchronised here since creation
* of the singleton GE instance is already synchronised and that is
* the only code path that accesses this map.
*/
private
static
HashMap
registeredDirs
=
new
HashMap
();
/* Array of directories to be added to the X11 font path.
* Used by static method called from Toolkits which use X11 fonts.
* Specifically this means MToolkit
*/
private
static
String
[]
fontdirs
=
null
;
private
static
String
[]
defaultPlatformFont
=
null
;
private
FontConfigManager
fcManager
=
null
;
public
static
X11FontManager
getInstance
()
{
return
(
X11FontManager
)
SunFontManager
.
getInstance
();
}
/**
* Takes family name property in the following format:
* "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1"
* and returns the name of the corresponding physical font.
* This code is used to resolve font configuration fonts, and expects
* only to get called for these fonts.
*/
@Override
public
String
getFileNameFromPlatformName
(
String
platName
)
{
/* If the FontConfig file doesn't use xlfds, or its
* FcFontConfiguration, this may be already a file name.
*/
if
(
platName
.
startsWith
(
"/"
))
{
return
platName
;
}
String
fileName
=
null
;
String
fontID
=
specificFontIDForName
(
platName
);
/* If the font filename has been explicitly assigned in the
* font configuration file, use it. This avoids accessing
* the wrong fonts on Linux, where different fonts (some
* of which may not be usable by 2D) may share the same
* specific font ID. It may also speed up the lookup.
*/
fileName
=
super
.
getFileNameFromPlatformName
(
platName
);
if
(
fileName
!=
null
)
{
if
(
isHeadless
()
&&
fileName
.
startsWith
(
"-"
))
{
/* if it's headless, no xlfd should be used */
return
null
;
}
if
(
fileName
.
startsWith
(
"/"
))
{
/* If a path is assigned in the font configuration file,
* it is required that the config file also specify using the
* new awtfontpath key the X11 font directories
* which must be added to the X11 font path to support
* AWT access to that font. For that reason we no longer
* have code here to add the parent directory to the list
* of font config dirs, since the parent directory may not
* be sufficient if fonts are symbolically linked to a
* different directory.
*
* Add this XLFD (platform name) to the list of known
* ones for this file.
*/
Vector
xVal
=
(
Vector
)
xlfdMap
.
get
(
fileName
);
if
(
xVal
==
null
)
{
/* Try to be robust on Linux distros which move fonts
* around by verifying that the fileName represents a
* file that exists. If it doesn't, set it to null
* to trigger a search.
*/
if
(
getFontConfiguration
().
needToSearchForFile
(
fileName
))
{
fileName
=
null
;
}
if
(
fileName
!=
null
)
{
xVal
=
new
Vector
();
xVal
.
add
(
platName
);
xlfdMap
.
put
(
fileName
,
xVal
);
}
}
else
{
if
(!
xVal
.
contains
(
platName
))
{
xVal
.
add
(
platName
);
}
}
}
if
(
fileName
!=
null
)
{
fontNameMap
.
put
(
fontID
,
fileName
);
return
fileName
;
}
}
if
(
fontID
!=
null
)
{
fileName
=
(
String
)
fontNameMap
.
get
(
fontID
);
/* On Linux check for the Lucida Oblique fonts */
if
(
fileName
==
null
&&
FontUtilities
.
isLinux
&&
!
isOpenJDK
())
{
if
(
oblmap
==
null
)
{
initObliqueLucidaFontMap
();
}
String
oblkey
=
getObliqueLucidaFontID
(
fontID
);
if
(
oblkey
!=
null
)
{
fileName
=
oblmap
.
get
(
oblkey
);
}
}
if
(
fontPath
==
null
&&
(
fileName
==
null
||
!
fileName
.
startsWith
(
"/"
)))
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
warning
(
"** Registering all font paths because "
+
"can't find file for "
+
platName
);
}
fontPath
=
getPlatformFontPath
(
noType1Font
);
registerFontDirs
(
fontPath
);
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
warning
(
"** Finished registering all font paths"
);
}
fileName
=
(
String
)
fontNameMap
.
get
(
fontID
);
}
if
(
fileName
==
null
&&
!
isHeadless
())
{
/* Query X11 directly to see if this font is available
* as a native font.
*/
fileName
=
getX11FontName
(
platName
);
}
if
(
fileName
==
null
)
{
fontID
=
switchFontIDForName
(
platName
);
fileName
=
(
String
)
fontNameMap
.
get
(
fontID
);
}
if
(
fileName
!=
null
)
{
fontNameMap
.
put
(
fontID
,
fileName
);
}
}
return
fileName
;
}
@Override
protected
String
[]
getNativeNames
(
String
fontFileName
,
String
platformName
)
{
Vector
nativeNames
;
if
((
nativeNames
=(
Vector
)
xlfdMap
.
get
(
fontFileName
))==
null
)
{
if
(
platformName
==
null
)
{
return
null
;
}
else
{
/* back-stop so that at least the name used in the
* font configuration file is known as a native name
*/
String
[]
natNames
=
new
String
[
1
];
natNames
[
0
]
=
platformName
;
return
natNames
;
}
}
else
{
int
len
=
nativeNames
.
size
();
return
(
String
[])
nativeNames
.
toArray
(
new
String
[
len
]);
}
}
/* NOTE: this method needs to be executed in a privileged context.
* The superclass constructor which is the primary caller of
* this method executes entirely in such a context. Additionally
* the loadFonts() method does too. So all should be well.
*/
@Override
protected
void
registerFontDir
(
String
path
)
{
/* fonts.dir file format looks like :-
* 47
* Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1
* Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1
* ...
*/
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
().
info
(
"ParseFontDir "
+
path
);
}
File
fontsDotDir
=
new
File
(
path
+
File
.
separator
+
"fonts.dir"
);
FileReader
fr
=
null
;
try
{
if
(
fontsDotDir
.
canRead
())
{
fr
=
new
FileReader
(
fontsDotDir
);
BufferedReader
br
=
new
BufferedReader
(
fr
,
8192
);
StreamTokenizer
st
=
new
StreamTokenizer
(
br
);
st
.
eolIsSignificant
(
true
);
int
ttype
=
st
.
nextToken
();
if
(
ttype
==
StreamTokenizer
.
TT_NUMBER
)
{
int
numEntries
=
(
int
)
st
.
nval
;
ttype
=
st
.
nextToken
();
if
(
ttype
==
StreamTokenizer
.
TT_EOL
)
{
st
.
resetSyntax
();
st
.
wordChars
(
32
,
127
);
st
.
wordChars
(
128
+
32
,
255
);
st
.
whitespaceChars
(
0
,
31
);
for
(
int
i
=
0
;
i
<
numEntries
;
i
++)
{
ttype
=
st
.
nextToken
();
if
(
ttype
==
StreamTokenizer
.
TT_EOF
)
{
break
;
}
if
(
ttype
!=
StreamTokenizer
.
TT_WORD
)
{
break
;
}
int
breakPos
=
st
.
sval
.
indexOf
(
' '
);
if
(
breakPos
<=
0
)
{
/* On TurboLinux 8.0 a fonts.dir file had
* a line with integer value "24" which
* appeared to be the number of remaining
* entries in the file. This didn't add to
* the value on the first line of the file.
* Seemed like XFree86 didn't like this line
* much either. It failed to parse the file.
* Ignore lines like this completely, and
* don't let them count as an entry.
*/
numEntries
++;
ttype
=
st
.
nextToken
();
if
(
ttype
!=
StreamTokenizer
.
TT_EOL
)
{
break
;
}
continue
;
}
if
(
st
.
sval
.
charAt
(
0
)
==
'!'
)
{
/* TurboLinux 8.0 comment line: ignore.
* can't use st.commentChar('!') to just
* skip because this line mustn't count
* against numEntries.
*/
numEntries
++;
ttype
=
st
.
nextToken
();
if
(
ttype
!=
StreamTokenizer
.
TT_EOL
)
{
break
;
}
continue
;
}
String
fileName
=
st
.
sval
.
substring
(
0
,
breakPos
);
/* TurboLinux 8.0 uses some additional syntax to
* indicate algorithmic styling values.
* Ignore ':' separated files at the beginning
* of the fileName
*/
int
lastColon
=
fileName
.
lastIndexOf
(
':'
);
if
(
lastColon
>
0
)
{
if
(
lastColon
+
1
>=
fileName
.
length
())
{
continue
;
}
fileName
=
fileName
.
substring
(
lastColon
+
1
);
}
String
fontPart
=
st
.
sval
.
substring
(
breakPos
+
1
);
String
fontID
=
specificFontIDForName
(
fontPart
);
String
sVal
=
(
String
)
fontNameMap
.
get
(
fontID
);
if
(
FontUtilities
.
debugFonts
())
{
Logger
logger
=
FontUtilities
.
getLogger
();
logger
.
info
(
"file="
+
fileName
+
" xlfd="
+
fontPart
);
logger
.
info
(
"fontID="
+
fontID
+
" sVal="
+
sVal
);
}
String
fullPath
=
null
;
try
{
File
file
=
new
File
(
path
,
fileName
);
/* we may have a resolved symbolic link
* this becomes important for an xlfd we
* still need to know the location it was
* found to update the X server font path
* for use by AWT heavyweights - and when 2D
* wants to use the native rasteriser.
*/
if
(
xFontDirsMap
==
null
)
{
xFontDirsMap
=
new
HashMap
();
}
xFontDirsMap
.
put
(
fontID
,
path
);
fullPath
=
file
.
getCanonicalPath
();
}
catch
(
IOException
e
)
{
fullPath
=
path
+
File
.
separator
+
fileName
;
}
Vector
xVal
=
(
Vector
)
xlfdMap
.
get
(
fullPath
);
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
info
(
"fullPath="
+
fullPath
+
" xVal="
+
xVal
);
}
if
((
xVal
==
null
||
!
xVal
.
contains
(
fontPart
))
&&
(
sVal
==
null
)
||
!
sVal
.
startsWith
(
"/"
))
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
info
(
"Map fontID:"
+
fontID
+
"to file:"
+
fullPath
);
}
fontNameMap
.
put
(
fontID
,
fullPath
);
if
(
xVal
==
null
)
{
xVal
=
new
Vector
();
xlfdMap
.
put
(
fullPath
,
xVal
);
}
xVal
.
add
(
fontPart
);
}
ttype
=
st
.
nextToken
();
if
(
ttype
!=
StreamTokenizer
.
TT_EOL
)
{
break
;
}
}
}
}
fr
.
close
();
}
}
catch
(
IOException
ioe1
)
{
}
finally
{
if
(
fr
!=
null
)
{
try
{
fr
.
close
();
}
catch
(
IOException
ioe2
)
{
}
}
}
}
@Override
public
void
loadFonts
()
{
super
.
loadFonts
();
/* These maps are greatly expanded during a loadFonts but
* can be reset to their initial state afterwards.
* Since preferLocaleFonts() and preferProportionalFonts() will
* trigger a partial repopulating from the FontConfiguration
* it has to be the inital (empty) state for the latter two, not
* simply nulling out.
* xFontDirsMap is a special case in that the implementation
* will typically not ever need to initialise it so it can be null.
*/
xFontDirsMap
=
null
;
xlfdMap
=
new
HashMap
(
1
);
fontNameMap
=
new
HashMap
(
1
);
}
private
String
getObliqueLucidaFontID
(
String
fontID
)
{
if
(
fontID
.
startsWith
(
"-lucidasans-medium-i-normal"
)
||
fontID
.
startsWith
(
"-lucidasans-bold-i-normal"
)
||
fontID
.
startsWith
(
"-lucidatypewriter-medium-i-normal"
)
||
fontID
.
startsWith
(
"-lucidatypewriter-bold-i-normal"
))
{
return
fontID
.
substring
(
0
,
fontID
.
indexOf
(
"-i-"
));
}
else
{
return
null
;
}
}
private
static
String
getX11FontName
(
String
platName
)
{
String
xlfd
=
platName
.
replaceAll
(
"%d"
,
"*"
);
if
(
NativeFont
.
fontExists
(
xlfd
))
{
return
xlfd
;
}
else
{
return
null
;
}
}
private
void
initObliqueLucidaFontMap
()
{
oblmap
=
new
HashMap
<
String
,
String
>();
oblmap
.
put
(
"-lucidasans-medium"
,
jreLibDirName
+
"/fonts/LucidaSansRegular.ttf"
);
oblmap
.
put
(
"-lucidasans-bold"
,
jreLibDirName
+
"/fonts/LucidaSansDemiBold.ttf"
);
oblmap
.
put
(
"-lucidatypewriter-medium"
,
jreLibDirName
+
"/fonts/LucidaTypewriterRegular.ttf"
);
oblmap
.
put
(
"-lucidatypewriter-bold"
,
jreLibDirName
+
"/fonts/LucidaTypewriterBold.ttf"
);
}
private
boolean
isHeadless
()
{
GraphicsEnvironment
ge
=
GraphicsEnvironment
.
getLocalGraphicsEnvironment
();
return
GraphicsEnvironment
.
isHeadless
();
}
private
String
specificFontIDForName
(
String
name
)
{
int
[]
hPos
=
new
int
[
14
];
int
hyphenCnt
=
1
;
int
pos
=
1
;
while
(
pos
!=
-
1
&&
hyphenCnt
<
14
)
{
pos
=
name
.
indexOf
(
'-'
,
pos
);
if
(
pos
!=
-
1
)
{
hPos
[
hyphenCnt
++]
=
pos
;
pos
++;
}
}
if
(
hyphenCnt
!=
14
)
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
severe
(
"Font Configuration Font ID is malformed:"
+
name
);
}
return
name
;
// what else can we do?
}
StringBuffer
sb
=
new
StringBuffer
(
name
.
substring
(
hPos
[
FAMILY_NAME_FIELD
-
1
],
hPos
[
SETWIDTH_NAME_FIELD
]));
sb
.
append
(
name
.
substring
(
hPos
[
CHARSET_REGISTRY_FIELD
-
1
]));
String
retval
=
sb
.
toString
().
toLowerCase
(
Locale
.
ENGLISH
);
return
retval
;
}
private
String
switchFontIDForName
(
String
name
)
{
int
[]
hPos
=
new
int
[
14
];
int
hyphenCnt
=
1
;
int
pos
=
1
;
while
(
pos
!=
-
1
&&
hyphenCnt
<
14
)
{
pos
=
name
.
indexOf
(
'-'
,
pos
);
if
(
pos
!=
-
1
)
{
hPos
[
hyphenCnt
++]
=
pos
;
pos
++;
}
}
if
(
hyphenCnt
!=
14
)
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
()
.
severe
(
"Font Configuration Font ID is malformed:"
+
name
);
}
return
name
;
// what else can we do?
}
String
slant
=
name
.
substring
(
hPos
[
SLANT_FIELD
-
1
]+
1
,
hPos
[
SLANT_FIELD
]);
String
family
=
name
.
substring
(
hPos
[
FAMILY_NAME_FIELD
-
1
]+
1
,
hPos
[
FAMILY_NAME_FIELD
]);
String
registry
=
name
.
substring
(
hPos
[
CHARSET_REGISTRY_FIELD
-
1
]+
1
,
hPos
[
CHARSET_REGISTRY_FIELD
]);
String
encoding
=
name
.
substring
(
hPos
[
CHARSET_ENCODING_FIELD
-
1
]+
1
);
if
(
slant
.
equals
(
"i"
))
{
slant
=
"o"
;
}
else
if
(
slant
.
equals
(
"o"
))
{
slant
=
"i"
;
}
// workaround for #4471000
if
(
family
.
equals
(
"itc zapfdingbats"
)
&&
registry
.
equals
(
"sun"
)
&&
encoding
.
equals
(
"fontspecific"
)){
registry
=
"adobe"
;
}
StringBuffer
sb
=
new
StringBuffer
(
name
.
substring
(
hPos
[
FAMILY_NAME_FIELD
-
1
],
hPos
[
SLANT_FIELD
-
1
]+
1
));
sb
.
append
(
slant
);
sb
.
append
(
name
.
substring
(
hPos
[
SLANT_FIELD
],
hPos
[
SETWIDTH_NAME_FIELD
]+
1
));
sb
.
append
(
registry
);
sb
.
append
(
name
.
substring
(
hPos
[
CHARSET_ENCODING_FIELD
-
1
]));
String
retval
=
sb
.
toString
().
toLowerCase
(
Locale
.
ENGLISH
);
return
retval
;
}
/**
* Returns the face name for the given XLFD.
*/
public
String
getFileNameFromXLFD
(
String
name
)
{
String
fileName
=
null
;
String
fontID
=
specificFontIDForName
(
name
);
if
(
fontID
!=
null
)
{
fileName
=
(
String
)
fontNameMap
.
get
(
fontID
);
if
(
fileName
==
null
)
{
fontID
=
switchFontIDForName
(
name
);
fileName
=
(
String
)
fontNameMap
.
get
(
fontID
);
}
if
(
fileName
==
null
)
{
fileName
=
getDefaultFontFile
();
}
}
return
fileName
;
}
/* Register just the paths, (it doesn't register the fonts).
* If a font configuration file has specified a baseFontPath
* fontPath is just those directories, unless on usage we
* find it doesn't contain what we need for the logical fonts.
* Otherwise, we register all the paths on Solaris, because
* the fontPath we have here is the complete one from
* parsing /var/sadm/install/contents, not just
* what's on the X font path (may be this should be
* changed).
* But for now what it means is that if we didn't do
* this then if the font weren't listed anywhere on the
* less complete font path we'd trigger loadFonts which
* actually registers the fonts. This may actually be
* the right thing tho' since that would also set up
* the X font path without which we wouldn't be able to
* display some "native" fonts.
* So something to revisit is that probably fontPath
* here ought to be only the X font path + jre font dir.
* loadFonts should have a separate native call to
* get the rest of the platform font path.
*
* Registering the directories can now be avoided in the
* font configuration initialisation when filename entries
* exist in the font configuration file for all fonts.
* (Perhaps a little confusingly a filename entry is
* actually keyed using the XLFD used in the font entries,
* and it maps *to* a real filename).
* In the event any are missing, registration of all
* directories will be invoked to find the real files.
*
* But registering the directory performed other
* functions such as filling in the map of all native names
* for the font. So when this method isn't invoked, they still
* must be found. This is mitigated by getNativeNames now
* being able to return at least the platform name, but mostly
* by ensuring that when a filename key is found, that
* xlfd key is stored as one of the set of platform names
* for the font. Its a set because typical font configuration
* files reference the same CJK font files using multiple
* X11 encodings. For the code that adds this to the map
* see X11GE.getFileNameFromPlatformName(..)
* If you don't get all of these then some code points may
* not use the Xserver, and will not get the PCF bitmaps
* that are available for some point sizes.
* So, in the event that there is such a problem,
* unconditionally making this call may be necessary, at
* some cost to JRE start-up
*/
@Override
protected
void
registerFontDirs
(
String
pathName
)
{
StringTokenizer
parser
=
new
StringTokenizer
(
pathName
,
File
.
pathSeparator
);
try
{
while
(
parser
.
hasMoreTokens
())
{
String
dirPath
=
parser
.
nextToken
();
if
(
dirPath
!=
null
&&
!
registeredDirs
.
containsKey
(
dirPath
))
{
registeredDirs
.
put
(
dirPath
,
null
);
registerFontDir
(
dirPath
);
}
}
}
catch
(
NoSuchElementException
e
)
{
}
}
// An X font spec (xlfd) includes an encoding. The same TrueType font file
// may be referenced from different X font directories in font.dir files
// to support use in multiple encodings by X apps.
// So for the purposes of font configuration logical fonts where AWT
// heavyweights need to access the font via X APIs we need to ensure that
// the directory for precisely the encodings needed by this are added to
// the x font path. This requires that we note the platform names
// specified in font configuration files and use that to identify the
// X font directory that contains a font.dir file for that platform name
// and add it to the X font path (if display is local)
// Here we make use of an already built map of xlfds to font locations
// to add the font location to the set of those required to build the
// x font path needed by AWT.
// These are added to the x font path later.
// All this is necessary because on Solaris the font.dir directories
// may contain not real font files, but symbolic links to the actual
// location but that location is not suitable for the x font path, since
// it probably doesn't have a font.dir at all and certainly not one
// with the required encodings
// If the fontconfiguration file is properly set up so that all fonts
// are mapped to files then we will never trigger initialising
// xFontDirsMap (it will be null). In this case the awtfontpath entries
// must specify all the X11 directories needed by AWT.
@Override
protected
void
addFontToPlatformFontPath
(
String
platformName
)
{
// Lazily initialize fontConfigDirs.
getPlatformFontPathFromFontConfig
();
if
(
xFontDirsMap
!=
null
)
{
String
fontID
=
specificFontIDForName
(
platformName
);
String
dirName
=
(
String
)
xFontDirsMap
.
get
(
fontID
);
if
(
dirName
!=
null
)
{
fontConfigDirs
.
add
(
dirName
);
}
}
return
;
}
private
void
getPlatformFontPathFromFontConfig
()
{
if
(
fontConfigDirs
==
null
)
{
fontConfigDirs
=
getFontConfiguration
().
getAWTFontPathSet
();
if
(
FontUtilities
.
debugFonts
()
&&
fontConfigDirs
!=
null
)
{
String
[]
names
=
fontConfigDirs
.
toArray
(
new
String
[
0
]);
for
(
int
i
=
0
;
i
<
names
.
length
;
i
++)
{
FontUtilities
.
getLogger
().
info
(
"awtfontpath : "
+
names
[
i
]);
}
}
}
}
@Override
protected
void
registerPlatformFontsUsedByFontConfiguration
()
{
// Lazily initialize fontConfigDirs.
getPlatformFontPathFromFontConfig
();
if
(
fontConfigDirs
==
null
)
{
return
;
}
if
(
FontUtilities
.
isLinux
)
{
fontConfigDirs
.
add
(
jreLibDirName
+
File
.
separator
+
"oblique-fonts"
);
}
fontdirs
=
(
String
[])
fontConfigDirs
.
toArray
(
new
String
[
0
]);
}
/* Called by MToolkit to set the X11 font path */
public
static
void
setNativeFontPath
()
{
if
(
fontdirs
==
null
)
{
return
;
}
// need to register these individually rather than by one call
// to ensure that one bad directory doesn't cause all to be rejected
for
(
int
i
=
0
;
i
<
fontdirs
.
length
;
i
++)
{
if
(
FontUtilities
.
debugFonts
())
{
FontUtilities
.
getLogger
().
info
(
"Add "
+
fontdirs
[
i
]
+
" to X11 fontpath"
);
}
setNativeFontPath
(
fontdirs
[
i
]);
}
}
private
synchronized
static
native
void
setNativeFontPath
(
String
fontPath
);
// Implements SunGraphicsEnvironment.createFontConfiguration.
protected
FontConfiguration
createFontConfiguration
()
{
/* The logic here decides whether to use a preconfigured
* fontconfig.properties file, or synthesise one using platform APIs.
* On Solaris (as opposed to OpenSolaris) we try to use the
* pre-configured ones, but if the files it specifies are missing
* we fail-safe to synthesising one. This might happen if Solaris
* changes its fonts.
* For OpenSolaris I don't expect us to ever create fontconfig files,
* so it will always synthesise. Note that if we misidentify
* OpenSolaris as Solaris, then the test for the presence of
* Solaris-only font files will correct this.
* For Linux we require an exact match of distro and version to
* use the preconfigured file, and also that it points to
* existent fonts.
* If synthesising fails, we fall back to any preconfigured file
* and do the best we can. For the commercial JDK this will be
* fine as it includes the Lucida fonts. OpenJDK should not hit
* this as the synthesis should always work on its platforms.
*/
FontConfiguration
mFontConfig
=
new
MFontConfiguration
(
this
);
if
(
FontUtilities
.
isOpenSolaris
||
(
FontUtilities
.
isLinux
&&
(!
mFontConfig
.
foundOsSpecificFile
()
||
!
mFontConfig
.
fontFilesArePresent
())
||
(
FontUtilities
.
isSolaris
&&
!
mFontConfig
.
fontFilesArePresent
())))
{
FcFontConfiguration
fcFontConfig
=
new
FcFontConfiguration
(
this
);
if
(
fcFontConfig
.
init
())
{
return
fcFontConfig
;
}
}
mFontConfig
.
init
();
return
mFontConfig
;
}
public
FontConfiguration
createFontConfiguration
(
boolean
preferLocaleFonts
,
boolean
preferPropFonts
)
{
return
new
MFontConfiguration
(
this
,
preferLocaleFonts
,
preferPropFonts
);
}
public
synchronized
native
String
getFontPath
(
boolean
noType1Fonts
);
public
String
[]
getDefaultPlatformFont
()
{
if
(
defaultPlatformFont
!=
null
)
{
return
defaultPlatformFont
;
}
String
[]
info
=
new
String
[
2
];
getFontConfigManager
().
initFontConfigFonts
(
false
);
FontConfigManager
.
FcCompFont
[]
fontConfigFonts
=
getFontConfigManager
().
getFontConfigFonts
();
for
(
int
i
=
0
;
i
<
fontConfigFonts
.
length
;
i
++)
{
if
(
"sans"
.
equals
(
fontConfigFonts
[
i
].
fcFamily
)
&&
0
==
fontConfigFonts
[
i
].
style
)
{
info
[
0
]
=
fontConfigFonts
[
i
].
firstFont
.
familyName
;
info
[
1
]
=
fontConfigFonts
[
i
].
firstFont
.
fontFile
;
break
;
}
}
/* Absolute last ditch attempt in the face of fontconfig problems.
* If we didn't match, pick the first, or just make something
* up so we don't NPE.
*/
if
(
info
[
0
]
==
null
)
{
if
(
fontConfigFonts
.
length
>
0
&&
fontConfigFonts
[
0
].
firstFont
.
fontFile
!=
null
)
{
info
[
0
]
=
fontConfigFonts
[
0
].
firstFont
.
familyName
;
info
[
1
]
=
fontConfigFonts
[
0
].
firstFont
.
fontFile
;
}
else
{
info
[
0
]
=
"Dialog"
;
info
[
1
]
=
"/dialog.ttf"
;
}
}
defaultPlatformFont
=
info
;
return
defaultPlatformFont
;
}
public
synchronized
FontConfigManager
getFontConfigManager
()
{
if
(
fcManager
==
null
)
{
fcManager
=
new
FontConfigManager
();
}
return
fcManager
;
}
@Override
protected
FontUIResource
getFontConfigFUIR
(
String
family
,
int
style
,
int
size
)
{
CompositeFont
font2D
=
getFontConfigManager
().
getFontConfigFont
(
family
,
style
);
if
(
font2D
==
null
)
{
// Not expected, just a precaution.
return
new
FontUIResource
(
family
,
style
,
size
);
}
/* The name of the font will be that of the physical font in slot,
* but by setting the handle to that of the CompositeFont it
* renders as that CompositeFont.
* It also needs to be marked as a created font which is the
* current mechanism to signal that deriveFont etc must copy
* the handle from the original font.
*/
FontUIResource
fuir
=
new
FontUIResource
(
font2D
.
getFamilyName
(
null
),
style
,
size
);
FontAccess
.
getFontAccess
().
setFont2D
(
fuir
,
font2D
.
handle
);
FontAccess
.
getFontAccess
().
setCreatedFont
(
fuir
);
return
fuir
;
}
}
src/solaris/classes/sun/font/FontConfigManager.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.font
;
import
java.util.Locale
;
import
java.util.logging.Logger
;
import
sun.awt.SunHints
;
import
sun.awt.SunToolkit
;
/**
* Small utility class to manage FontConfig.
*/
public
class
FontConfigManager
{
static
boolean
fontConfigFailed
=
false
;
/* This is populated by native */
private
static
final
FontConfigInfo
fcInfo
=
new
FontConfigInfo
();
/* Begin support for GTK Look and Feel - query libfontconfig and
* return a composite Font to Swing that uses the desktop font(s).
*/
/* These next three classes are just data structures.
*/
public
static
class
FontConfigFont
{
public
String
familyName
;
// eg Bitstream Vera Sans
public
String
styleStr
;
// eg Bold
public
String
fullName
;
// eg Bitstream Vera Sans Bold
public
String
fontFile
;
// eg /usr/X11/lib/fonts/foo.ttf
}
public
static
class
FcCompFont
{
public
String
fcName
;
// eg sans
public
String
fcFamily
;
// eg sans
public
String
jdkName
;
// eg sansserif
public
int
style
;
// eg 0=PLAIN
public
FontConfigFont
firstFont
;
public
FontConfigFont
[]
allFonts
;
//boolean preferBitmaps; // if embedded bitmaps preferred over AA
public
CompositeFont
compFont
;
// null if not yet created/known.
}
public
static
class
FontConfigInfo
{
public
int
fcVersion
;
public
String
[]
cacheDirs
=
new
String
[
4
];
}
/* fontconfig recognises slants roman, italic, as well as oblique,
* and a slew of weights, where the ones that matter here are
* regular and bold.
* To fully qualify what we want, we can for example ask for (eg)
* Font.PLAIN : "serif:regular:roman"
* Font.BOLD : "serif:bold:roman"
* Font.ITALIC : "serif:regular:italic"
* Font.BOLD|Font.ITALIC : "serif:bold:italic"
*/
private
static
String
[]
fontConfigNames
=
{
"sans:regular:roman"
,
"sans:bold:roman"
,
"sans:regular:italic"
,
"sans:bold:italic"
,
"serif:regular:roman"
,
"serif:bold:roman"
,
"serif:regular:italic"
,
"serif:bold:italic"
,
"monospace:regular:roman"
,
"monospace:bold:roman"
,
"monospace:regular:italic"
,
"monospace:bold:italic"
,
};
/* This array has the array elements created in Java code and is
* passed down to native to be filled in.
*/
private
FcCompFont
[]
fontConfigFonts
;
/**
* Instantiates a new FontConfigManager getting the default instance
* of FontManager from the FontManagerFactory.
*/
public
FontConfigManager
()
{
}
public
static
String
[]
getFontConfigNames
()
{
return
fontConfigNames
;
}
/* Called from code that needs to know what are the AA settings
* that apps using FC would pick up for the default desktop font.
* Note apps can change the default desktop font. etc, so this
* isn't certain to be right but its going to correct for most cases.
* Native return values map to the text aa values in sun.awt.SunHints.
* which is used to look up the renderinghint value object.
*/
public
static
Object
getFontConfigAAHint
()
{
return
getFontConfigAAHint
(
"sans"
);
}
/* This is public solely so that for debugging purposes it can be called
* with other names, which might (eg) include a size, eg "sans-24"
* The return value is a text aa rendering hint value.
* Normally we should call the no-args version.
*/
public
static
Object
getFontConfigAAHint
(
String
fcFamily
)
{
if
(
FontUtilities
.
isWindows
)
{
return
null
;
}
else
{
int
hint
=
getFontConfigAASettings
(
getFCLocaleStr
(),
fcFamily
);
if
(
hint
<
0
)
{
return
null
;
}
else
{
return
SunHints
.
Value
.
get
(
SunHints
.
INTKEY_TEXT_ANTIALIASING
,
hint
);
}
}
}
private
static
String
getFCLocaleStr
()
{
Locale
l
=
SunToolkit
.
getStartupLocale
();
String
localeStr
=
l
.
getLanguage
();
String
country
=
l
.
getCountry
();
if
(!
country
.
equals
(
""
))
{
localeStr
=
localeStr
+
"-"
+
country
;
}
return
localeStr
;
}
/* This does cause the native libfontconfig to be loaded and unloaded,
* but it does not incur the overhead of initialisation of its
* data structures, so shouldn't have a measurable impact.
*/
public
static
native
int
getFontConfigVersion
();
/* This can be made public if it's needed to force a re-read
* rather than using the cached values. The re-read would be needed
* only if some event signalled that the fontconfig has changed.
* In that event this method would need to return directly the array
* to be used by the caller in case it subsequently changed.
*/
public
synchronized
void
initFontConfigFonts
(
boolean
includeFallbacks
)
{
if
(
fontConfigFonts
!=
null
)
{
if
(!
includeFallbacks
||
(
fontConfigFonts
[
0
].
allFonts
!=
null
))
{
return
;
}
}
if
(
FontUtilities
.
isWindows
||
fontConfigFailed
)
{
return
;
}
long
t0
=
0
;
if
(
FontUtilities
.
isLogging
())
{
t0
=
System
.
nanoTime
();
}
String
[]
fontConfigNames
=
FontConfigManager
.
getFontConfigNames
();
FcCompFont
[]
fontArr
=
new
FcCompFont
[
fontConfigNames
.
length
];
for
(
int
i
=
0
;
i
<
fontArr
.
length
;
i
++)
{
fontArr
[
i
]
=
new
FcCompFont
();
fontArr
[
i
].
fcName
=
fontConfigNames
[
i
];
int
colonPos
=
fontArr
[
i
].
fcName
.
indexOf
(
':'
);
fontArr
[
i
].
fcFamily
=
fontArr
[
i
].
fcName
.
substring
(
0
,
colonPos
);
fontArr
[
i
].
jdkName
=
FontUtilities
.
mapFcName
(
fontArr
[
i
].
fcFamily
);
fontArr
[
i
].
style
=
i
%
4
;
// depends on array order.
}
getFontConfig
(
getFCLocaleStr
(),
fcInfo
,
fontArr
,
includeFallbacks
);
/* If don't find anything (eg no libfontconfig), then just return */
for
(
int
i
=
0
;
i
<
fontArr
.
length
;
i
++)
{
FcCompFont
fci
=
fontArr
[
i
];
if
(
fci
.
firstFont
==
null
)
{
if
(
FontUtilities
.
isLogging
())
{
Logger
logger
=
FontUtilities
.
getLogger
();
logger
.
info
(
"Fontconfig returned no fonts."
);
}
fontConfigFailed
=
true
;
return
;
}
}
fontConfigFonts
=
fontArr
;
if
(
FontUtilities
.
isLogging
())
{
Logger
logger
=
FontUtilities
.
getLogger
();
long
t1
=
System
.
nanoTime
();
logger
.
info
(
"Time spent accessing fontconfig="
+
((
t1
-
t0
)
/
1000000
)
+
"ms."
);
for
(
int
i
=
0
;
i
<
fontConfigFonts
.
length
;
i
++)
{
FcCompFont
fci
=
fontConfigFonts
[
i
];
logger
.
info
(
"FC font "
+
fci
.
fcName
+
" maps to family "
+
fci
.
firstFont
.
familyName
+
" in file "
+
fci
.
firstFont
.
fontFile
);
if
(
fci
.
allFonts
!=
null
)
{
for
(
int
f
=
0
;
f
<
fci
.
allFonts
.
length
;
f
++)
{
FontConfigFont
fcf
=
fci
.
allFonts
[
f
];
logger
.
info
(
"Family="
+
fcf
.
familyName
+
" Style="
+
fcf
.
styleStr
+
" Fullname="
+
fcf
.
fullName
+
" File="
+
fcf
.
fontFile
);
}
}
}
}
}
public
PhysicalFont
registerFromFcInfo
(
FcCompFont
fcInfo
)
{
SunFontManager
fm
=
SunFontManager
.
getInstance
();
/* If it's a TTC file we need to know that as we will need to
* make sure we return the right font */
String
fontFile
=
fcInfo
.
firstFont
.
fontFile
;
int
offset
=
fontFile
.
length
()-
4
;
if
(
offset
<=
0
)
{
return
null
;
}
String
ext
=
fontFile
.
substring
(
offset
).
toLowerCase
();
boolean
isTTC
=
ext
.
equals
(
".ttc"
);
/* If this file is already registered, can just return its font.
* However we do need to check in case it's a TTC as we need
* a specific font, so rather than directly returning it, let
* findFont2D resolve that.
*/
PhysicalFont
physFont
=
fm
.
getRegisteredFontFile
(
fontFile
);
if
(
physFont
!=
null
)
{
if
(
isTTC
)
{
Font2D
f2d
=
fm
.
findFont2D
(
fcInfo
.
firstFont
.
familyName
,
fcInfo
.
style
,
FontManager
.
NO_FALLBACK
);
if
(
f2d
instanceof
PhysicalFont
)
{
/* paranoia */
return
(
PhysicalFont
)
f2d
;
}
else
{
return
null
;
}
}
else
{
return
physFont
;
}
}
/* If the font may hide a JRE font (eg fontconfig says it is
* Lucida Sans), we want to use the JRE version, so make it
* point to the JRE font.
*/
physFont
=
fm
.
findJREDeferredFont
(
fcInfo
.
firstFont
.
familyName
,
fcInfo
.
style
);
/* It is also possible the font file is on the "deferred" list,
* in which case we can just initialise it now.
*/
if
(
physFont
==
null
&&
fm
.
isDeferredFont
(
fontFile
)
==
true
)
{
physFont
=
fm
.
initialiseDeferredFont
(
fcInfo
.
firstFont
.
fontFile
);
/* use findFont2D to get the right font from TTC's */
if
(
physFont
!=
null
)
{
if
(
isTTC
)
{
Font2D
f2d
=
fm
.
findFont2D
(
fcInfo
.
firstFont
.
familyName
,
fcInfo
.
style
,
FontManager
.
NO_FALLBACK
);
if
(
f2d
instanceof
PhysicalFont
)
{
/* paranoia */
return
(
PhysicalFont
)
f2d
;
}
else
{
return
null
;
}
}
else
{
return
physFont
;
}
}
}
/* In the majority of cases we reach here, and need to determine
* the type and rank to register the font.
*/
if
(
physFont
==
null
)
{
int
fontFormat
=
SunFontManager
.
FONTFORMAT_NONE
;
int
fontRank
=
Font2D
.
UNKNOWN_RANK
;
if
(
ext
.
equals
(
".ttf"
)
||
isTTC
)
{
fontFormat
=
SunFontManager
.
FONTFORMAT_TRUETYPE
;
fontRank
=
Font2D
.
TTF_RANK
;
}
else
if
(
ext
.
equals
(
".pfa"
)
||
ext
.
equals
(
".pfb"
))
{
fontFormat
=
SunFontManager
.
FONTFORMAT_TYPE1
;
fontRank
=
Font2D
.
TYPE1_RANK
;
}
physFont
=
fm
.
registerFontFile
(
fcInfo
.
firstFont
.
fontFile
,
null
,
fontFormat
,
true
,
fontRank
);
}
return
physFont
;
}
/*
* We need to return a Composite font which has as the font in
* its first slot one obtained from fontconfig.
*/
public
CompositeFont
getFontConfigFont
(
String
name
,
int
style
)
{
name
=
name
.
toLowerCase
();
initFontConfigFonts
(
false
);
FcCompFont
fcInfo
=
null
;
for
(
int
i
=
0
;
i
<
fontConfigFonts
.
length
;
i
++)
{
if
(
name
.
equals
(
fontConfigFonts
[
i
].
fcFamily
)
&&
style
==
fontConfigFonts
[
i
].
style
)
{
fcInfo
=
fontConfigFonts
[
i
];
break
;
}
}
if
(
fcInfo
==
null
)
{
fcInfo
=
fontConfigFonts
[
0
];
}
if
(
FontUtilities
.
isLogging
())
{
FontUtilities
.
getLogger
()
.
info
(
"FC name="
+
name
+
" style="
+
style
+
" uses "
+
fcInfo
.
firstFont
.
familyName
+
" in file: "
+
fcInfo
.
firstFont
.
fontFile
);
}
if
(
fcInfo
.
compFont
!=
null
)
{
return
fcInfo
.
compFont
;
}
/* jdkFont is going to be used for slots 1..N and as a fallback.
* Slot 0 will be the physical font from fontconfig.
*/
FontManager
fm
=
FontManagerFactory
.
getInstance
();
CompositeFont
jdkFont
=
(
CompositeFont
)
fm
.
findFont2D
(
fcInfo
.
jdkName
,
style
,
FontManager
.
LOGICAL_FALLBACK
);
if
(
fcInfo
.
firstFont
.
familyName
==
null
||
fcInfo
.
firstFont
.
fontFile
==
null
)
{
return
(
fcInfo
.
compFont
=
jdkFont
);
}
/* First, see if the family and exact style is already registered.
* If it is, use it. If it's not, then try to register it.
* If that registration fails (signalled by null) just return the
* regular JDK composite.
* Algorithmically styled fonts won't match on exact style, so
* will fall through this code, but the regisration code will
* find that file already registered and return its font.
*/
FontFamily
family
=
FontFamily
.
getFamily
(
fcInfo
.
firstFont
.
familyName
);
PhysicalFont
physFont
=
null
;
if
(
family
!=
null
)
{
Font2D
f2D
=
family
.
getFontWithExactStyleMatch
(
fcInfo
.
style
);
if
(
f2D
instanceof
PhysicalFont
)
{
physFont
=
(
PhysicalFont
)
f2D
;
}
}
if
(
physFont
==
null
||
!
fcInfo
.
firstFont
.
fontFile
.
equals
(
physFont
.
platName
))
{
physFont
=
registerFromFcInfo
(
fcInfo
);
if
(
physFont
==
null
)
{
return
(
fcInfo
.
compFont
=
jdkFont
);
}
family
=
FontFamily
.
getFamily
(
physFont
.
getFamilyName
(
null
));
}
/* Now register the fonts in the family (the other styles) after
* checking that they aren't already registered and are actually in
* a different file. They may be the same file in CJK cases.
* For cases where they are different font files - eg as is common for
* Latin fonts, then we rely on fontconfig to report these correctly.
* Assume that all styles of this font are found by fontconfig,
* so we can find all the family members which must be registered
* together to prevent synthetic styling.
*/
for
(
int
i
=
0
;
i
<
fontConfigFonts
.
length
;
i
++)
{
FcCompFont
fc
=
fontConfigFonts
[
i
];
if
(
fc
!=
fcInfo
&&
physFont
.
getFamilyName
(
null
).
equals
(
fc
.
firstFont
.
familyName
)
&&
!
fc
.
firstFont
.
fontFile
.
equals
(
physFont
.
platName
)
&&
family
.
getFontWithExactStyleMatch
(
fc
.
style
)
==
null
)
{
registerFromFcInfo
(
fontConfigFonts
[
i
]);
}
}
/* Now we have a physical font. We will back this up with the JDK
* logical font (sansserif, serif, or monospaced) that corresponds
* to the Pango/GTK/FC logical font name.
*/
return
(
fcInfo
.
compFont
=
new
CompositeFont
(
physFont
,
jdkFont
));
}
/**
*
* @param locale
* @param fcFamily
* @return
*/
public
FcCompFont
[]
getFontConfigFonts
()
{
return
fontConfigFonts
;
}
/* Return an array of FcCompFont structs describing the primary
* font located for each of fontconfig/GTK/Pango's logical font names.
*/
private
static
native
void
getFontConfig
(
String
locale
,
FontConfigInfo
fcInfo
,
FcCompFont
[]
fonts
,
boolean
includeFallbacks
);
void
populateFontConfig
(
FcCompFont
[]
fcInfo
)
{
fontConfigFonts
=
fcInfo
;
}
FcCompFont
[]
loadFontConfig
()
{
initFontConfigFonts
(
true
);
return
fontConfigFonts
;
}
FontConfigInfo
getFontConfigInfo
()
{
initFontConfigFonts
(
true
);
return
fcInfo
;
}
private
static
native
int
getFontConfigAASettings
(
String
locale
,
String
fcFamily
);
}
src/windows/classes/sun/awt/Win32FontManager.java
0 → 100644
浏览文件 @
84c7e7ae
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.awt
;
import
java.awt.FontFormatException
;
import
java.awt.GraphicsEnvironment
;
import
java.io.File
;
import
java.security.AccessController
;
import
java.security.PrivilegedAction
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.Locale
;
import
java.util.NoSuchElementException
;
import
java.util.StringTokenizer
;
import
sun.awt.Win32GraphicsEnvironment
;
import
sun.awt.windows.WFontConfiguration
;
import
sun.font.FontManager
;
import
sun.font.SunFontManager
;
import
sun.font.TrueTypeFont
;
import
sun.java2d.HeadlessGraphicsEnvironment
;
import
sun.java2d.SunGraphicsEnvironment
;
/**
* The X11 implementation of {@link FontManager}.
*/
public
class
Win32FontManager
extends
SunFontManager
{
private
static
String
[]
defaultPlatformFont
=
null
;
private
static
TrueTypeFont
eudcFont
;
static
{
AccessController
.
doPrivileged
(
new
PrivilegedAction
()
{
public
Object
run
()
{
String
eudcFile
=
getEUDCFontFile
();
if
(
eudcFile
!=
null
)
{
try
{
eudcFont
=
new
TrueTypeFont
(
eudcFile
,
null
,
0
,
true
);
}
catch
(
FontFormatException
e
)
{
}
}
return
null
;
}
});
}
/* Used on Windows to obtain from the windows registry the name
* of a file containing the system EUFC font. If running in one of
* the locales for which this applies, and one is defined, the font
* defined by this file is appended to all composite fonts as a
* fallback component.
*/
private
static
native
String
getEUDCFontFile
();
public
Win32FontManager
()
{
super
();
AccessController
.
doPrivileged
(
new
PrivilegedAction
()
{
public
Object
run
()
{
/* Register the JRE fonts so that the native platform can
* access them. This is used only on Windows so that when
* printing the printer driver can access the fonts.
*/
registerJREFontsWithPlatform
(
jreFontDirName
);
return
null
;
}
});
}
/* Unlike the shared code version, this expects a base file name -
* not a full path name.
* The font configuration file has base file names and the FontConfiguration
* class reports these back to the GraphicsEnvironment, so these
* are the componentFileNames of CompositeFonts.
*/
protected
void
registerFontFile
(
String
fontFileName
,
String
[]
nativeNames
,
int
fontRank
,
boolean
defer
)
{
// REMIND: case compare depends on platform
if
(
registeredFontFiles
.
contains
(
fontFileName
))
{
return
;
}
registeredFontFiles
.
add
(
fontFileName
);
int
fontFormat
;
if
(
getTrueTypeFilter
().
accept
(
null
,
fontFileName
))
{
fontFormat
=
SunFontManager
.
FONTFORMAT_TRUETYPE
;
}
else
if
(
getType1Filter
().
accept
(
null
,
fontFileName
))
{
fontFormat
=
SunFontManager
.
FONTFORMAT_TYPE1
;
}
else
{
/* on windows we don't use/register native fonts */
return
;
}
if
(
fontPath
==
null
)
{
fontPath
=
getPlatformFontPath
(
noType1Font
);
}
/* Look in the JRE font directory first.
* This is playing it safe as we would want to find fonts in the
* JRE font directory ahead of those in the system directory
*/
String
tmpFontPath
=
jreFontDirName
+
File
.
pathSeparator
+
fontPath
;
StringTokenizer
parser
=
new
StringTokenizer
(
tmpFontPath
,
File
.
pathSeparator
);
boolean
found
=
false
;
try
{
while
(!
found
&&
parser
.
hasMoreTokens
())
{
String
newPath
=
parser
.
nextToken
();
File
theFile
=
new
File
(
newPath
,
fontFileName
);
if
(
theFile
.
canRead
())
{
found
=
true
;
String
path
=
theFile
.
getAbsolutePath
();
if
(
defer
)
{
registerDeferredFont
(
fontFileName
,
path
,
nativeNames
,
fontFormat
,
true
,
fontRank
);
}
else
{
registerFontFile
(
path
,
nativeNames
,
fontFormat
,
true
,
fontRank
);
}
break
;
}
}
}
catch
(
NoSuchElementException
e
)
{
System
.
err
.
println
(
e
);
}
if
(!
found
)
{
addToMissingFontFileList
(
fontFileName
);
}
}
@Override
protected
FontConfiguration
createFontConfiguration
()
{
FontConfiguration
fc
=
new
WFontConfiguration
(
this
);
fc
.
init
();
return
fc
;
}
@Override
public
FontConfiguration
createFontConfiguration
(
boolean
preferLocaleFonts
,
boolean
preferPropFonts
)
{
return
new
WFontConfiguration
(
this
,
preferLocaleFonts
,
preferPropFonts
);
}
protected
void
populateFontFileNameMap
(
HashMap
<
String
,
String
>
fontToFileMap
,
HashMap
<
String
,
String
>
fontToFamilyNameMap
,
HashMap
<
String
,
ArrayList
<
String
>>
familyToFontListMap
,
Locale
locale
)
{
populateFontFileNameMap0
(
fontToFileMap
,
fontToFamilyNameMap
,
familyToFontListMap
,
locale
);
}
private
static
native
void
populateFontFileNameMap0
(
HashMap
<
String
,
String
>
fontToFileMap
,
HashMap
<
String
,
String
>
fontToFamilyNameMap
,
HashMap
<
String
,
ArrayList
<
String
>>
familyToFontListMap
,
Locale
locale
);
public
synchronized
native
String
getFontPath
(
boolean
noType1Fonts
);
public
String
[]
getDefaultPlatformFont
()
{
if
(
defaultPlatformFont
!=
null
)
{
return
defaultPlatformFont
;
}
String
[]
info
=
new
String
[
2
];
info
[
0
]
=
"Arial"
;
info
[
1
]
=
"c:\\windows\\fonts"
;
final
String
[]
dirs
=
getPlatformFontDirs
(
true
);
if
(
dirs
.
length
>
1
)
{
String
dir
=
(
String
)
AccessController
.
doPrivileged
(
new
PrivilegedAction
()
{
public
Object
run
()
{
for
(
int
i
=
0
;
i
<
dirs
.
length
;
i
++)
{
String
path
=
dirs
[
i
]
+
File
.
separator
+
"arial.ttf"
;
File
file
=
new
File
(
path
);
if
(
file
.
exists
())
{
return
dirs
[
i
];
}
}
return
null
;
}
});
if
(
dir
!=
null
)
{
info
[
1
]
=
dir
;
}
}
else
{
info
[
1
]
=
dirs
[
0
];
}
info
[
1
]
=
info
[
1
]
+
File
.
separator
+
"arial.ttf"
;
defaultPlatformFont
=
info
;
return
defaultPlatformFont
;
}
/* register only TrueType/OpenType fonts
* Because these need to be registed just for use when printing,
* we defer the actual registration and the static initialiser
* for the printing class makes the call to registerJREFontsForPrinting()
*/
static
String
fontsForPrinting
=
null
;
protected
void
registerJREFontsWithPlatform
(
String
pathName
)
{
fontsForPrinting
=
pathName
;
}
public
static
void
registerJREFontsForPrinting
()
{
final
String
pathName
;
synchronized
(
Win32GraphicsEnvironment
.
class
)
{
GraphicsEnvironment
.
getLocalGraphicsEnvironment
();
if
(
fontsForPrinting
==
null
)
{
return
;
}
pathName
=
fontsForPrinting
;
fontsForPrinting
=
null
;
}
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
File
f1
=
new
File
(
pathName
);
String
[]
ls
=
f1
.
list
(
SunFontManager
.
getInstance
().
getTrueTypeFilter
());
if
(
ls
==
null
)
{
return
null
;
}
for
(
int
i
=
0
;
i
<
ls
.
length
;
i
++
)
{
File
fontFile
=
new
File
(
f1
,
ls
[
i
]);
registerFontWithPlatform
(
fontFile
.
getAbsolutePath
());
}
return
null
;
}
});
}
protected
static
native
void
registerFontWithPlatform
(
String
fontName
);
protected
static
native
void
deRegisterFontWithPlatform
(
String
fontName
);
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录