You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CommonHyphenation.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.fo.properties;
  19. import java.util.Locale;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.apache.fop.fo.Constants;
  23. import org.apache.fop.fo.PropertyList;
  24. import org.apache.fop.fo.expr.PropertyException;
  25. import org.apache.fop.fonts.FontMetrics;
  26. import org.apache.fop.fonts.Typeface;
  27. /**
  28. * Store all common hyphenation properties.
  29. * See Sec. 7.9 of the XSL-FO Standard.
  30. * Public "structure" allows direct member access.
  31. */
  32. public final class CommonHyphenation {
  33. /** Logger */
  34. private static final Log LOG = LogFactory.getLog(CommonHyphenation.class);
  35. private static final PropertyCache<CommonHyphenation> CACHE =
  36. new PropertyCache<CommonHyphenation>();
  37. private int hash;
  38. /** The "language" property */
  39. public final StringProperty language;
  40. /** The "country" property */
  41. public final StringProperty country;
  42. /** The "script" property */
  43. public final StringProperty script;
  44. /** The "hyphenate" property */
  45. public final EnumProperty hyphenate;
  46. /** The "hyphenation-character" property */
  47. public final OptionalCharacterProperty hyphenationCharacter;
  48. /** The "hyphenation-push-character-count" property */
  49. public final NumberProperty hyphenationPushCharacterCount;
  50. /** The "hyphenation-remain-character-count" property*/
  51. public final NumberProperty hyphenationRemainCharacterCount;
  52. /**
  53. * Construct a CommonHyphenation object holding the given properties
  54. *
  55. */
  56. private CommonHyphenation(StringProperty language,
  57. StringProperty country,
  58. StringProperty script,
  59. EnumProperty hyphenate,
  60. OptionalCharacterProperty hyphenationCharacter,
  61. NumberProperty hyphenationPushCharacterCount,
  62. NumberProperty hyphenationRemainCharacterCount) {
  63. this.language = language;
  64. this.country = country;
  65. this.script = script;
  66. this.hyphenate = hyphenate;
  67. this.hyphenationCharacter = hyphenationCharacter;
  68. this.hyphenationPushCharacterCount = hyphenationPushCharacterCount;
  69. this.hyphenationRemainCharacterCount = hyphenationRemainCharacterCount;
  70. }
  71. /**
  72. * Gets the canonical <code>CommonHyphenation</code> instance corresponding
  73. * to the values of the related properties present on the given
  74. * <code>PropertyList</code>
  75. * @param propertyList the <code>PropertyList</code>
  76. * @return a common hyphenation instance
  77. * @throws PropertyException if a a property exception occurs
  78. */
  79. public static CommonHyphenation getInstance(PropertyList propertyList)
  80. throws PropertyException {
  81. StringProperty language
  82. = (StringProperty) propertyList.get(Constants.PR_LANGUAGE);
  83. StringProperty country
  84. = (StringProperty) propertyList.get(Constants.PR_COUNTRY);
  85. StringProperty script
  86. = (StringProperty) propertyList.get(Constants.PR_SCRIPT);
  87. EnumProperty hyphenate
  88. = (EnumProperty) propertyList.get(Constants.PR_HYPHENATE);
  89. OptionalCharacterProperty hyphenationCharacter
  90. = (OptionalCharacterProperty) propertyList.get(Constants.PR_HYPHENATION_CHARACTER);
  91. NumberProperty hyphenationPushCharacterCount
  92. = (NumberProperty) propertyList.get(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT);
  93. NumberProperty hyphenationRemainCharacterCount
  94. = (NumberProperty) propertyList.get(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT);
  95. CommonHyphenation instance = new CommonHyphenation(
  96. language,
  97. country,
  98. script,
  99. hyphenate,
  100. hyphenationCharacter,
  101. hyphenationPushCharacterCount,
  102. hyphenationRemainCharacterCount);
  103. return CACHE.fetch(instance);
  104. }
  105. private static final char HYPHEN_MINUS = '-';
  106. private static final char MINUS_SIGN = '\u2212';
  107. /**
  108. * Returns the effective hyphenation character for a font. The hyphenation character specified
  109. * in XSL-FO may be substituted if it's not available in the font.
  110. * @param font the font
  111. * @return the effective hyphenation character.
  112. */
  113. public Character getHyphChar(org.apache.fop.fonts.Font font) {
  114. if (hyphenationCharacter.getObject() == null) {
  115. return null;
  116. }
  117. char hyphChar = hyphenationCharacter.getCharacter();
  118. if (font.hasChar(hyphChar)) {
  119. return hyphChar; //short-cut
  120. }
  121. char effHyphChar = hyphChar;
  122. boolean warn = false;
  123. if (font.hasChar(HYPHEN_MINUS)) {
  124. effHyphChar = HYPHEN_MINUS;
  125. warn = true;
  126. } else if (font.hasChar(MINUS_SIGN)) {
  127. effHyphChar = MINUS_SIGN;
  128. FontMetrics metrics = font.getFontMetrics();
  129. if (metrics instanceof Typeface) {
  130. Typeface typeface = (Typeface)metrics;
  131. if ("SymbolEncoding".equals(typeface.getEncodingName())) {
  132. //SymbolEncoding doesn't have HYPHEN_MINUS, so replace by MINUS_SIGN
  133. } else {
  134. //only warn if the encoding is not SymbolEncoding
  135. warn = true;
  136. }
  137. }
  138. } else {
  139. effHyphChar = ' ';
  140. FontMetrics metrics = font.getFontMetrics();
  141. if (metrics instanceof Typeface) {
  142. Typeface typeface = (Typeface)metrics;
  143. if ("ZapfDingbatsEncoding".equals(typeface.getEncodingName())) {
  144. //ZapfDingbatsEncoding doesn't have HYPHEN_MINUS, so replace by ' '
  145. } else {
  146. //only warn if the encoding is not ZapfDingbatsEncoding
  147. warn = true;
  148. }
  149. }
  150. }
  151. if (warn) {
  152. LOG.warn("Substituted specified hyphenation character (0x"
  153. + Integer.toHexString(hyphChar)
  154. + ") with 0x" + Integer.toHexString(effHyphChar)
  155. + " because the font doesn't have the specified hyphenation character: "
  156. + font.getFontTriplet());
  157. }
  158. return effHyphChar;
  159. }
  160. /**
  161. * Returns the IPD for the hyphenation character for a font.
  162. * @param font the font
  163. * @return the IPD in millipoints for the hyphenation character.
  164. */
  165. public int getHyphIPD(org.apache.fop.fonts.Font font) {
  166. Character hyphChar = getHyphChar(font);
  167. return (hyphChar == null) ? 0 : font.getCharWidth(hyphChar);
  168. }
  169. /**
  170. * Creates and returns a {@link Locale} representation of the language and country.
  171. *
  172. * @return the language (and the country if set) represented as a locale, {@code null}
  173. * if the language has not been set (i.e., has been left to its initial value of
  174. * "none")
  175. */
  176. public Locale getLocale() {
  177. return toLocale(language.getString(), country.getString());
  178. }
  179. /**
  180. * Creates and returns a {@link Locale} representation of the given language, and the
  181. * given country if set. The country is considered to be set if not {@code null} and
  182. * not set to "none".
  183. *
  184. * @return the language and country represented as a locale, {@code null} if the
  185. * language is null or "none" (case insensitive)
  186. */
  187. public static Locale toLocale(String language, String country) {
  188. Locale locale = null;
  189. if (isDefined(language)) {
  190. if (isDefined(country)) {
  191. locale = new Locale(language, country);
  192. } else {
  193. locale = new Locale(language);
  194. }
  195. }
  196. return locale;
  197. }
  198. private static boolean isDefined(String property) {
  199. return !(property == null || property.equalsIgnoreCase("none"));
  200. }
  201. /** {@inheritDoc} */
  202. public boolean equals(Object obj) {
  203. if (obj == this) {
  204. return true;
  205. }
  206. if (obj instanceof CommonHyphenation) {
  207. CommonHyphenation ch = (CommonHyphenation) obj;
  208. return (ch.language == this.language
  209. && ch.country == this.country
  210. && ch.script == this.script
  211. && ch.hyphenate == this.hyphenate
  212. && ch.hyphenationCharacter == this.hyphenationCharacter
  213. && ch.hyphenationPushCharacterCount == this.hyphenationPushCharacterCount
  214. && ch.hyphenationRemainCharacterCount == this.hyphenationRemainCharacterCount);
  215. }
  216. return false;
  217. }
  218. /** {@inheritDoc} */
  219. public int hashCode() {
  220. if (this.hash == 0) {
  221. int hash = 17;
  222. hash = 37 * hash + (language == null ? 0 : language.hashCode());
  223. hash = 37 * hash + (script == null ? 0 : script.hashCode());
  224. hash = 37 * hash + (country == null ? 0 : country.hashCode());
  225. hash = 37 * hash + (hyphenate == null ? 0 : hyphenate.hashCode());
  226. hash = 37 * hash
  227. + (hyphenationCharacter == null
  228. ? 0 : hyphenationCharacter.hashCode());
  229. hash = 37 * hash
  230. + (hyphenationPushCharacterCount == null
  231. ? 0 : hyphenationPushCharacterCount.hashCode());
  232. hash = 37 * hash
  233. + (hyphenationRemainCharacterCount == null
  234. ? 0 : hyphenationRemainCharacterCount.hashCode());
  235. this.hash = hash;
  236. }
  237. return this.hash;
  238. }
  239. }