Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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.fonts;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Set;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. /**
  26. * Generic SingleByte font
  27. */
  28. public class SingleByteFont extends CustomFont {
  29. /** logger */
  30. private static Log log = LogFactory.getLog(SingleByteFont.class);
  31. private SingleByteEncoding mapping;
  32. private boolean useNativeEncoding = false;
  33. private int[] width = null;
  34. private Map unencodedCharacters;
  35. //Map<Character, UnencodedCharacter>
  36. private List additionalEncodings;
  37. /**
  38. * Main constructor.
  39. */
  40. public SingleByteFont() {
  41. setEncoding(CodePointMapping.WIN_ANSI_ENCODING);
  42. }
  43. /** {@inheritDoc} */
  44. public boolean isEmbeddable() {
  45. return (!(getEmbedFileName() == null
  46. && getEmbedResourceName() == null));
  47. }
  48. /** {@inheritDoc} */
  49. public String getEncodingName() {
  50. return this.mapping.getName();
  51. }
  52. /**
  53. * Returns the code point mapping (encoding) of this font.
  54. * @return the code point mapping
  55. */
  56. public SingleByteEncoding getEncoding() {
  57. return this.mapping;
  58. }
  59. /** {@inheritDoc} */
  60. public int getWidth(int i, int size) {
  61. if (i < 256) {
  62. int idx = i - getFirstChar();
  63. if (idx >= 0 && idx < width.length) {
  64. return size * width[i - getFirstChar()];
  65. }
  66. } else if (this.additionalEncodings != null) {
  67. int encodingIndex = (i / 256) - 1;
  68. SimpleSingleByteEncoding encoding = getAdditionalEncoding(encodingIndex);
  69. int codePoint = i % 256;
  70. NamedCharacter nc = encoding.getCharacterForIndex(codePoint);
  71. UnencodedCharacter uc
  72. = (UnencodedCharacter)this.unencodedCharacters.get(
  73. new Character(nc.getSingleUnicodeValue()));
  74. return size * uc.getWidth();
  75. }
  76. return 0;
  77. }
  78. /** {@inheritDoc} */
  79. public int[] getWidths() {
  80. int[] arr = new int[width.length];
  81. System.arraycopy(width, 0, arr, 0, width.length);
  82. return arr;
  83. }
  84. /** {@inheritDoc} */
  85. public char mapChar(char c) {
  86. notifyMapOperation();
  87. char d = mapping.mapChar(c);
  88. if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
  89. return d;
  90. }
  91. //Check unencoded characters which are available in the font by character name
  92. d = mapUnencodedChar(c);
  93. if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
  94. return d;
  95. }
  96. this.warnMissingGlyph(c);
  97. return Typeface.NOT_FOUND;
  98. }
  99. private char mapUnencodedChar(char ch) {
  100. if (this.unencodedCharacters != null) {
  101. UnencodedCharacter unencoded
  102. = (UnencodedCharacter)this.unencodedCharacters.get(new Character(ch));
  103. if (unencoded != null) {
  104. if (this.additionalEncodings == null) {
  105. this.additionalEncodings = new java.util.ArrayList();
  106. }
  107. SimpleSingleByteEncoding encoding = null;
  108. char mappedStart = 0;
  109. int additionalsCount = this.additionalEncodings.size();
  110. for (int i = 0; i < additionalsCount; i++) {
  111. mappedStart += 256;
  112. encoding = getAdditionalEncoding(i);
  113. char alt = encoding.mapChar(ch);
  114. if (alt != 0) {
  115. return (char)(mappedStart + alt);
  116. }
  117. }
  118. if (encoding != null && encoding.isFull()) {
  119. encoding = null;
  120. }
  121. if (encoding == null) {
  122. encoding = new SimpleSingleByteEncoding(
  123. getFontName() + "EncodingSupp" + (additionalsCount + 1));
  124. this.additionalEncodings.add(encoding);
  125. mappedStart += 256;
  126. }
  127. return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter()));
  128. }
  129. }
  130. return 0;
  131. }
  132. /** {@inheritDoc} */
  133. public boolean hasChar(char c) {
  134. char d = mapping.mapChar(c);
  135. if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
  136. return true;
  137. }
  138. //Check unencoded characters which are available in the font by character name
  139. d = mapUnencodedChar(c);
  140. if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
  141. return true;
  142. }
  143. return false;
  144. }
  145. /* ---- single byte font specific setters --- */
  146. /**
  147. * Updates the mapping variable based on the encoding.
  148. * @param encoding the name of the encoding
  149. */
  150. protected void updateMapping(String encoding) {
  151. try {
  152. this.mapping = CodePointMapping.getMapping(encoding);
  153. } catch (UnsupportedOperationException e) {
  154. log.error("Font '" + super.getFontName() + "': " + e.getMessage());
  155. }
  156. }
  157. /**
  158. * Sets the encoding of the font.
  159. * @param encoding the encoding (ex. "WinAnsiEncoding" or "SymbolEncoding")
  160. */
  161. public void setEncoding(String encoding) {
  162. updateMapping(encoding);
  163. }
  164. /**
  165. * Sets the encoding of the font.
  166. * @param encoding the encoding information
  167. */
  168. public void setEncoding(CodePointMapping encoding) {
  169. this.mapping = encoding;
  170. }
  171. /**
  172. * Controls whether the font is configured to use its native encoding or if it
  173. * may need to be re-encoded for the target format.
  174. * @param value true indicates that the configured encoding is the font's native encoding
  175. */
  176. public void setUseNativeEncoding(boolean value) {
  177. this.useNativeEncoding = value;
  178. }
  179. /**
  180. * Indicates whether this font is configured to use its native encoding. This
  181. * method is used to determine whether the font needs to be re-encoded.
  182. * @return true if the font uses its native encoding.
  183. */
  184. public boolean isUsingNativeEncoding() {
  185. return this.useNativeEncoding;
  186. }
  187. /**
  188. * Sets a width for a character.
  189. * @param index index of the character
  190. * @param w the width of the character
  191. */
  192. public void setWidth(int index, int w) {
  193. if (this.width == null) {
  194. this.width = new int[getLastChar() - getFirstChar() + 1];
  195. }
  196. this.width[index - getFirstChar()] = w;
  197. }
  198. /**
  199. * Adds an unencoded character (one that is not supported by the primary encoding).
  200. * @param ch the named character
  201. * @param width the width of the character
  202. */
  203. public void addUnencodedCharacter(NamedCharacter ch, int width) {
  204. if (this.unencodedCharacters == null) {
  205. this.unencodedCharacters = new java.util.HashMap();
  206. }
  207. if (ch.hasSingleUnicodeValue()) {
  208. UnencodedCharacter uc = new UnencodedCharacter(ch, width);
  209. this.unencodedCharacters.put(new Character(ch.getSingleUnicodeValue()), uc);
  210. } else {
  211. //Cannot deal with unicode sequences, so ignore this character
  212. }
  213. }
  214. /**
  215. * Makes all unencoded characters available through additional encodings. This method
  216. * is used in cases where the fonts need to be encoded in the target format before
  217. * all text of the document is processed (for example in PostScript when resource optimization
  218. * is disabled).
  219. */
  220. public void encodeAllUnencodedCharacters() {
  221. if (this.unencodedCharacters != null) {
  222. Set sortedKeys = new java.util.TreeSet(this.unencodedCharacters.keySet());
  223. Iterator iter = sortedKeys.iterator();
  224. while (iter.hasNext()) {
  225. Character ch = (Character)iter.next();
  226. char mapped = mapChar(ch.charValue());
  227. assert mapped != Typeface.NOT_FOUND;
  228. }
  229. }
  230. }
  231. /**
  232. * Indicates whether the encoding has additional encodings besides the primary encoding.
  233. * @return true if there are additional encodings.
  234. */
  235. public boolean hasAdditionalEncodings() {
  236. return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0);
  237. }
  238. /**
  239. * Returns the number of additional encodings this single-byte font maintains.
  240. * @return the number of additional encodings
  241. */
  242. public int getAdditionalEncodingCount() {
  243. if (hasAdditionalEncodings()) {
  244. return this.additionalEncodings.size();
  245. } else {
  246. return 0;
  247. }
  248. }
  249. /**
  250. * Returns an additional encoding.
  251. * @param index the index of the additional encoding
  252. * @return the additional encoding
  253. * @throws IndexOutOfBoundsException if the index is out of bounds
  254. */
  255. public SimpleSingleByteEncoding getAdditionalEncoding(int index)
  256. throws IndexOutOfBoundsException {
  257. if (hasAdditionalEncodings()) {
  258. return (SimpleSingleByteEncoding)this.additionalEncodings.get(index);
  259. } else {
  260. throw new IndexOutOfBoundsException("No additional encodings available");
  261. }
  262. }
  263. /**
  264. * Returns an array with the widths for an additional encoding.
  265. * @param index the index of the additional encoding
  266. * @return the width array
  267. */
  268. public int[] getAdditionalWidths(int index) {
  269. SimpleSingleByteEncoding enc = getAdditionalEncoding(index);
  270. int[] arr = new int[enc.getLastChar() - enc.getFirstChar() + 1];
  271. for (int i = 0, c = arr.length; i < c; i++) {
  272. NamedCharacter nc = enc.getCharacterForIndex(enc.getFirstChar() + i);
  273. UnencodedCharacter uc = (UnencodedCharacter)this.unencodedCharacters.get(
  274. new Character(nc.getSingleUnicodeValue()));
  275. arr[i] = uc.getWidth();
  276. }
  277. return arr;
  278. }
  279. private static final class UnencodedCharacter {
  280. private NamedCharacter character;
  281. private int width;
  282. public UnencodedCharacter(NamedCharacter character, int width) {
  283. this.character = character;
  284. this.width = width;
  285. }
  286. public NamedCharacter getCharacter() {
  287. return this.character;
  288. }
  289. public int getWidth() {
  290. return this.width;
  291. }
  292. /** {@inheritDoc} */
  293. public String toString() {
  294. return getCharacter().toString();
  295. }
  296. }
  297. }