blob: 256c6dd7ab2f6ce059df7aba8ba36e6fc51a3590 [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.
*/
/* $Id$ */
package org.apache.fop.fo.properties;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.fonts.Typeface;
/**
* Store all common hyphenation properties.
* See Sec. 7.9 of the XSL-FO Standard.
* Public "structure" allows direct member access.
*/
public final class CommonHyphenation {
/** Logger */
private static final Log LOG = LogFactory.getLog(CommonHyphenation.class);
private static final PropertyCache<CommonHyphenation> CACHE =
new PropertyCache<CommonHyphenation>();
private int hash;
/** The "language" property */
public final StringProperty language;
/** The "country" property */
public final StringProperty country;
/** The "script" property */
public final StringProperty script;
/** The "hyphenate" property */
public final EnumProperty hyphenate;
/** The "hyphenation-character" property */
public final OptionalCharacterProperty hyphenationCharacter;
/** The "hyphenation-push-character-count" property */
public final NumberProperty hyphenationPushCharacterCount;
/** The "hyphenation-remain-character-count" property*/
public final NumberProperty hyphenationRemainCharacterCount;
/**
* Construct a CommonHyphenation object holding the given properties
*
*/
private CommonHyphenation(StringProperty language,
StringProperty country,
StringProperty script,
EnumProperty hyphenate,
OptionalCharacterProperty hyphenationCharacter,
NumberProperty hyphenationPushCharacterCount,
NumberProperty hyphenationRemainCharacterCount) {
this.language = language;
this.country = country;
this.script = script;
this.hyphenate = hyphenate;
this.hyphenationCharacter = hyphenationCharacter;
this.hyphenationPushCharacterCount = hyphenationPushCharacterCount;
this.hyphenationRemainCharacterCount = hyphenationRemainCharacterCount;
}
/**
* Gets the canonical <code>CommonHyphenation</code> instance corresponding
* to the values of the related properties present on the given
* <code>PropertyList</code>
* @param propertyList the <code>PropertyList</code>
* @return a common hyphenation instance
* @throws PropertyException if a a property exception occurs
*/
public static CommonHyphenation getInstance(PropertyList propertyList)
throws PropertyException {
StringProperty language
= (StringProperty) propertyList.get(Constants.PR_LANGUAGE);
StringProperty country
= (StringProperty) propertyList.get(Constants.PR_COUNTRY);
StringProperty script
= (StringProperty) propertyList.get(Constants.PR_SCRIPT);
EnumProperty hyphenate
= (EnumProperty) propertyList.get(Constants.PR_HYPHENATE);
OptionalCharacterProperty hyphenationCharacter
= (OptionalCharacterProperty) propertyList.get(Constants.PR_HYPHENATION_CHARACTER);
NumberProperty hyphenationPushCharacterCount
= (NumberProperty) propertyList.get(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT);
NumberProperty hyphenationRemainCharacterCount
= (NumberProperty) propertyList.get(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT);
CommonHyphenation instance = new CommonHyphenation(
language,
country,
script,
hyphenate,
hyphenationCharacter,
hyphenationPushCharacterCount,
hyphenationRemainCharacterCount);
return CACHE.fetch(instance);
}
private static final char HYPHEN_MINUS = '-';
private static final char MINUS_SIGN = '\u2212';
/**
* Returns the effective hyphenation character for a font. The hyphenation character specified
* in XSL-FO may be substituted if it's not available in the font.
* @param font the font
* @return the effective hyphenation character.
*/
public Character getHyphChar(org.apache.fop.fonts.Font font) {
if (hyphenationCharacter.getObject() == null) {
return null;
}
char hyphChar = hyphenationCharacter.getCharacter();
if (font.hasChar(hyphChar)) {
return hyphChar; //short-cut
}
char effHyphChar = hyphChar;
boolean warn = false;
if (font.hasChar(HYPHEN_MINUS)) {
effHyphChar = HYPHEN_MINUS;
warn = true;
} else if (font.hasChar(MINUS_SIGN)) {
effHyphChar = MINUS_SIGN;
FontMetrics metrics = font.getFontMetrics();
if (metrics instanceof Typeface) {
Typeface typeface = (Typeface)metrics;
if ("SymbolEncoding".equals(typeface.getEncodingName())) {
//SymbolEncoding doesn't have HYPHEN_MINUS, so replace by MINUS_SIGN
} else {
//only warn if the encoding is not SymbolEncoding
warn = true;
}
}
} else {
effHyphChar = ' ';
FontMetrics metrics = font.getFontMetrics();
if (metrics instanceof Typeface) {
Typeface typeface = (Typeface)metrics;
if ("ZapfDingbatsEncoding".equals(typeface.getEncodingName())) {
//ZapfDingbatsEncoding doesn't have HYPHEN_MINUS, so replace by ' '
} else {
//only warn if the encoding is not ZapfDingbatsEncoding
warn = true;
}
}
}
if (warn) {
LOG.warn("Substituted specified hyphenation character (0x"
+ Integer.toHexString(hyphChar)
+ ") with 0x" + Integer.toHexString(effHyphChar)
+ " because the font doesn't have the specified hyphenation character: "
+ font.getFontTriplet());
}
return effHyphChar;
}
/**
* Returns the IPD for the hyphenation character for a font.
* @param font the font
* @return the IPD in millipoints for the hyphenation character.
*/
public int getHyphIPD(org.apache.fop.fonts.Font font) {
Character hyphChar = getHyphChar(font);
return (hyphChar == null) ? 0 : font.getCharWidth(hyphChar);
}
/**
* Creates and returns a {@link Locale} representation of the language and country.
*
* @return the language (and the country if set) represented as a locale, {@code null}
* if the language has not been set (i.e., has been left to its initial value of
* "none")
*/
public Locale getLocale() {
return toLocale(language.getString(), country.getString());
}
/**
* Creates and returns a {@link Locale} representation of the given language, and the
* given country if set. The country is considered to be set if not {@code null} and
* not set to "none".
*
* @return the language and country represented as a locale, {@code null} if the
* language is null or "none" (case insensitive)
*/
public static Locale toLocale(String language, String country) {
Locale locale = null;
if (isDefined(language)) {
if (isDefined(country)) {
locale = new Locale(language, country);
} else {
locale = new Locale(language);
}
}
return locale;
}
private static boolean isDefined(String property) {
return !(property == null || property.equalsIgnoreCase("none"));
}
/** {@inheritDoc} */
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof CommonHyphenation) {
CommonHyphenation ch = (CommonHyphenation) obj;
return (ch.language == this.language
&& ch.country == this.country
&& ch.script == this.script
&& ch.hyphenate == this.hyphenate
&& ch.hyphenationCharacter == this.hyphenationCharacter
&& ch.hyphenationPushCharacterCount == this.hyphenationPushCharacterCount
&& ch.hyphenationRemainCharacterCount == this.hyphenationRemainCharacterCount);
}
return false;
}
/** {@inheritDoc} */
public int hashCode() {
if (this.hash == 0) {
int hash = 17;
hash = 37 * hash + (language == null ? 0 : language.hashCode());
hash = 37 * hash + (script == null ? 0 : script.hashCode());
hash = 37 * hash + (country == null ? 0 : country.hashCode());
hash = 37 * hash + (hyphenate == null ? 0 : hyphenate.hashCode());
hash = 37 * hash
+ (hyphenationCharacter == null
? 0 : hyphenationCharacter.hashCode());
hash = 37 * hash
+ (hyphenationPushCharacterCount == null
? 0 : hyphenationPushCharacterCount.hashCode());
hash = 37 * hash
+ (hyphenationRemainCharacterCount == null
? 0 : hyphenationRemainCharacterCount.hashCode());
this.hash = hash;
}
return this.hash;
}
}