All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.fontengine.inlineformatting.FallbackFontSet Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * File: FallbackFontSet.java
 * 
 *	ADOBE CONFIDENTIAL
 *	___________________
 *
 *	Copyright 2005 Adobe Systems Incorporated
 *	All Rights Reserved.
 *
 *	NOTICE: All information contained herein is, and remains the property of
 *	Adobe Systems Incorporated and its suppliers, if any. The intellectual
 *	and technical concepts contained herein are proprietary to Adobe Systems
 *	Incorporated and its suppliers and may be covered by U.S. and Foreign
 *	Patents, patents in process, and are protected by trade secret or
 *	copyright law. Dissemination of this information or reproduction of this
 *	material is strictly forbidden unless prior written permission is
 *      obtained from Adobe Systems Incorporated.
 *
 */
package com.adobe.fontengine.inlineformatting;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.TreeMap;

import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.font.Font;
import com.adobe.fontengine.font.FontException;
import com.adobe.fontengine.fontmanagement.postscript.PostscriptFontDescription;

/**
 * A set of fonts indexed by {@link ULocale}, typically used for fallback.
 * 
 * 

* A {@link FallbackFontSet} is a set of fonts indexed by {@link ULocale}. A * typical use is during formatting of text: if the requested font cannot be * found or does not contain suitable glyphs for a portion of the text being * formatted, the formatter can enumerate the fallback fonts for the locale * of the text until a suitable font is found. Indexing by locale (by opposition * to a flat set) improves the quality of the generated rendering; for example, * some characters are used to write both Japanese and Chinese, but different * shapes are expected. * *

* The {@link #addFallbackFont(ULocale, Font)} and * {@link #addFallbackFonts(ULocale, Font[])} methods allow the addition of one * or more fonts to a set, for a given locale. * *

* The {@link #getFallbackFonts(ULocale)} method returns an {@link Iterator} * which first enumerates the fonts for a locale (in the order in which they * were added), then the fonts for the fallback locale of that locale, and so on * up to the fonts for the {@link ULocale#ROOT}locale. * *

* For example, with a set setup like this: * *

 * set.add (new ULocale ("en_US"), us1);
 * set.add (new ULocale ("en_US"), us2);
 * set.add (new ULocale ("en_CA"), ca1);
 * set.add (new ULocale ("en_CA"), ca2);
 * set.add (new ULocale ("en"), en1);
 * set.add (new ULocale ("en"), en2);
 * set.add (ULocale.ROOT, r1);
 * set.add (ULocale.ROOT, r2);
 * 
* * then the fallback fonts for the locale en_US are * {us1, us2, en1, en2, r1, r2} in that order, the fallback fonts * for the locale en_CA are * {ca1, ca2, en1, en2, r1, r2} in that order, the fallback fonts * for the locale en are {en1, en2, r1, r2} in * that order, and the fallback fonts for the locale it are * {r1, r2} in that order. * *

Synchronization

* * {@link FallbackFontSet}objects are suitably synchronized for use in multiple * threads. * *

Serialization

* * {@link FallbackFontSet}objects can be serialized, provided that they contain * only fonts which can be serialized. */ public final class FallbackFontSet implements Serializable { /* Serialization signature is explicitly set and should be * incremented on each release to prevent compatibility. */ static final long serialVersionUID = 1; /* synchronized via this. */ private final HashMap /*>*/ fonts; private static final ULocale REAL_ROOT = new ULocale(""); /** * Create an empty FallbackFontSet. */ public FallbackFontSet () { this.fonts = new HashMap (); } /** * Create a FallbackFontSet by copying an existing one. */ public FallbackFontSet (FallbackFontSet ffs) { this.fonts = new HashMap (); synchronized (ffs) { for (Iterator it = ffs.fonts.keySet().iterator(); it.hasNext (); ) { Object locale = it.next (); List l = new ArrayList/**/ (); this.fonts.put (locale, l); for (Iterator it2 = ((List) ffs.fonts.get (locale)).iterator(); it2.hasNext(); ) { l.add (it2.next ()); } } } } /** * Add each font in an array to the set. * * @param locale the locale for which those fonts are appropriate * @param fontsToAdd the fonts to add */ public void addFallbackFonts (ULocale locale, Font[] fontsToAdd) { for (Font font : fontsToAdd) { addFallbackFont(locale, font); } } /** * Add a font to the set * * @param locale the locale for which the font is appropriate * @param font the font to add */ public void addFallbackFont (ULocale locale, Font font) { synchronized (this) { if (locale.equals(ULocale.ROOT)) { locale = FallbackFontSet.REAL_ROOT; } List/**/ l = (List/**/) fonts.get (locale); if (l == null) { l = new ArrayList/**/ (); fonts.put (locale, l); } l.add (font); } } private final class FallbackFontIterator implements Iterator { /* invariant: the fonts that remain to be enumerated are * - those remaining in localeIt, if it is non-null * - those associated with locale and its fallback locales * * Those two members are synchronized via this. */ private ULocale locale; private Iterator/**/ localeIt; public FallbackFontIterator (ULocale locale) { this.locale = locale; this.localeIt = null; } public boolean hasNext () { synchronized (this) { while (localeIt != null || locale != null) { if (localeIt == null) { List/**/ l = (List/**/) fonts.get (locale); if (l != null) { localeIt = l.iterator (); } locale = locale.getFallback (); } else { if (localeIt.hasNext ()) { return true; } localeIt = null; } } } return false; } public Object next () throws NoSuchElementException { synchronized (this) { if (hasNext ()) { // hasNext returns true only when localeIt != null and // localeIt.hasNext == true, so this is safe. return localeIt.next (); } } throw new NoSuchElementException (); } public void remove () throws UnsupportedOperationException { throw new UnsupportedOperationException ("cannot remove elements from a FallbackFontIterator"); } } /** * Enumerate the fonts for a locale. * * The fonts for locale are enumerated, then the fonts for the * fallback locale of locale, and so on. */ public Iterator getFallbackFonts (ULocale locale) { synchronized (this) { return new FallbackFontIterator (locale); } } public boolean equals (Object otherObject) { if (otherObject == this) { return true; } if (otherObject == null || ! (otherObject instanceof FallbackFontSet)) { return false; } return this.fonts.equals (((FallbackFontSet) otherObject).fonts); } public int hashCode () { return fonts.hashCode (); } /** * A test on whether this fontset contains any fonts. * * @return true if the fontset is empty, false otherwise */ public boolean isEmpty () { return this.fonts.isEmpty(); } /** * A test on whether this fontset contains any fonts for the given locale. * * @param locale the locale to test * @return true if the fontset has no fonts for the given locale, false otherwise */ public boolean isEmpty (ULocale locale) { return this.fonts.containsKey(locale); } public String toString() { TreeMap tempMap = new TreeMap(); for (Iterator it = fonts.keySet().iterator(); it.hasNext(); ) { ULocale locale = (ULocale)it.next(); tempMap.put(locale.getDisplayName(), locale); } HashMap tempMap1 = new HashMap(); for (Iterator it = tempMap.keySet().iterator(); it.hasNext(); ) { String displayName = (String)it.next(); ULocale locale = (ULocale)tempMap.get(displayName); TreeMap tempMap2 = new TreeMap(); for (Iterator it1 = ((List)fonts.get(locale)).iterator(); it1.hasNext(); ) { Font f = (Font)it1.next(); PostscriptFontDescription[] descs; TreeMap tempMap3 = new TreeMap(); try { descs = f.getPostscriptFontDescription(); for (int i = 0; i < descs.length; i++) { String name = descs[i].toString(); int count = 0; if (tempMap3.containsKey(name)) { count = ((Integer)tempMap3.get(name)).intValue(); } tempMap3.put(name, new Integer(++count)); } } catch (FontException e) { } char suffix = '0'; String tempKey = tempMap3.isEmpty() ? "" : (String)tempMap3.firstKey(); while (tempMap2.containsKey(tempKey + suffix)) { suffix++; } tempMap2.put(tempKey + suffix, tempMap3); } tempMap1.put(locale, tempMap2); } StringBuffer sb = new StringBuffer(); for (Iterator it = tempMap.keySet().iterator(); it.hasNext(); ) { String displayName = (String)it.next(); ULocale locale = (ULocale)tempMap.get(displayName); sb.append(displayName); sb.append(": "); String prefix2 = ""; TreeMap tempMap2 = (TreeMap)tempMap1.get(locale); for (Iterator it1 = tempMap2.keySet().iterator(); it1.hasNext(); ) { TreeMap tempMap3 = (TreeMap)tempMap2.get(it1.next()); sb.append(prefix2); sb.append("{"); String prefix = ""; for (Iterator it2 = tempMap3.keySet().iterator(); it2.hasNext(); ) { String name = (String)it2.next(); int count = ((Integer)tempMap3.get(name)).intValue(); while (count-- > 0) { sb.append(prefix); sb.append(name); prefix = ", "; } } sb.append("}"); sb.append(prefix2); prefix2 = ", "; } sb.append("\n"); } return sb.toString(); } }