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

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