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.

PFMReader.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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.apps;
  18. import java.io.File;
  19. import java.io.InputStream;
  20. import java.io.IOException;
  21. import java.util.Map;
  22. import java.util.List;
  23. import java.util.Iterator;
  24. import javax.xml.parsers.DocumentBuilderFactory;
  25. import javax.xml.transform.Transformer;
  26. import javax.xml.transform.TransformerException;
  27. import javax.xml.transform.TransformerFactory;
  28. import org.w3c.dom.Document;
  29. import org.w3c.dom.Element;
  30. import org.apache.commons.logging.Log;
  31. import org.apache.commons.logging.LogFactory;
  32. //FOP
  33. import org.apache.fop.apps.Fop;
  34. import org.apache.fop.fonts.type1.PFMFile;
  35. /**
  36. * A tool which reads PFM files from Adobe Type 1 fonts and creates
  37. * XML font metrics file for use in FOP.
  38. */
  39. public class PFMReader {
  40. /**
  41. * logging instance
  42. */
  43. protected Log log = LogFactory.getLog(TTFReader.class);
  44. /**
  45. * Parse commandline arguments. put options in the HashMap and return
  46. * arguments in the String array
  47. * the arguments: -fn Perpetua,Bold -cn PerpetuaBold per.ttf Perpetua.xml
  48. * returns a String[] with the per.ttf and Perpetua.xml. The hash
  49. * will have the (key, value) pairs: (-fn, Perpetua) and (-cn, PerpetuaBold)
  50. */
  51. private static String[] parseArguments(Map options, String[] args) {
  52. List arguments = new java.util.ArrayList();
  53. for (int i = 0; i < args.length; i++) {
  54. if (args[i].startsWith("-")) {
  55. if ((i + 1) < args.length && !args[i + 1].startsWith("-")) {
  56. options.put(args[i], args[i + 1]);
  57. i++;
  58. } else {
  59. options.put(args[i], "");
  60. }
  61. } else {
  62. arguments.add(args[i]);
  63. }
  64. }
  65. return (String[])arguments.toArray(new String[arguments.size()]);
  66. }
  67. private static void displayUsage() {
  68. System.out.println(
  69. "java " + PFMReader.class.getName() + " [options] metricfile.pfm xmlfile.xml");
  70. System.out.println();
  71. System.out.println("where options can be:");
  72. System.out.println("-d <WARN|INFO|DEBUG>");
  73. System.out.println(" Set debug level (default: WARN).");
  74. System.out.println("-fn <fontname>");
  75. System.out.println(" default is to use the fontname in the .pfm file, but");
  76. System.out.println(" you can override that name to make sure that the");
  77. System.out.println(" embedded font is used (if you're embedding fonts)");
  78. System.out.println(" instead of installed fonts when viewing documents ");
  79. System.out.println(" with Acrobat Reader.");
  80. }
  81. /**
  82. * The main method for the PFM reader tool.
  83. *
  84. * @param args Command-line arguments: [options] metricfile.pfm xmlfile.xml
  85. * where options can be:
  86. * -fn <fontname>
  87. * default is to use the fontname in the .pfm file, but you can override
  88. * that name to make sure that the embedded font is used instead of installed
  89. * fonts when viewing documents with Acrobat Reader.
  90. * -cn <classname>
  91. * default is to use the fontname
  92. * -ef <path to the Type1 .pfb fontfile>
  93. * will add the possibility to embed the font. When running fop, fop will look
  94. * for this file to embed it
  95. * -er <path to Type1 fontfile relative to org/apache/fop/render/pdf/fonts>
  96. * you can also include the fontfile in the fop.jar file when building fop.
  97. * You can use both -ef and -er. The file specified in -ef will be searched first,
  98. * then the -er file.
  99. */
  100. public static void main(String[] args) {
  101. String embFile = null;
  102. String embResource = null;
  103. String className = null;
  104. String fontName = null;
  105. Map options = new java.util.HashMap();
  106. String[] arguments = parseArguments(options, args);
  107. //Setup simple logger for this command-line application
  108. System.setProperty("org.apache.commons.logging.Log",
  109. "org.apache.commons.logging.impl.SimpleLog");
  110. System.setProperty("org.apache.commons.logging.simplelog.showShortLogname",
  111. "false");
  112. //Determine log level
  113. String level;
  114. if (options.get("-d") != null) {
  115. String lev = (String)options.get("-d");
  116. if ("DEBUG".equalsIgnoreCase(lev)) {
  117. level = "debug";
  118. } else if ("INFO".equalsIgnoreCase(lev)) {
  119. level = "info";
  120. } else {
  121. level = "warn";
  122. }
  123. } else {
  124. level = "warn";
  125. }
  126. System.setProperty("org.apache.commons.logging.simplelog.defaultlog",
  127. level);
  128. PFMReader app = new PFMReader();
  129. System.out.println("PFM Reader for Apache FOP " + Fop.getVersion() + "\n");
  130. if (options.get("-ef") != null) {
  131. embFile = (String)options.get("-ef");
  132. }
  133. if (options.get("-er") != null) {
  134. embResource = (String)options.get("-er");
  135. }
  136. if (options.get("-fn") != null) {
  137. fontName = (String)options.get("-fn");
  138. }
  139. if (options.get("-cn") != null) {
  140. className = (String)options.get("-cn");
  141. }
  142. if (arguments.length != 2 || options.get("-h") != null
  143. || options.get("-help") != null || options.get("--help") != null) {
  144. displayUsage();
  145. } else {
  146. try {
  147. System.out.println("Parsing font...");
  148. PFMFile pfm = app.loadPFM(arguments[0]);
  149. if (pfm != null) {
  150. app.preview(pfm);
  151. Document doc = app.constructFontXML(pfm,
  152. fontName, className, embResource, embFile);
  153. app.writeFontXML(doc, arguments[1]);
  154. }
  155. System.out.println("\nXML font metrics file successfullly created.");
  156. } catch (Exception e) {
  157. System.err.println("Error while building XML font metrics file");
  158. e.printStackTrace(System.err);
  159. System.exit(-1);
  160. }
  161. }
  162. }
  163. /**
  164. * Read a PFM file and returns it as an object.
  165. *
  166. * @param filename The filename of the PFM file.
  167. * @return The PFM as an object.
  168. * @throws IOException In case of an I/O problem
  169. */
  170. public PFMFile loadPFM(String filename) throws IOException {
  171. log.info("Reading " + filename + "...");
  172. log.info("");
  173. InputStream in = new java.io.FileInputStream(filename);
  174. try {
  175. PFMFile pfm = new PFMFile();
  176. pfm.load(in);
  177. return pfm;
  178. } finally {
  179. in.close();
  180. }
  181. }
  182. /**
  183. * Displays a preview of the PFM file on the console.
  184. *
  185. * @param pfm The PFM file to preview.
  186. */
  187. public void preview(PFMFile pfm) {
  188. if (log != null & log.isInfoEnabled()) {
  189. log.info("Font: " + pfm.getWindowsName());
  190. log.info("Name: " + pfm.getPostscriptName());
  191. log.info("CharSet: " + pfm.getCharSetName());
  192. log.info("CapHeight: " + pfm.getCapHeight());
  193. log.info("XHeight: " + pfm.getXHeight());
  194. log.info("LowerCaseAscent: " + pfm.getLowerCaseAscent());
  195. log.info("LowerCaseDescent: " + pfm.getLowerCaseDescent());
  196. log.info("Having widths for " + (pfm.getLastChar() - pfm.getFirstChar())
  197. + " characters (" + pfm.getFirstChar()
  198. + "-" + pfm.getLastChar() + ").");
  199. log.info("for example: Char " + pfm.getFirstChar()
  200. + " has a width of " + pfm.getCharWidth(pfm.getFirstChar()));
  201. log.info("");
  202. }
  203. }
  204. /**
  205. * Writes the generated DOM Document to a file.
  206. *
  207. * @param doc The DOM Document to save.
  208. * @param target The target filename for the XML file.
  209. * @throws TransformerException if an error occurs during serialization
  210. */
  211. public void writeFontXML(org.w3c.dom.Document doc, String target)
  212. throws TransformerException {
  213. log.info("Writing xml font file " + target + "...");
  214. log.info("");
  215. TransformerFactory factory = TransformerFactory.newInstance();
  216. Transformer transformer = factory.newTransformer();
  217. transformer.transform(
  218. new javax.xml.transform.dom.DOMSource(doc),
  219. new javax.xml.transform.stream.StreamResult(new File(target)));
  220. }
  221. /**
  222. * Generates the font metrics file from the PFM file.
  223. *
  224. * @param pfm The PFM file to generate the font metrics from.
  225. * @param fontName name of the font
  226. * @param className class name for the font
  227. * @param resource path to the font as embedded resource
  228. * @param file path to the font as file
  229. * @return The DOM document representing the font metrics file.
  230. */
  231. public org.w3c.dom.Document constructFontXML(PFMFile pfm,
  232. String fontName, String className, String resource, String file) {
  233. log.info("Creating xml font file...");
  234. log.info("");
  235. Document doc;
  236. try {
  237. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  238. doc = factory.newDocumentBuilder().newDocument();
  239. } catch (javax.xml.parsers.ParserConfigurationException e) {
  240. log.error("Can't create DOM implementation", e);
  241. return null;
  242. }
  243. Element root = doc.createElement("font-metrics");
  244. doc.appendChild(root);
  245. root.setAttribute("type", "TYPE1");
  246. Element el = doc.createElement("font-name");
  247. root.appendChild(el);
  248. el.appendChild(doc.createTextNode(pfm.getPostscriptName()));
  249. String s = pfm.getPostscriptName();
  250. int pos = s.indexOf("-");
  251. if (pos >= 0) {
  252. char[] sb = new char[s.length() - 1];
  253. s.getChars(0, pos, sb, 0);
  254. s.getChars(pos + 1, s.length(), sb, pos);
  255. s = new String(sb);
  256. }
  257. el = doc.createElement("embed");
  258. root.appendChild(el);
  259. if (file != null) {
  260. el.setAttribute("file", file);
  261. }
  262. if (resource != null) {
  263. el.setAttribute("class", resource);
  264. }
  265. el = doc.createElement("encoding");
  266. root.appendChild(el);
  267. el.appendChild(doc.createTextNode(pfm.getCharSetName() + "Encoding"));
  268. el = doc.createElement("cap-height");
  269. root.appendChild(el);
  270. Integer value = new Integer(pfm.getCapHeight());
  271. el.appendChild(doc.createTextNode(value.toString()));
  272. el = doc.createElement("x-height");
  273. root.appendChild(el);
  274. value = new Integer(pfm.getXHeight());
  275. el.appendChild(doc.createTextNode(value.toString()));
  276. el = doc.createElement("ascender");
  277. root.appendChild(el);
  278. value = new Integer(pfm.getLowerCaseAscent());
  279. el.appendChild(doc.createTextNode(value.toString()));
  280. el = doc.createElement("descender");
  281. root.appendChild(el);
  282. value = new Integer(-pfm.getLowerCaseDescent());
  283. el.appendChild(doc.createTextNode(value.toString()));
  284. Element bbox = doc.createElement("bbox");
  285. root.appendChild(bbox);
  286. int[] bb = pfm.getFontBBox();
  287. final String[] names = {"left", "bottom", "right", "top"};
  288. for (int i = 0; i < names.length; i++) {
  289. el = doc.createElement(names[i]);
  290. bbox.appendChild(el);
  291. value = new Integer(bb[i]);
  292. el.appendChild(doc.createTextNode(value.toString()));
  293. }
  294. el = doc.createElement("flags");
  295. root.appendChild(el);
  296. value = new Integer(pfm.getFlags());
  297. el.appendChild(doc.createTextNode(value.toString()));
  298. el = doc.createElement("stemv");
  299. root.appendChild(el);
  300. value = new Integer(pfm.getStemV());
  301. el.appendChild(doc.createTextNode(value.toString()));
  302. el = doc.createElement("italicangle");
  303. root.appendChild(el);
  304. value = new Integer(pfm.getItalicAngle());
  305. el.appendChild(doc.createTextNode(value.toString()));
  306. el = doc.createElement("first-char");
  307. root.appendChild(el);
  308. value = new Integer(pfm.getFirstChar());
  309. el.appendChild(doc.createTextNode(value.toString()));
  310. el = doc.createElement("last-char");
  311. root.appendChild(el);
  312. value = new Integer(pfm.getLastChar());
  313. el.appendChild(doc.createTextNode(value.toString()));
  314. Element widths = doc.createElement("widths");
  315. root.appendChild(widths);
  316. for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
  317. el = doc.createElement("char");
  318. widths.appendChild(el);
  319. el.setAttribute("idx", Integer.toString(i));
  320. el.setAttribute("wdt",
  321. new Integer(pfm.getCharWidth(i)).toString());
  322. }
  323. // Get kerning
  324. Iterator enum = pfm.getKerning().keySet().iterator();
  325. while (enum.hasNext()) {
  326. Integer kpx1 = (Integer)enum.next();
  327. el = doc.createElement("kerning");
  328. el.setAttribute("kpx1", kpx1.toString());
  329. root.appendChild(el);
  330. Element el2 = null;
  331. Map h2 = (Map)pfm.getKerning().get(kpx1);
  332. Iterator enum2 = h2.keySet().iterator();
  333. while (enum2.hasNext()) {
  334. Integer kpx2 = (Integer)enum2.next();
  335. el2 = doc.createElement("pair");
  336. el2.setAttribute("kpx2", kpx2.toString());
  337. Integer val = (Integer)h2.get(kpx2);
  338. el2.setAttribute("kern", val.toString());
  339. el.appendChild(el2);
  340. }
  341. }
  342. return doc;
  343. }
  344. private String escapeString(String str) {
  345. StringBuffer esc = new StringBuffer();
  346. for (int i = 0; i < str.length(); i++) {
  347. if (str.charAt(i) == '\\') {
  348. esc.append("\\\\");
  349. } else {
  350. esc.append(str.charAt(i));
  351. }
  352. }
  353. return esc.toString();
  354. }
  355. }