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

com.adobe.fontengine.inlineformatting.css20.CSS20FontDatabase Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 *
 *	File: CSSFontDatabase.java
 *
 *
 *	ADOBE CONFIDENTIAL
 *	___________________
 *
 *	Copyright 2004-2006 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.css20;


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

import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.font.Font;
import com.adobe.fontengine.font.FontException;
import com.adobe.fontengine.font.FontLoadingException;
import com.adobe.fontengine.font.InvalidFontException;
import com.adobe.fontengine.font.UnsupportedFontException;
import com.adobe.fontengine.fontmanagement.FontProxy;
import com.adobe.fontengine.fontmanagement.FontResolutionPriority;
import com.adobe.fontengine.fontmanagement.FontSetStack;
import com.adobe.fontengine.fontmanagement.IntelligentResolver;
import com.adobe.fontengine.inlineformatting.FallbackFontSet;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute.CSSStretchValue;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute.CSSStyleValue;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute.CSSVariantValue;

/**
 * CSS20FontDatabase
 */
final public class CSS20FontDatabase extends FontSetStack implements CSS20FontSet
{
	/* Serialization signature is explicitly set and should be 
	 * incremented on each release to prevent compatibility.
	 */
	static final long serialVersionUID = 1;

	private final FamilyNameNormalizer normalizer;
	private final boolean ignoreVariant;

	/* The CSSPDF16FontDescription objects are stored in ArrayLists with all of the other fonts that
	 * share the same name.  Each of those ArrayLists is stored in a HashMap with the key
	 * being the font name.
	 */
	private HashMap/**/ fontsByFamilyName = new HashMap();
	private FontResolutionPriority resolutionPriority = FontResolutionPriority.FIRST;

	// Generic font families and the fallback fonts
	private ArrayList/**/ serif = new ArrayList();
	private ArrayList/**/ sansSerif = new ArrayList();
	private ArrayList/**/ monospace = new ArrayList();
	private ArrayList/**/ fantasy = new ArrayList();
	private ArrayList/**/ cursive = new ArrayList();

	private FallbackFontSet fallbackFontSet = new FallbackFontSet ();



	public CSS20FontDatabase () {
		this (new PassThroughFamilyNameNormalizer (), true);
	}

	public CSS20FontDatabase (boolean ignoreVariant) {
		this (new PassThroughFamilyNameNormalizer (), ignoreVariant);
	}

	public CSS20FontDatabase (FamilyNameNormalizer normalizer) {
		this (normalizer, true);
	}

	public CSS20FontDatabase(FamilyNameNormalizer normalizer, boolean ignoreVariant) {
		if (normalizer == null)
		{
			normalizer = new PassThroughFamilyNameNormalizer();
		}
		this.normalizer = normalizer;
		this.ignoreVariant = ignoreVariant;
	}


	/**
	 * Copy Constructor.
	 */
	public CSS20FontDatabase(CSS20FontDatabase original)
	{
		super(original);
		
		this.normalizer = original.normalizer;
		this.ignoreVariant = original.ignoreVariant;

		this.resolutionPriority = original.resolutionPriority;

		this.serif = (ArrayList) original.serif.clone();
		this.sansSerif = (ArrayList) original.sansSerif.clone();
		this.monospace = (ArrayList) original.monospace.clone();
		this.fantasy = (ArrayList) original.fantasy.clone();
		this.cursive = (ArrayList) original.cursive.clone();
		this.fallbackFontSet = original.fallbackFontSet;

		// Must deep copy the HashMap because the CSSFontsWithSameName's can be modified after this.
		Set keySet = original.fontsByFamilyName.keySet();
		Iterator iter = keySet.iterator();
		while (iter.hasNext())
		{
			Object key = iter.next();
			CSSFontsWithSameName fontsByName = new CSSFontsWithSameName((CSSFontsWithSameName) original.fontsByFamilyName.get(key));
			this.fontsByFamilyName.put(key, fontsByName);
		}
	}


	public FontResolutionPriority setResolutionPriority(FontResolutionPriority priority)
	{
		FontResolutionPriority oldPriority = this.resolutionPriority;
		this.resolutionPriority = priority;
		return oldPriority;
	}

	public void addFont(Font font) throws InvalidFontException, UnsupportedFontException, FontLoadingException
	{
		CSS20FontDescription[] fontDesc = font.getCSS20FontDescription();
		if (fontDesc == null)
		{
			throw new InvalidFontException("Font has no CSS20FontDescription", font);
		}

		for (int i = 0; i < fontDesc.length; i++)
		{
			FontProxy fontProxy = new FontProxy(fontDesc[i], font);
			addFontProxy(fontProxy);
		}
	}

	public void addFont(CSS20FontDescription fontDesc, Font font)
	throws InvalidFontException, UnsupportedFontException, FontLoadingException
	{
		FontProxy fontProxy = new FontProxy(fontDesc, font);
		addFontProxy(fontProxy);
	}

	private boolean addFontProxy(FontProxy fontProxy)
	throws InvalidFontException, UnsupportedFontException, FontLoadingException
	{
		String familyName = normalizer.normalize(((CSS20FontDescription) fontProxy.getFontDescription()).getFamilyName());
		CSSFontsWithSameName fontsWithSameName = (CSSFontsWithSameName) fontsByFamilyName.get(familyName);
		if (fontsWithSameName == null)
		{
			fontsWithSameName = new CSSFontsWithSameName();
			fontsByFamilyName.put(familyName, fontsWithSameName);
		}
		boolean added = fontsWithSameName.addFont(fontProxy);
		if (added)
		{
			super.fontAdded(fontProxy);
		}
		return added;
	}

	protected void removeFontProxy(FontProxy fontProxy)
	{
		String familyName = normalizer.normalize(((CSS20FontDescription) fontProxy.getFontDescription()).getFamilyName());
		CSSFontsWithSameName fontsWithSameName = (CSSFontsWithSameName) fontsByFamilyName.get(familyName);
		if (fontsWithSameName != null)
		{
			fontsWithSameName.removeFont(fontProxy);
		}
	}

	public boolean isEmpty()
	{
		return fontsByFamilyName.isEmpty();
	}

	public void setGenericFont(CSS20GenericFontFamily genericFamily, String[] replacementFontNames)
	{
		ArrayList genericFamilyList = null;

		if (genericFamily == CSS20GenericFontFamily.SERIF)
		{
			genericFamilyList = this.serif;
		} 
		else if (genericFamily == CSS20GenericFontFamily.SANS_SERIF)
		{
			genericFamilyList = this.sansSerif; 
		}
		else if (genericFamily == CSS20GenericFontFamily.CURSIVE)
		{
			genericFamilyList = this.cursive;
		}
		else if (genericFamily == CSS20GenericFontFamily.FANTASY)
		{
			genericFamilyList = this.fantasy;
		}
		else if (genericFamily == CSS20GenericFontFamily.MONOSPACE)
		{
			genericFamilyList = this.monospace;
		} else {
			return;
		}

		genericFamilyList.clear();
		genericFamilyList.ensureCapacity(replacementFontNames.length);
		for (int i = 0; i < replacementFontNames.length; i++)
		{
			genericFamilyList.add(i, replacementFontNames[i]);
		}
	}

	public void setFallbackFonts (FallbackFontSet fallbackFontSet) {
		this.fallbackFontSet = fallbackFontSet;
	}


	protected List getGenericFont(CSS20GenericFontFamily genericFamily)
	{
		if (genericFamily == CSS20GenericFontFamily.SERIF)
		{
			return this.serif;
		} 
		else if (genericFamily == CSS20GenericFontFamily.SANS_SERIF)
		{
			return this.sansSerif; 
		}
		else if (genericFamily == CSS20GenericFontFamily.CURSIVE)
		{
			return this.cursive;
		}
		else if (genericFamily == CSS20GenericFontFamily.FANTASY)
		{
			return this.fantasy;
		}
		else if (genericFamily == CSS20GenericFontFamily.MONOSPACE)
		{
			return this.monospace;
		}
		return null;
	}

	protected FallbackFontSet getFallbackFontSet () {
		return fallbackFontSet;
	}

	protected boolean replaceGenericFontFamily(List fontFamilyNames)
	{
		boolean foundGenerics = false;
		for (int i = 0; i < fontFamilyNames.size(); i++)
		{
			String fontName = (String) fontFamilyNames.get(i);

			if (fontName.equals("serif"))
			{
				List serif = getGenericFont(CSS20GenericFontFamily.SERIF);
				fontFamilyNames.remove(i);
				if (serif != null)
				{
					fontFamilyNames.addAll(i, serif);
					foundGenerics = true;
				}
			} else if (fontName.equals("sans-serif"))
			{
				List sansSerif = getGenericFont(CSS20GenericFontFamily.SANS_SERIF);
				fontFamilyNames.remove(i);
				if (sansSerif != null)
				{
					fontFamilyNames.addAll(i, sansSerif);
					foundGenerics = true;
				}
			} else if (fontName.equals("cursive"))
			{
				List cursive = getGenericFont(CSS20GenericFontFamily.CURSIVE);
				fontFamilyNames.remove(i);
				if (cursive != null)
				{
					fontFamilyNames.addAll(i, cursive);
					foundGenerics = true;
				}
			} else if (fontName.equals("fantasy"))
			{
				List fantasy = getGenericFont(CSS20GenericFontFamily.FANTASY);
				fontFamilyNames.remove(i);
				if (fantasy != null)
				{
					fontFamilyNames.addAll(i, fantasy);
					foundGenerics = true;              
				}
			} else if (fontName.equals("monospace"))
			{
				List monospace = getGenericFont(CSS20GenericFontFamily.MONOSPACE);
				fontFamilyNames.remove(i);
				if (monospace != null)
				{
					fontFamilyNames.addAll(i, monospace);
					foundGenerics = true;
				}
			}
		}
		return foundGenerics;
	}

	protected Font[] findFont(String familyName, CSS20Attribute attributes)
	{
		Font[] foundFonts = null;

		CSSFontsWithSameName fontsWithSameName = (CSSFontsWithSameName) fontsByFamilyName.get(normalizer.normalize(familyName));
		if (fontsWithSameName != null)
		{
			foundFonts = fontsWithSameName.findFont(attributes);
		} // if (fontsWithSameName != null)
		return foundFonts;
	}


	public Font findFont(CSS20Attribute attributes, ULocale locale) 
	throws FontException
	{
		// First - search using the family list
		// These are font names and need to be matched
		List fontFamilyList = attributes.getFamilyNamesList ();
		replaceGenericFontFamily (fontFamilyList);
		for (int i = 0; i < fontFamilyList.size(); i++) {
			String familyName = (String) fontFamilyList.get (i);
			Font[] proposedFonts = findFont(familyName, attributes);
			if (proposedFonts != null) {
				return proposedFonts[0]; }}

		// Second, if no font selected, use fallback fonts
		Iterator it = getFallbackFontSet ().getFallbackFonts(locale);
		if (it.hasNext ()) {
			return (Font) it.next (); }

		return null;
	}

	final class CSSFontsWithSameName implements Serializable
	{
		/* Serialization signature is explicitly set and should be 
		 * incremented on each release to prevent compatibility.
		 */
		static final long serialVersionUID = 1;

		// Store the fonts in a multi-dimensional array of ArrayLists
		// first dimension = Variant
		//	0 = NORMAL
		//	1 = SMALL-CAPS
		// second dimension = Style
		//	0 = NORMAL
		//	1 = OBLIQUE
		//	2 = ITALIC
		private ArrayList/**/[][] fonts = new ArrayList[2][3];

		private static final int kVariantNormal	= 0;
		private static final int kVariantSmallCaps	= 1;
		private static final int kVariantSize		= 2;

		private static final int kStyleNormal		= 0;
		private static final int kStyleOblique		= 1;
		private static final int kStyleItalic		= 2;
		private static final int kStyleSize		= 3;


		protected CSSFontsWithSameName()
		{
		}

		protected CSSFontsWithSameName(CSSFontsWithSameName original)
		{
			// Clone all the contained objects
			for (int i = 0; i < kVariantSize; i++)
			{
				for (int j = 0; j < kStyleSize; j++)
				{
					if (original.fonts[i][j] != null)
					{
						this.fonts[i][j] = (ArrayList) original.fonts[i][j].clone();
					}
				}
			}

		}

		protected boolean addFont(FontProxy fontProxy)
		throws InvalidFontException, UnsupportedFontException, FontLoadingException
		{
			CSS20FontDescription fontDesc = (CSS20FontDescription) fontProxy.getFontDescription();
			ArrayList fontList = this.pickStyleVariantList(fontDesc.getVariant(), 
					fontDesc.getStyle(), true /* create if null */);
			return insertFontInList(fontList, fontProxy);
		}

		protected boolean insertFontInList(ArrayList fontList, FontProxy fontProxy)
		throws InvalidFontException, UnsupportedFontException, FontLoadingException
		{
			boolean wasAdded = true;

			// if the list is empty just add it
			if (fontList.size() == 0)
			{
				fontList.add(fontProxy);
			}
			
			CSS20FontDescription fontDesc = (CSS20FontDescription) fontProxy.getFontDescription();

			int[] bounds = new int[2];
			bounds[0] = 0;
			bounds[1] = fontList.size();
			this.findWeightRange(fontDesc.getWeight(), fontList, bounds);

			int foundWeight = ((CSS20FontDescription) ((FontProxy) fontList.get(bounds[0])).getFontDescription()).getWeight();
			if (fontDesc.getWeight() < foundWeight)
			{
				// nothing at the desired weight and the new font weight is lower
				fontList.add(bounds[0], fontProxy);                                
			}
			else if (fontDesc.getWeight() > foundWeight)
			{
				// nothing at the desired weight and the new font weight is higher
				fontList.add(bounds[1], fontProxy);
			}
			else
			{
				// fonts found at the desired weight - now need to find stretch
				this.findStretchRange(fontDesc.getStretch(), fontList, bounds);

				int foundStretch = ((CSS20FontDescription) ((FontProxy) fontList.get(bounds[0])).getFontDescription()).getStretch().getValue();

				if (fontDesc.getStretch().getValue() < foundStretch)
				{
					// nothing at the desired stretch and the new font stretch is lower
					fontList.add(bounds[0], fontProxy);                   
				}
				else if (fontDesc.getStretch().getValue() > foundStretch)
				{
					// nothing at the desired stretch and the new font stretch is higher
					fontList.add(bounds[1], fontProxy);                  
				}
				else
				{
					// fonts found at the desired stretch - now need to find point size
					if (resolutionPriority == FontResolutionPriority.LAST)
					{
						// add to the front of the list
						fontList.add(bounds[0], fontProxy);
					} else if (resolutionPriority == FontResolutionPriority.FIRST)
					{
						// add to the end of the list.
						fontList.add(bounds[1], fontProxy);
					} else {
						Font fontInList = ((FontProxy) fontList.get(bounds[0])).getFont();
						Font preferred = 
							IntelligentResolver.choosePreferredFont(fontInList, fontProxy.getFont(),
									resolutionPriority == FontResolutionPriority.INTELLIGENT_FIRST);
						if (preferred == fontInList)
						{
							fontList.add(bounds[1], fontProxy);
						} else {
							fontList.add(bounds[0], fontProxy);
						}
					}
				}
			}
			return wasAdded;
		}

		/**
		 * This method implements the weight selection portion of the basic CSS font selection algorithm.
		 * It operates on a list that is sorted by weight and in which there may be multiple fonts with
		 * the same weight.  It returns the bounds of the fonts that meet the CSS weight selection without
		 * requiring that the CSS weights be pre-filled and in a single pass.  To do this it uses a somewhat
		 * complex set of conditionals.  These statements implement the two tables given below.
		 * 
		 * 

Variables/Contants Used in the Tables

*
    *
  • boundaryWeight - the weight above which unresolved weights should move "upwards" *
  • searchWeight - the weight value that is specified in the search *
  • currentWeight - the weight value of the current entry as the list is iterated over *
  • bandWeight - the weight of the potentially matching "band" of values *
  • lowerBandIndex - the lower index of the "band" *
  • upperBandIndex - the current upper index of the "band" *
* *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Main Decision Table Used on Each Iteration through List
* * * currentWeight == bandWeight * * currentWeight > bandWeight * * currentWeight < bandWeight *
* currentWeight == searchWeight * * upperBandIndex = currentIndex * * upperBandIndex = currentIndex *
lowerBandIndex = currentIndex *
bandWeight = currentWeight
*
* ERROR CONDITION *
shouldn't happen *
* currentWeight < searchWeight * * upperBandIndex = currentIndex * * upperBandIndex = currentIndex *
lowerBandIndex = currentIndex *
bandWeight = currentWeight
*
* ERROR CONDITION *
shouldn't happen *
* currentWeight > searchWeight * * upperBandIndex = currentIndex * * secondary table *
see below *
* ERROR CONDITION *
shouldn't happen *
* * *

* * * * * * * * * * * * * * * * * * * * * * * * *
Secondary Decision Table Used Only IF Condition Above Met
* * * bandWeight == 0 * * bandWeight != 0
&& bandWeight < searchWeight
*
* bandWeight != 0
&& bandWeight >= searchWeight
*
* searchWeight <= boundaryWeight * * upperBandIndex = currentIndex *
lowerBandIndex = currentIndex *
bandWeight = currentWeight
*
* break; * * break; *
* searchWeight > boundaryWeight * * upperBandIndex = currentIndex *
lowerBandIndex = currentIndex *
bandWeight = currentWeight
*
* upperBandIndex = currentIndex *
lowerBandIndex = currentIndex *
bandWeight = currentWeight
*
* break; *
* @param searchWeight the weight to search for * @param fontList the list of fonts to search through * @param bounds the bounds of the search range (low - inclusive, high - exclusive) * @return the index of the preferred font in the range */ protected int findWeightRange (int searchWeight, ArrayList fontList, int[] bounds) { int bandWeight = 0; int[] bandBounds = {bounds[0], bounds[0]}; // First find the weight range for (int i = bounds[0]; i < bounds[1]; i++) { FontProxy currentFontProxy = (FontProxy) fontList.get(i); CSS20FontDescription currentFontDescription = (CSS20FontDescription) currentFontProxy.getFontDescription(); int currentWeight = currentFontDescription.getWeight(); // See the table above to understand the code below // This set of nested if's implements the table(s) if (currentWeight == bandWeight) { // still in the same band - raise the upper bound bandBounds[1] = i; } else if (currentWeight > bandWeight) { if (currentWeight <= searchWeight) { // reset the band bounds bandBounds[0] = i; bandBounds[1] = i; bandWeight = currentWeight; } else { if (bandWeight == 0) { // reset the band bounds bandBounds[0] = i; bandBounds[1] = i; bandWeight = currentWeight; } else { if (searchWeight <= CSS20Attribute.CSSWeightValue.W500.getValue()) { // have our band - no need to keep searching break; } else { if (bandWeight < searchWeight) { // reset the band bounds bandBounds[0] = i; bandBounds[1] = i; bandWeight = currentWeight; } else { // have our band - no need to keep searching break; } } } } } // This should never occur // else if (currentWeight < bandWeight) // { // throw new FormattingException("Impossible condition in the matching of font weights"); // } } bounds[0] = bandBounds[0]; bounds[1] = bandBounds[1] + 1; return bounds[0]; } /** * This method implements the stretch search portion of the simple CSS font search algorithm. * It is quite similar to the weight search algorithm and a fuller description of the algorithm is * available there. * @param stretch the stretch to search for * @param fontList the list of fonts to search through * @param bounds the bounds of the search range (low - inclusive, high - exclusive) * @return the index of the preferred font in the range * * @see com.adobe.fontengine.inlineformatting.css20.CSS20FontDatabase.CSSFontsWithSameName#findWeightRange(int, ArrayList, int[]) */ protected int findStretchRange (CSSStretchValue stretch, ArrayList fontList, int[] bounds) { int bandStretch = 0; int[] bandBounds = {bounds[0], bounds[0]}; int searchStretch = stretch.getValue();; // First find the stretch range for (int i = bounds[0]; i < bounds[1]; i++) { FontProxy currentFontProxy = (FontProxy) fontList.get(i); CSS20FontDescription currentFontDescription = (CSS20FontDescription) currentFontProxy.getFontDescription(); int currentStretch = currentFontDescription.getStretch().getValue(); // See the table above to understand the code below // This set of nested if's implements the table(s) if (currentStretch == bandStretch) { // still in the same band - raise the upper bound bandBounds[1] = i; } else if (currentStretch > bandStretch) { if (currentStretch <= searchStretch) { // reset the band bounds bandBounds[0] = i; bandBounds[1] = i; bandStretch = currentStretch; } else { if (bandStretch == 0) { // reset the band bounds bandBounds[0] = i; bandBounds[1] = i; bandStretch = currentStretch; } else { if (searchStretch <= CSS20Attribute.CSSStretchValue.NORMAL.getValue()) { // have our band - no need to keep searching break; } else { if (bandStretch < searchStretch) { // reset the band bounds bandBounds[0] = i; bandBounds[1] = i; bandStretch = currentStretch; } else { // have our band - no need to keep searching break; } } } } } // This should never occur // else if (currentStretch < bandStretch) // { // throw new FormattingException("Impossible condition in the matching of font stretches"); // } } bounds[0] = bandBounds[0]; bounds[1] = bandBounds[1] + 1; return bounds[0]; } protected int findOpticalSize(double pointSize, ArrayList fontList, int[] bounds) { for (int i = bounds[0]; i < bounds[1]; i++) { FontProxy currentFontProxy = (FontProxy) fontList.get(i); CSS20FontDescription currentFontDescription = (CSS20FontDescription) currentFontProxy.getFontDescription(); double lowPointSize = currentFontDescription.getLowPointSize(); double highPointSize = currentFontDescription.getHighPointSize(); if (pointSize >= lowPointSize && pointSize < highPointSize) { return i; } } return bounds[0]; } protected Font[] findFont(CSS20Attribute attributes) { ArrayList fontList = pickStyleVariantList(attributes.getVariant(), attributes.getStyle(), false /*dont create*/); Font[] foundFonts = null; if (fontList != null) { int foundFontIndex = 0; int[] bounds = { 0, fontList.size() }; this.findWeightRange(attributes.getWeight(), fontList, bounds); this.findStretchRange(attributes.getStretch(), fontList,bounds); foundFontIndex = this.findOpticalSize(attributes.getOpticalSize(), fontList, bounds); foundFonts = new Font[bounds[1] - bounds[0]]; for (int i = 0; i < foundFonts.length; i++) { foundFonts[i] = ((FontProxy) fontList.get(foundFontIndex)).getFont(); foundFontIndex = (++foundFontIndex == bounds[1]) ? bounds[0] : foundFontIndex; } } return foundFonts; } protected void removeFont(FontProxy fontProxy) { CSS20FontDescription fontDesc = (CSS20FontDescription) fontProxy.getFontDescription(); ArrayList fontList = this.pickStyleVariantList(fontDesc.getVariant(), fontDesc.getStyle(), true /* create if null */); Iterator iter = fontList.iterator(); while(iter.hasNext()) { FontProxy currentFontProxy = (FontProxy) iter.next(); if (fontProxy == currentFontProxy) { iter.remove(); break; } } } private ArrayList pickStyleVariantList(CSSVariantValue variant, CSSStyleValue style, boolean createIfNull) { int variantIndex, styleIndex; if (variant == CSSVariantValue.NORMAL || ignoreVariant) { variantIndex = kVariantNormal; } else { // Must be Small-Caps variantIndex = kVariantSmallCaps; } if (style == CSSStyleValue.NORMAL) { styleIndex = kStyleNormal; } else if (style == CSSStyleValue.OBLIQUE) { styleIndex = kStyleOblique; } else { // Must be Italic styleIndex = kStyleItalic; } if (createIfNull && fonts[variantIndex][styleIndex] == null) { fonts[variantIndex][styleIndex] = new ArrayList(); } return fonts[variantIndex][styleIndex]; } public boolean equals(Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (! (obj instanceof CSSFontsWithSameName)) { return false; } CSSFontsWithSameName o = (CSSFontsWithSameName)obj; for (int i = 0; i < kVariantSize; i++) { for (int j = 0; j < kStyleSize; j++) { if (this.fonts[i][j] != null) { if (!this.fonts[i][j].equals(o.fonts[i][j])) { return false; } } else { if (o.fonts[i][j] != null) { return false; } } } } return true; } public int hashCode() { int hashCode = 0; for (int i = 0; i < kVariantSize; i++) { for (int j = 0; j < kStyleSize; j++) { if (fonts[i][j] != null) { hashCode ^= fonts[i][j].hashCode(); } } } return hashCode; } public String toString() { TreeMap tempMap = new TreeMap(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < fonts.length; i++) { for (int j = 0; j < fonts [i].length; j++) { if (fonts [i][j] != null) { for (Iterator it = fonts[i][j].iterator(); it.hasNext(); ) { String name = it.next().toString(); int count = 0; if (tempMap.containsKey(name)) { count = ((Integer)tempMap.get(name)).intValue(); } tempMap.put(name, new Integer(++count)); } } } } for (Iterator it = tempMap.keySet().iterator(); it.hasNext(); ) { String name = (String)it.next(); int count = ((Integer)tempMap.get(name)).intValue(); while (count-- > 0) { sb.append(name); sb.append("\n"); } } return sb.toString(); } } public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (! (obj instanceof CSS20FontDatabase)) { return false; } CSS20FontDatabase o = (CSS20FontDatabase)obj; return this.fontsByFamilyName.equals (o.fontsByFamilyName) && this.serif.equals(o.serif) && this.sansSerif.equals(o.sansSerif) && this.monospace.equals(o.monospace) && this.fantasy.equals(o.fantasy) && this.cursive.equals (o.cursive) && this.fallbackFontSet.equals (o.fallbackFontSet); } public int hashCode() { int hashCode = 0; int hashCode1 = this.fontsByFamilyName.hashCode(); int hashCode2 = this.serif.hashCode(); int hashCode3 = this.sansSerif.hashCode(); int hashCode4 = this.monospace.hashCode(); int hashCode5 = this.fantasy.hashCode(); int hashCode6 = this.cursive.hashCode(); int hashCode7 = this.fallbackFontSet.hashCode(); hashCode = hashCode1 ^ hashCode2 ^ hashCode3 ^ hashCode4 ^ hashCode5 ^ hashCode6 ^ hashCode7; return hashCode; } public String toString() { TreeSet tempSet = new TreeSet(); for (Iterator it = fontsByFamilyName.keySet().iterator(); it.hasNext(); ) { tempSet.add(it.next()); } StringBuffer sb = new StringBuffer(); sb.append("family names:\n"); for (Iterator it = tempSet.iterator(); it.hasNext(); ) { String k = (String)it.next(); sb.append(" "); sb.append(k); sb.append(" = "); sb.append(fontsByFamilyName.get(k).toString()); sb.append("\n"); } sb.append("generic names:\n"); toString(sb, "serif", serif); toString(sb, "sans", sansSerif); toString(sb, "mono", monospace); toString(sb, "fantasy", fantasy); toString(sb, "cursive", cursive); sb.append("fallback fonts:\n"); sb.append(fallbackFontSet.toString()); return sb.toString(); } protected void toString(StringBuffer sb, String key, ArrayList al) { TreeMap tempMap = new TreeMap(); for (Iterator it = al.iterator(); it.hasNext(); ) { String name = (String)it.next(); int count = 0; if (tempMap.containsKey(name)) { count = ((Integer)tempMap.get(name)).intValue(); } tempMap.put(name, new Integer(++count)); } sb.append(" "); sb.append(key); sb.append(" = {"); String prefix = "'"; for (Iterator it = tempMap.keySet().iterator(); it.hasNext(); ) { String name = (String)it.next(); int count = ((Integer)tempMap.get(name)).intValue(); while (count-- > 0) { sb.append(prefix); sb.append("'"); sb.append(name); sb.append("'"); prefix = ", "; } } sb.append("}\n"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy