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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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.InternalResourceResolver;
  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 InternalResourceResolver resourceResolver;
  54. private StringBuffer text = new StringBuffer();
  55. private List<Integer> cidWidths;
  56. //private int cidWidthIndex;
  57. private Map<Integer, Integer> currentKerning;
  58. private List<CMapSegment> 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, InternalResourceResolver resourceResolver) throws FOPException {
  66. this.resourceResolver = resourceResolver;
  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. @Override
  127. public void startDocument() {
  128. }
  129. /**
  130. * {@inheritDoc}
  131. */
  132. public void startElement(String uri, String localName, String qName, Attributes attributes)
  133. throws SAXException {
  134. if (localName.equals("font-metrics")) {
  135. if ("TYPE0".equals(attributes.getValue("type"))) {
  136. multiFont = new MultiByteFont(resourceResolver, EmbeddingMode.AUTO);
  137. returnFont = multiFont;
  138. isCID = true;
  139. TTFReader.checkMetricsVersion(attributes);
  140. } else if ("TRUETYPE".equals(attributes.getValue("type"))) {
  141. singleFont = new SingleByteFont(resourceResolver);
  142. singleFont.setFontType(FontType.TRUETYPE);
  143. returnFont = singleFont;
  144. isCID = false;
  145. TTFReader.checkMetricsVersion(attributes);
  146. } else {
  147. singleFont = new SingleByteFont(resourceResolver);
  148. singleFont.setFontType(FontType.TYPE1);
  149. returnFont = singleFont;
  150. isCID = false;
  151. }
  152. } else if ("embed".equals(localName)) {
  153. try {
  154. returnFont.setEmbedURI(InternalResourceResolver.cleanURI(attributes.getValue("file")));
  155. } catch (URISyntaxException e) {
  156. throw new SAXException("URI syntax error in metrics file: " + e.getMessage(), e);
  157. }
  158. returnFont.setEmbedResourceName(attributes.getValue("class"));
  159. } else if ("cid-widths".equals(localName)) {
  160. // This is unused
  161. // cidWidthIndex = getInt(attributes.getValue("start-index"));
  162. cidWidths = new ArrayList<Integer>();
  163. } else if ("kerning".equals(localName)) {
  164. currentKerning = new HashMap<Integer, Integer>();
  165. returnFont.putKerningEntry(getInt(attributes.getValue("kpx1")),
  166. currentKerning);
  167. } else if ("bfranges".equals(localName)) {
  168. bfranges = new ArrayList<CMapSegment>();
  169. } else if ("bf".equals(localName)) {
  170. CMapSegment entry = new CMapSegment(getInt(attributes.getValue("us")),
  171. getInt(attributes.getValue("ue")),
  172. getInt(attributes.getValue("gi")));
  173. bfranges.add(entry);
  174. } else if ("wx".equals(localName)) {
  175. cidWidths.add(getInt(attributes.getValue("w")));
  176. // } else if ("widths".equals(localName)) {
  177. // singleFont.width = new int[256];
  178. } else if ("char".equals(localName)) {
  179. try {
  180. singleFont.setWidth(getInt(attributes.getValue("idx")),
  181. getInt(attributes.getValue("wdt")));
  182. } catch (NumberFormatException ne) {
  183. throw new SAXException("Malformed width in metric file: " + ne.getMessage(), ne);
  184. }
  185. } else if ("pair".equals(localName)) {
  186. currentKerning.put(getInt(attributes.getValue("kpx2")),
  187. getInt(attributes.getValue("kern")));
  188. }
  189. }
  190. private int getInt(String str) throws SAXException {
  191. int ret = 0;
  192. try {
  193. ret = Integer.parseInt(str);
  194. } catch (Exception e) {
  195. throw new SAXException("Error while parsing integer value: " + str, e);
  196. }
  197. return ret;
  198. }
  199. /**
  200. * {@inheritDoc}
  201. */
  202. @Override
  203. public void endElement(String uri, String localName, String qName) throws SAXException {
  204. String content = text.toString().trim();
  205. if ("font-name".equals(localName)) {
  206. returnFont.setFontName(content);
  207. } else if ("full-name".equals(localName)) {
  208. returnFont.setFullName(content);
  209. } else if ("family-name".equals(localName)) {
  210. Set<String> s = new HashSet<String>();
  211. s.add(content);
  212. returnFont.setFamilyNames(s);
  213. } else if ("ttc-name".equals(localName) && isCID) {
  214. multiFont.setTTCName(content);
  215. } else if ("encoding".equals(localName)) {
  216. if (singleFont != null && singleFont.getFontType() == FontType.TYPE1) {
  217. singleFont.setEncoding(content);
  218. }
  219. } else if ("cap-height".equals(localName)) {
  220. returnFont.setCapHeight(getInt(content));
  221. } else if ("x-height".equals(localName)) {
  222. returnFont.setXHeight(getInt(content));
  223. } else if ("ascender".equals(localName)) {
  224. returnFont.setAscender(getInt(content));
  225. } else if ("descender".equals(localName)) {
  226. returnFont.setDescender(getInt(content));
  227. } else if ("left".equals(localName)) {
  228. int[] bbox = returnFont.getFontBBox();
  229. bbox[0] = getInt(content);
  230. returnFont.setFontBBox(bbox);
  231. } else if ("bottom".equals(localName)) {
  232. int[] bbox = returnFont.getFontBBox();
  233. bbox[1] = getInt(content);
  234. returnFont.setFontBBox(bbox);
  235. } else if ("right".equals(localName)) {
  236. int[] bbox = returnFont.getFontBBox();
  237. bbox[2] = getInt(content);
  238. returnFont.setFontBBox(bbox);
  239. } else if ("top".equals(localName)) {
  240. int[] bbox = returnFont.getFontBBox();
  241. bbox[3] = getInt(content);
  242. returnFont.setFontBBox(bbox);
  243. } else if ("first-char".equals(localName)) {
  244. returnFont.setFirstChar(getInt(content));
  245. } else if ("last-char".equals(localName)) {
  246. returnFont.setLastChar(getInt(content));
  247. } else if ("flags".equals(localName)) {
  248. returnFont.setFlags(getInt(content));
  249. } else if ("stemv".equals(localName)) {
  250. returnFont.setStemV(getInt(content));
  251. } else if ("italic-angle".equals(localName)) {
  252. returnFont.setItalicAngle(getInt(content));
  253. } else if ("missing-width".equals(localName)) {
  254. returnFont.setMissingWidth(getInt(content));
  255. } else if ("cid-type".equals(localName)) {
  256. multiFont.setCIDType(CIDFontType.byName(content));
  257. } else if ("default-width".equals(localName)) {
  258. multiFont.setDefaultWidth(getInt(content));
  259. } else if ("cid-widths".equals(localName)) {
  260. int[] wds = new int[cidWidths.size()];
  261. int j = 0;
  262. for (int count = 0; count < cidWidths.size(); count++) {
  263. wds[j++] = cidWidths.get(count).intValue();
  264. }
  265. //multiFont.addCIDWidthEntry(cidWidthIndex, wds);
  266. multiFont.setWidthArray(wds);
  267. } else if ("bfranges".equals(localName)) {
  268. multiFont.setCMap(bfranges.toArray(new CMapSegment[0]));
  269. }
  270. text.setLength(0); //Reset text buffer (see characters())
  271. }
  272. /**
  273. * {@inheritDoc}
  274. */
  275. @Override
  276. public void characters(char[] ch, int start, int length) {
  277. text.append(ch, start, length);
  278. }
  279. }