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.

FontReader.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. //Java
  20. import java.io.IOException;
  21. import java.net.URI;
  22. import java.net.URISyntaxException;
  23. import java.util.ArrayList;
  24. import java.util.HashMap;
  25. import java.util.HashSet;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Set;
  29. import javax.xml.parsers.SAXParserFactory;
  30. import org.xml.sax.Attributes;
  31. import org.xml.sax.InputSource;
  32. import org.xml.sax.SAXException;
  33. import org.xml.sax.XMLReader;
  34. import org.xml.sax.helpers.DefaultHandler;
  35. import org.apache.fop.apps.FOPException;
  36. import org.apache.fop.apps.io.URIResolverWrapper;
  37. import org.apache.fop.fonts.apps.TTFReader;
  38. /**
  39. * Class for reading a metric.xml file and creating a font object.
  40. * Typical usage:
  41. * <pre>
  42. * FontReader reader = new FontReader(<path til metrics.xml>);
  43. * reader.setFontEmbedPath(<path to a .ttf or .pfb file or null to diable embedding>);
  44. * reader.useKerning(true);
  45. * Font f = reader.getFont();
  46. * </pre>
  47. */
  48. public class FontReader extends DefaultHandler {
  49. private boolean isCID;
  50. private CustomFont returnFont;
  51. private MultiByteFont multiFont;
  52. private SingleByteFont singleFont;
  53. private final URIResolverWrapper resolver;
  54. private StringBuffer text = new StringBuffer();
  55. private List<Integer> cidWidths;
  56. //private int cidWidthIndex;
  57. private Map<Integer, Integer> currentKerning;
  58. private List<BFEntry> bfranges;
  59. /**
  60. * Construct a FontReader object from a path to a metric.xml file
  61. * and read metric data
  62. * @param source Source of the font metric file
  63. * @throws FOPException if loading the font fails
  64. */
  65. public FontReader(InputSource source, URIResolverWrapper resolver) throws FOPException {
  66. this.resolver = resolver;
  67. createFont(source);
  68. }
  69. private void createFont(InputSource source) throws FOPException {
  70. XMLReader parser = null;
  71. try {
  72. final SAXParserFactory factory = javax.xml.parsers.SAXParserFactory.newInstance();
  73. factory.setNamespaceAware(true);
  74. parser = factory.newSAXParser().getXMLReader();
  75. } catch (Exception e) {
  76. throw new FOPException(e);
  77. }
  78. if (parser == null) {
  79. throw new FOPException("Unable to create SAX parser");
  80. }
  81. try {
  82. parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
  83. } catch (SAXException e) {
  84. throw new FOPException("You need a SAX parser which supports SAX version 2", e);
  85. }
  86. parser.setContentHandler(this);
  87. try {
  88. parser.parse(source);
  89. } catch (SAXException e) {
  90. throw new FOPException(e);
  91. } catch (IOException e) {
  92. throw new FOPException(e);
  93. }
  94. }
  95. /**
  96. * Sets the path to embed a font. A null value disables font embedding.
  97. * @param path URI for the embeddable file
  98. */
  99. public void setFontEmbedURI(URI path) {
  100. returnFont.setEmbedURI(path);
  101. }
  102. /**
  103. * Enable/disable use of kerning for the font
  104. * @param enabled true to enable kerning, false to disable
  105. */
  106. public void setKerningEnabled(boolean enabled) {
  107. returnFont.setKerningEnabled(enabled);
  108. }
  109. /**
  110. * Enable/disable use of advanced typographic features for the font
  111. * @param enabled true to enable, false to disable
  112. */
  113. public void setAdvancedEnabled(boolean enabled) {
  114. returnFont.setAdvancedEnabled(enabled);
  115. }
  116. /**
  117. * Get the generated font object
  118. * @return the font
  119. */
  120. public Typeface getFont() {
  121. return returnFont;
  122. }
  123. /**
  124. * {@inheritDoc}
  125. */
  126. public void startDocument() {
  127. }
  128. /**
  129. * {@inheritDoc}
  130. */
  131. public void startElement(String uri, String localName, String qName, Attributes attributes)
  132. throws SAXException {
  133. if (localName.equals("font-metrics")) {
  134. if ("TYPE0".equals(attributes.getValue("type"))) {
  135. multiFont = new MultiByteFont(resolver);
  136. returnFont = multiFont;
  137. isCID = true;
  138. TTFReader.checkMetricsVersion(attributes);
  139. } else if ("TRUETYPE".equals(attributes.getValue("type"))) {
  140. singleFont = new SingleByteFont(resolver);
  141. singleFont.setFontType(FontType.TRUETYPE);
  142. returnFont = singleFont;
  143. isCID = false;
  144. TTFReader.checkMetricsVersion(attributes);
  145. } else {
  146. singleFont = new SingleByteFont(resolver);
  147. singleFont.setFontType(FontType.TYPE1);
  148. returnFont = singleFont;
  149. isCID = false;
  150. }
  151. } else if ("embed".equals(localName)) {
  152. try {
  153. returnFont.setEmbedURI(URIResolverWrapper.cleanURI(attributes.getValue("file")));
  154. } catch (URISyntaxException e) {
  155. // TODO: dunno what to do here?!?!
  156. }
  157. returnFont.setEmbedResourceName(attributes.getValue("class"));
  158. } else if ("cid-widths".equals(localName)) {
  159. // This is unused
  160. // cidWidthIndex = getInt(attributes.getValue("start-index"));
  161. cidWidths = new ArrayList<Integer>();
  162. } else if ("kerning".equals(localName)) {
  163. currentKerning = new HashMap<Integer, Integer>();
  164. returnFont.putKerningEntry(getInt(attributes.getValue("kpx1")),
  165. currentKerning);
  166. } else if ("bfranges".equals(localName)) {
  167. bfranges = new ArrayList<BFEntry>();
  168. } else if ("bf".equals(localName)) {
  169. BFEntry entry = new BFEntry(getInt(attributes.getValue("us")),
  170. getInt(attributes.getValue("ue")),
  171. getInt(attributes.getValue("gi")));
  172. bfranges.add(entry);
  173. } else if ("wx".equals(localName)) {
  174. cidWidths.add(getInt(attributes.getValue("w")));
  175. // } else if ("widths".equals(localName)) {
  176. // singleFont.width = new int[256];
  177. } else if ("char".equals(localName)) {
  178. try {
  179. singleFont.setWidth(getInt(attributes.getValue("idx")),
  180. getInt(attributes.getValue("wdt")));
  181. } catch (NumberFormatException ne) {
  182. throw new SAXException("Malformed width in metric file: " + ne.getMessage(), ne);
  183. }
  184. } else if ("pair".equals(localName)) {
  185. currentKerning.put(getInt(attributes.getValue("kpx2")),
  186. getInt(attributes.getValue("kern")));
  187. }
  188. }
  189. private int getInt(String str) throws SAXException {
  190. int ret = 0;
  191. try {
  192. ret = Integer.parseInt(str);
  193. } catch (Exception e) {
  194. throw new SAXException("Error while parsing integer value: " + str, e);
  195. }
  196. return ret;
  197. }
  198. /**
  199. * {@inheritDoc}
  200. */
  201. public void endElement(String uri, String localName, String qName) throws SAXException {
  202. String content = text.toString().trim();
  203. if ("font-name".equals(localName)) {
  204. returnFont.setFontName(content);
  205. } else if ("full-name".equals(localName)) {
  206. returnFont.setFullName(content);
  207. } else if ("family-name".equals(localName)) {
  208. Set<String> s = new HashSet<String>();
  209. s.add(content);
  210. returnFont.setFamilyNames(s);
  211. } else if ("ttc-name".equals(localName) && isCID) {
  212. multiFont.setTTCName(content);
  213. } else if ("encoding".equals(localName)) {
  214. if (singleFont != null && singleFont.getFontType() == FontType.TYPE1) {
  215. singleFont.setEncoding(content);
  216. }
  217. } else if ("cap-height".equals(localName)) {
  218. returnFont.setCapHeight(getInt(content));
  219. } else if ("x-height".equals(localName)) {
  220. returnFont.setXHeight(getInt(content));
  221. } else if ("ascender".equals(localName)) {
  222. returnFont.setAscender(getInt(content));
  223. } else if ("descender".equals(localName)) {
  224. returnFont.setDescender(getInt(content));
  225. } else if ("left".equals(localName)) {
  226. int[] bbox = returnFont.getFontBBox();
  227. bbox[0] = getInt(content);
  228. returnFont.setFontBBox(bbox);
  229. } else if ("bottom".equals(localName)) {
  230. int[] bbox = returnFont.getFontBBox();
  231. bbox[1] = getInt(content);
  232. returnFont.setFontBBox(bbox);
  233. } else if ("right".equals(localName)) {
  234. int[] bbox = returnFont.getFontBBox();
  235. bbox[2] = getInt(content);
  236. returnFont.setFontBBox(bbox);
  237. } else if ("top".equals(localName)) {
  238. int[] bbox = returnFont.getFontBBox();
  239. bbox[3] = getInt(content);
  240. returnFont.setFontBBox(bbox);
  241. } else if ("first-char".equals(localName)) {
  242. returnFont.setFirstChar(getInt(content));
  243. } else if ("last-char".equals(localName)) {
  244. returnFont.setLastChar(getInt(content));
  245. } else if ("flags".equals(localName)) {
  246. returnFont.setFlags(getInt(content));
  247. } else if ("stemv".equals(localName)) {
  248. returnFont.setStemV(getInt(content));
  249. } else if ("italic-angle".equals(localName)) {
  250. returnFont.setItalicAngle(getInt(content));
  251. } else if ("missing-width".equals(localName)) {
  252. returnFont.setMissingWidth(getInt(content));
  253. } else if ("cid-type".equals(localName)) {
  254. multiFont.setCIDType(CIDFontType.byName(content));
  255. } else if ("default-width".equals(localName)) {
  256. multiFont.setDefaultWidth(getInt(content));
  257. } else if ("cid-widths".equals(localName)) {
  258. int[] wds = new int[cidWidths.size()];
  259. int j = 0;
  260. for (int count = 0; count < cidWidths.size(); count++) {
  261. wds[j++] = cidWidths.get(count).intValue();
  262. }
  263. //multiFont.addCIDWidthEntry(cidWidthIndex, wds);
  264. multiFont.setWidthArray(wds);
  265. } else if ("bfranges".equals(localName)) {
  266. multiFont.setBFEntries(bfranges.toArray(new BFEntry[0]));
  267. }
  268. text.setLength(0); //Reset text buffer (see characters())
  269. }
  270. /**
  271. * {@inheritDoc}
  272. */
  273. public void characters(char[] ch, int start, int length) {
  274. text.append(ch, start, length);
  275. }
  276. }