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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.fonts;
  18. //Java
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.io.IOException;
  22. import javax.xml.parsers.SAXParserFactory;
  23. //SAX
  24. import org.xml.sax.XMLReader;
  25. import org.xml.sax.SAXException;
  26. import org.xml.sax.Locator;
  27. import org.xml.sax.Attributes;
  28. import org.xml.sax.helpers.DefaultHandler;
  29. //FOP
  30. import org.apache.fop.apps.FOPException;
  31. /**
  32. * Class for reading a metric.xml file and creating a font object.
  33. * Typical usage:
  34. * <pre>
  35. * FontReader reader = new FontReader(<path til metrics.xml>);
  36. * reader.setFontEmbedPath(<path to a .ttf or .pfb file or null to diable embedding>);
  37. * reader.useKerning(true);
  38. * Font f = reader.getFont();
  39. * </pre>
  40. */
  41. public class FontReader extends DefaultHandler {
  42. private Locator locator = null;
  43. private boolean isCID = false;
  44. private CustomFont returnFont = null;
  45. private MultiByteFont multiFont = null;
  46. private SingleByteFont singleFont = null;
  47. private StringBuffer text = new StringBuffer();
  48. private List cidWidths = null;
  49. private int cidWidthIndex = 0;
  50. private Map currentKerning = null;
  51. private List bfranges = null;
  52. private void createFont(String path) throws FOPException {
  53. XMLReader parser = null;
  54. try {
  55. final SAXParserFactory factory = javax.xml.parsers.SAXParserFactory.newInstance();
  56. factory.setNamespaceAware(true);
  57. parser = factory.newSAXParser().getXMLReader();
  58. } catch (Exception e) {
  59. throw new FOPException(e);
  60. }
  61. if (parser == null) {
  62. throw new FOPException("Unable to create SAX parser");
  63. }
  64. try {
  65. parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
  66. false);
  67. } catch (SAXException e) {
  68. throw new FOPException("You need a SAX parser which supports SAX version 2",
  69. e);
  70. }
  71. parser.setContentHandler(this);
  72. try {
  73. parser.parse(path);
  74. } catch (SAXException e) {
  75. throw new FOPException(e);
  76. } catch (IOException e) {
  77. throw new FOPException(e);
  78. }
  79. }
  80. /**
  81. * Sets the path to embed a font. A null value disables font embedding.
  82. * @param path URI for the embeddable file
  83. */
  84. public void setFontEmbedPath(String path) {
  85. returnFont.setEmbedFileName(path);
  86. }
  87. /**
  88. * Enable/disable use of kerning for the font
  89. * @param enabled true to enable kerning, false to disable
  90. */
  91. public void setKerningEnabled(boolean enabled) {
  92. returnFont.setKerningEnabled(enabled);
  93. }
  94. /**
  95. * Get the generated font object
  96. * @return the font
  97. */
  98. public Typeface getFont() {
  99. return returnFont;
  100. }
  101. /**
  102. * Construct a FontReader object from a path to a metric.xml file
  103. * and read metric data
  104. * @param path URI to the font metric file
  105. * @throws FOPException if loading the font fails
  106. */
  107. public FontReader(String path) throws FOPException {
  108. createFont(path);
  109. }
  110. /**
  111. * @see org.xml.sax.ContentHandler#startDocument()
  112. */
  113. public void startDocument() {
  114. }
  115. /**
  116. * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
  117. */
  118. public void setDocumentLocator(Locator locator) {
  119. this.locator = locator;
  120. }
  121. /**
  122. * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
  123. */
  124. public void startElement(String uri, String localName, String qName,
  125. Attributes attributes) {
  126. if (localName.equals("font-metrics")) {
  127. if ("TYPE0".equals(attributes.getValue("type"))) {
  128. multiFont = new MultiByteFont();
  129. returnFont = multiFont;
  130. isCID = true;
  131. } else if ("TRUETYPE".equals(attributes.getValue("type"))) {
  132. singleFont = new SingleByteFont();
  133. singleFont.setFontType(FontType.TRUETYPE);
  134. returnFont = singleFont;
  135. isCID = false;
  136. } else {
  137. singleFont = new SingleByteFont();
  138. singleFont.setFontType(FontType.TYPE1);
  139. returnFont = singleFont;
  140. isCID = false;
  141. }
  142. } else if ("embed".equals(localName)) {
  143. returnFont.setEmbedFileName(attributes.getValue("file"));
  144. returnFont.setEmbedResourceName(attributes.getValue("class"));
  145. } else if ("cid-widths".equals(localName)) {
  146. cidWidthIndex = getInt(attributes.getValue("start-index"));
  147. cidWidths = new java.util.ArrayList();
  148. } else if ("kerning".equals(localName)) {
  149. currentKerning = new java.util.HashMap();
  150. returnFont.putKerningEntry(new Integer(attributes.getValue("kpx1")),
  151. currentKerning);
  152. } else if ("bfranges".equals(localName)) {
  153. bfranges = new java.util.ArrayList();
  154. } else if ("bf".equals(localName)) {
  155. BFEntry entry = new BFEntry(getInt(attributes.getValue("us")),
  156. getInt(attributes.getValue("ue")),
  157. getInt(attributes.getValue("gi")));
  158. bfranges.add(entry);
  159. } else if ("wx".equals(localName)) {
  160. cidWidths.add(new Integer(attributes.getValue("w")));
  161. } else if ("widths".equals(localName)) {
  162. //singleFont.width = new int[256];
  163. } else if ("char".equals(localName)) {
  164. try {
  165. singleFont.setWidth(Integer.parseInt(attributes.getValue("idx")),
  166. Integer.parseInt(attributes.getValue("wdt")));
  167. } catch (NumberFormatException ne) {
  168. System.out.println("Malformed width in metric file: "
  169. + ne.getMessage());
  170. }
  171. } else if ("pair".equals(localName)) {
  172. currentKerning.put(new Integer(attributes.getValue("kpx2")),
  173. new Integer(attributes.getValue("kern")));
  174. }
  175. }
  176. private int getInt(String str) {
  177. int ret = 0;
  178. try {
  179. ret = Integer.parseInt(str);
  180. } catch (Exception e) {
  181. /**@todo log this exception */
  182. }
  183. return ret;
  184. }
  185. /**
  186. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  187. */
  188. public void endElement(String uri, String localName, String qName) {
  189. if ("font-name".equals(localName)) {
  190. returnFont.setFontName(text.toString());
  191. } else if ("ttc-name".equals(localName) && isCID) {
  192. multiFont.setTTCName(text.toString());
  193. } else if ("cap-height".equals(localName)) {
  194. returnFont.setCapHeight(getInt(text.toString()));
  195. } else if ("x-height".equals(localName)) {
  196. returnFont.setXHeight(getInt(text.toString()));
  197. } else if ("ascender".equals(localName)) {
  198. returnFont.setAscender(getInt(text.toString()));
  199. } else if ("descender".equals(localName)) {
  200. returnFont.setDescender(getInt(text.toString()));
  201. } else if ("left".equals(localName)) {
  202. int[] bbox = returnFont.getFontBBox();
  203. bbox[0] = getInt(text.toString());
  204. returnFont.setFontBBox(bbox);
  205. } else if ("bottom".equals(localName)) {
  206. int[] bbox = returnFont.getFontBBox();
  207. bbox[1] = getInt(text.toString());
  208. returnFont.setFontBBox(bbox);
  209. } else if ("right".equals(localName)) {
  210. int[] bbox = returnFont.getFontBBox();
  211. bbox[2] = getInt(text.toString());
  212. returnFont.setFontBBox(bbox);
  213. } else if ("top".equals(localName)) {
  214. int[] bbox = returnFont.getFontBBox();
  215. bbox[3] = getInt(text.toString());
  216. returnFont.setFontBBox(bbox);
  217. } else if ("first-char".equals(localName)) {
  218. returnFont.setFirstChar(getInt(text.toString()));
  219. } else if ("last-char".equals(localName)) {
  220. returnFont.setLastChar(getInt(text.toString()));
  221. } else if ("flags".equals(localName)) {
  222. returnFont.setFlags(getInt(text.toString()));
  223. } else if ("stemv".equals(localName)) {
  224. returnFont.setStemV(getInt(text.toString()));
  225. } else if ("italic-angle".equals(localName)) {
  226. returnFont.setItalicAngle(getInt(text.toString()));
  227. } else if ("missing-width".equals(localName)) {
  228. returnFont.setMissingWidth(getInt(text.toString()));
  229. } else if ("cid-type".equals(localName)) {
  230. multiFont.setCIDType(CIDFontType.byName(text.toString()));
  231. } else if ("default-width".equals(localName)) {
  232. multiFont.setDefaultWidth(getInt(text.toString()));
  233. } else if ("cid-widths".equals(localName)) {
  234. int[] wds = new int[cidWidths.size()];
  235. int j = 0;
  236. for (int count = 0; count < cidWidths.size(); count++) {
  237. Integer i = (Integer)cidWidths.get(count);
  238. wds[j++] = i.intValue();
  239. }
  240. //multiFont.addCIDWidthEntry(cidWidthIndex, wds);
  241. multiFont.setWidthArray(wds);
  242. } else if ("bfranges".equals(localName)) {
  243. multiFont.setBFEntries((BFEntry[])bfranges.toArray(new BFEntry[0]));
  244. }
  245. text.setLength(0); //Reset text buffer (see characters())
  246. }
  247. /**
  248. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  249. */
  250. public void characters(char[] ch, int start, int length) {
  251. text.append(ch, start, length);
  252. }
  253. }