blob: 2e4c0a88293361b9c9510810e2f76bdb4d7fbbe4 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package flash.fonts;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.Map;
import java.util.StringTokenizer;
import flash.swf.tags.DefineFont;
import flash.util.Trace;
/**
* The FontManager provides a common interface to locating fonts from
* either locally (i.e. from the Operating System) or externally
* (i.e. from URL locations).
*/
@SuppressWarnings("unchecked")
public abstract class FontManager
{
public static final String LOCAL_FONT_PATHS = "local-font-paths";
public static final int PLAIN = 0;
public static final int BOLD = 1;
public static final int ITALIC = 2;
protected Properties languageRanges;
protected FontManager parent;
protected int majorCompatibilityVersion = 4;
/**
* Constructor
*/
protected FontManager()
{
}
/**
* Initialization properties can be provided as name/value pairs.
*
* @param map
*/
public void initialize(Map map)
{
if (map != null)
{
String compatVersion = (String)map.get(CachedFontManager.COMPATIBILITY_VERSION);
if (compatVersion != null)
{
String[] parts = compatVersion.split("\\.");
if (parts.length > 0)
{
try
{
int major = Integer.parseInt(parts[0]);
majorCompatibilityVersion = major;
}
catch (Throwable t)
{
}
}
}
}
}
/**
* Provides the ability to chain managers.
*
* @param parent
*/
public void setParent(FontManager parent)
{
this.parent = parent;
}
public void setLanguageRange(Properties languageRanges)
{
this.languageRanges = languageRanges;
}
/**
* If a given language token is registered, the corresponding unicode range
* (specified as a CSS-2 formatted string) is returned.
*
* @param lang
*/
public String getLanguageRange(String lang)
{
String range = null;
if (languageRanges != null && lang != null)
range = languageRanges.getProperty(lang);
return range;
}
/**
* Create a SWF DefineFont tag from a font file location specified as a URL.
*
* @param tagCode Specifies the version of the DefineFont SWF tag to create.
* @return A DefineFont tag
*/
public DefineFont createDefineFont(int tagCode, FontDescription desc)
{
// No op
return null;
}
/**
* Attempts to load a font from the cache by location or from disk if it is
* the first request at this address. The location is bound to a font family
* name and defineFont type after the initial loading, and the relationship
* exists for the lifetime of the cache.
*
* @param location
* @param style
* @return FontSet.FontFace
*/
public abstract FontFace getEntryFromLocation(URL location, int style,
boolean useTwips);
/**
* Attempts to locate a font by family name, style, and defineFont type from
* the runtime's list of fonts, which are primarily operating system
* registered fonts.
*
* @param familyName
* @param style either FontFace.PLAIN, FontFace.BOLD, FontFace.ITALIC or
* FontFace.BOLD+FontFace.ITALIC
* @return FontFace
*/
public abstract FontFace getEntryFromSystem(String familyName, int style,
boolean useTwips);
/**
* Allows a DefineFont SWF tag to be the basis of a FontFace.
*
* @param tag The DefineFont tag
* @param location The original location of the asset that created the
* DefineFont SWF tag.
*/
public void loadDefineFont(DefineFont tag, Object location)
{
// No-op
}
/**
* Allows a DefineFont SWF tag to be the basis of a FontFace.
*
* @param tag The DefineFont tag.
*/
public void loadDefineFont(DefineFont tag)
{
loadDefineFont(tag, null);
}
/**
* Parses a String representation of Unicode character ranges into an array
* of int arrays. e.g. U+0020-U+007F,U+20345. Note that int is used to
* support code points beyond the BMP.
*
* @param value String representation of unicode character ranges
* @return int[][] Array of an array of ints representing code points of
* the specified ranges.
* @see http://www.w3.org/TR/REC-CSS2/fonts.html#descdef-unicode-range
*/
public int[][] getUnicodeRanges(String value)
{
int[][] ranges = null;
// Check if it's a registered language name
String langRange = getLanguageRange(value);
if (langRange != null)
value = langRange;
if (value != null)
{
// Remove extraneous formatting first
value = value.replace(';', ' ').replace('\n', ' ').replace('\r', ' ').replace('\f', ' ');
StringTokenizer st = new StringTokenizer(value, ",");
int count = st.countTokens();
ranges = new int[count][2];
parseRanges(st, ranges);
}
return ranges;
}
public static boolean isItalic(int style)
{
return style == ITALIC || style == (BOLD + ITALIC);
}
public static boolean isBold(int style)
{
return style == BOLD || style == (BOLD + ITALIC);
}
/**
* Given a list of class names, this utility method attempts to construct a
* chain of FontManagers. The class must extend FontManager and have a
* public no-args constructor. Invalid classes are skipped.
*
* @param managerClasses
* @return the last FontManager in the chain
* @deprecated
*/
public static FontManager create(List managerClasses, Map map)
{
return FontManager.create(managerClasses, map, null);
}
/**
* Given a list of class names, this utility method attempts to construct a
* chain of FontManagers. The class must extend FontManager and have a
* public no-args constructor. Invalid classes are skipped.
*
* @param managerClasses List of class names representing FontManager
* implementations.
* @param map A Map of settings to be passed to the FontManager instance
* during initialization.
* @param languageRanges List of unicode character ranges for a given
* language.
* @return the last FontManager in the chain
*/
public static FontManager create(List managerClasses, Map map, Properties languageRanges)
{
FontManager manager = null;
if (managerClasses != null)
{
for (int i = 0; i < managerClasses.size(); i++)
{
try
{
Object className = managerClasses.get(i);
if (className != null)
{
Class clazz = Class.forName(className.toString());
Object obj = clazz.newInstance();
if (obj instanceof FontManager)
{
FontManager fm = (FontManager)obj;
fm.initialize(map);
if (manager != null)
fm.setParent(manager);
if (languageRanges != null)
fm.setLanguageRange(languageRanges);
manager = fm;
}
}
}
catch (Throwable t)
{
if (Trace.font)
{
Trace.trace(t.getMessage());
}
}
}
}
return manager;
}
public static void throwFontNotFound(String alias, String fontFamily, int style, String location)
{
StringBuilder message = new StringBuilder("Font for alias '");
message.append(alias).append("' ");
if (style == FontFace.BOLD)
{
message.append("with bold weight ");
}
else if (style == FontFace.ITALIC)
{
message.append("with italic style ");
}
else if (style == (FontFace.BOLD + FontFace.ITALIC))
{
message.append("with bold weight and italic style ");
}
else
{
message.append("with plain weight and style ");
}
if (location != null)
{
message.append("was not found at: ").append(location.toString());
}
else
{
message.append("was not found by family name '").append(fontFamily).append("'");
}
throw new FontNotFoundException(message.toString());
}
/**
* Values are expressed as hexadecimal numbers, prefixed with
* &quot;U+&quot;. For single numbers, the character '?' is assumed to mean
* 'any value' which creates a range of character positions. Otherwise, the
* range can be specified explicitly using a hyphen, e.g. U+00A0-U+00FF
*
* @param st
* @param ranges
*/
private static void parseRanges(StringTokenizer st, int[][] ranges)
{
int i = 0;
while (st.hasMoreElements())
{
String element = ((String)st.nextElement()).trim().toUpperCase();
if (element.startsWith("U+"))
{
String range = element.substring(2).trim();
String low;
String high;
if (range.indexOf('?') > 0) // Wild-Card Range, e.g. U+00??
{
low = range.replace('?', '0');
high = range.replace('?', 'F');
}
else if (range.indexOf('-') > 0) // Basic Range, e.g. U+0020-007E
{
low = range.substring(0, range.indexOf('-'));
String temp = range.substring(range.indexOf('-') + 1).trim();
// Support Flex's legacy additional U+ prefix on the
// high range (but not part of the CSS-2 specification).
if (temp.startsWith("U+"))
{
high = temp.substring(2).trim();
}
else
{
high = temp;
}
}
else if (range.length() <= 8) // Single Char, e.g. U+0041
{
low = range;
high = range;
}
else
{
throw new InvalidUnicodeRangeException(range);
}
try
{
ranges[i][0] = Integer.parseInt(low, 16);
ranges[i][1] = Integer.parseInt(high, 16);
}
catch (Exception ex)
{
throw new InvalidUnicodeRangeException(range);
}
i++;
}
else if (element.length() == 0)
{
continue;
}
else
{
throw new InvalidUnicodeRangeException(element);
}
}
}
public static final class FontNotFoundException extends RuntimeException
{
private static final long serialVersionUID = -2385779348825570473L;
public FontNotFoundException(String message)
{
super(message);
}
}
public static final class InvalidUnicodeRangeException extends
RuntimeException
{
private static final long serialVersionUID = 3173208110428813980L;
public InvalidUnicodeRangeException(String range)
{
this.range = range;
}
public String range;
}
}