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.

Type1FontLoader.java 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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.type1;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import java.util.Set;
  24. import org.apache.commons.io.IOUtils;
  25. import org.apache.fop.fonts.CodePointMapping;
  26. import org.apache.fop.fonts.FontLoader;
  27. import org.apache.fop.fonts.FontResolver;
  28. import org.apache.fop.fonts.FontType;
  29. import org.apache.fop.fonts.SingleByteFont;
  30. /**
  31. * Loads a Type 1 font into memory directly from the original font file.
  32. */
  33. public class Type1FontLoader extends FontLoader {
  34. private SingleByteFont singleFont;
  35. /**
  36. * Constructs a new Type 1 font loader.
  37. * @param fontFileURI the URI to the PFB file of a Type 1 font
  38. * @param resolver the font resolver used to resolve URIs
  39. * @throws IOException In case of an I/O error
  40. */
  41. public Type1FontLoader(String fontFileURI, FontResolver resolver)
  42. throws IOException {
  43. super(fontFileURI, resolver);
  44. }
  45. private String getPFMURI(String pfbURI) {
  46. String pfbExt = pfbURI.substring(pfbURI.length() - 3, pfbURI.length());
  47. String pfmExt = pfbExt.substring(0, 2)
  48. + (Character.isUpperCase(pfbExt.charAt(2)) ? "M" : "m");
  49. return pfbURI.substring(0, pfbURI.length() - 4) + "." + pfmExt;
  50. }
  51. private static final String[] AFM_EXTENSIONS = new String[] {".AFM", ".afm", ".Afm"};
  52. /** {@inheritDoc} */
  53. protected void read() throws IOException {
  54. AFMFile afm = null;
  55. PFMFile pfm = null;
  56. InputStream afmIn = null;
  57. for (int i = 0; i < AFM_EXTENSIONS.length; i++) {
  58. try {
  59. String afmUri = this.fontFileURI.substring(0, this.fontFileURI.length() - 4)
  60. + AFM_EXTENSIONS[i];
  61. afmIn = openFontUri(resolver, afmUri);
  62. if (afmIn != null) {
  63. break;
  64. }
  65. } catch (IOException ioe) {
  66. //Ignore, AFM probably not available under the URI
  67. }
  68. }
  69. if (afmIn != null) {
  70. try {
  71. AFMParser afmParser = new AFMParser();
  72. afm = afmParser.parse(afmIn);
  73. } finally {
  74. IOUtils.closeQuietly(afmIn);
  75. }
  76. }
  77. String pfmUri = getPFMURI(this.fontFileURI);
  78. InputStream pfmIn = null;
  79. try {
  80. pfmIn = openFontUri(resolver, pfmUri);
  81. } catch (IOException ioe) {
  82. //Ignore, PFM probably not available under the URI
  83. }
  84. if (pfmIn != null) {
  85. try {
  86. pfm = new PFMFile();
  87. pfm.load(pfmIn);
  88. } finally {
  89. IOUtils.closeQuietly(pfmIn);
  90. }
  91. }
  92. if (afm == null && pfm == null) {
  93. throw new java.io.FileNotFoundException(
  94. "Neither an AFM nor a PFM file was found for " + this.fontFileURI);
  95. }
  96. if (pfm == null) {
  97. //Cannot do without for now
  98. throw new java.io.FileNotFoundException(
  99. "No PFM file was found for " + this.fontFileURI);
  100. }
  101. buildFont(afm, pfm);
  102. this.loaded = true;
  103. }
  104. private void buildFont(AFMFile afm, PFMFile pfm) {
  105. if (afm == null && pfm == null) {
  106. throw new IllegalArgumentException("Need at least an AFM or a PFM!");
  107. }
  108. singleFont = new SingleByteFont();
  109. singleFont.setFontType(FontType.TYPE1);
  110. if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
  111. singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
  112. } else {
  113. log.warn("The PFM reports an unsupported encoding ("
  114. + pfm.getCharSetName() + "). The font may not work as expected.");
  115. singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees!
  116. }
  117. singleFont.setResolver(this.resolver);
  118. returnFont = singleFont;
  119. //Font name
  120. if (afm != null) {
  121. returnFont.setFontName(afm.getFontName()); //PostScript font name
  122. returnFont.setFullName(afm.getFullName());
  123. Set names = new java.util.HashSet();
  124. names.add(afm.getFamilyName());
  125. returnFont.setFamilyNames(names);
  126. } else {
  127. returnFont.setFontName(pfm.getPostscriptName());
  128. String fullName = pfm.getPostscriptName();
  129. fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
  130. returnFont.setFullName(fullName); //emulate afm.getFullName()
  131. Set names = new java.util.HashSet();
  132. names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
  133. returnFont.setFamilyNames(names);
  134. }
  135. //Encoding
  136. if (afm != null) {
  137. String encoding = afm.getEncodingScheme();
  138. if ("AdobeStandardEncoding".equals(encoding)) {
  139. //Use WinAnsi in this case as it better fits the usual character set people need
  140. singleFont.setEncoding(CodePointMapping.WIN_ANSI_ENCODING);
  141. } else {
  142. String effEncodingName;
  143. if ("FontSpecific".equals(encoding)) {
  144. effEncodingName = afm.getFontName() + "Encoding";
  145. } else {
  146. effEncodingName = encoding;
  147. }
  148. if (log.isDebugEnabled()) {
  149. log.debug("Unusual font encoding encountered: "
  150. + encoding + " -> " + effEncodingName);
  151. }
  152. CodePointMapping mapping = buildCustomEncoding(effEncodingName, afm);
  153. singleFont.setEncoding(mapping);
  154. }
  155. }
  156. //Basic metrics
  157. if (afm != null) {
  158. if (afm.getCapHeight() != null) {
  159. returnFont.setCapHeight(afm.getCapHeight().intValue());
  160. }
  161. if (afm.getXHeight() != null) {
  162. returnFont.setXHeight(afm.getXHeight().intValue());
  163. }
  164. if (afm.getAscender() != null) {
  165. returnFont.setAscender(afm.getAscender().intValue());
  166. }
  167. if (afm.getDescender() != null) {
  168. returnFont.setDescender(afm.getDescender().intValue());
  169. }
  170. returnFont.setFontBBox(afm.getFontBBoxAsIntArray());
  171. if (afm.getStdVW() != null) {
  172. returnFont.setStemV(afm.getStdVW().intValue());
  173. } else {
  174. returnFont.setStemV(80); //Arbitrary value
  175. }
  176. returnFont.setItalicAngle((int)afm.getWritingDirectionMetrics(0).getItalicAngle());
  177. } else {
  178. returnFont.setFontBBox(pfm.getFontBBox());
  179. returnFont.setStemV(pfm.getStemV());
  180. returnFont.setItalicAngle(pfm.getItalicAngle());
  181. }
  182. if (pfm != null) {
  183. if (returnFont.getCapHeight() == 0) {
  184. returnFont.setCapHeight(pfm.getCapHeight());
  185. }
  186. if (returnFont.getXHeight(1) == 0) {
  187. returnFont.setXHeight(pfm.getXHeight());
  188. }
  189. if (returnFont.getAscender() == 0) {
  190. returnFont.setAscender(pfm.getLowerCaseAscent());
  191. }
  192. if (returnFont.getDescender() == 0) {
  193. returnFont.setDescender(pfm.getLowerCaseDescent());
  194. }
  195. }
  196. returnFont.setFirstChar(pfm.getFirstChar());
  197. returnFont.setLastChar(pfm.getLastChar());
  198. returnFont.setFlags(pfm.getFlags());
  199. returnFont.setMissingWidth(0);
  200. for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
  201. singleFont.setWidth(i, pfm.getCharWidth(i));
  202. }
  203. returnFont.replaceKerningMap(pfm.getKerning());
  204. singleFont.setEmbedFileName(this.fontFileURI);
  205. }
  206. private CodePointMapping buildCustomEncoding(String encodingName, AFMFile afm) {
  207. List chars = afm.getCharMetrics();
  208. int mappingCount = 0;
  209. //Just count the first time...
  210. Iterator iter = chars.iterator();
  211. while (iter.hasNext()) {
  212. AFMCharMetrics charMetrics = (AFMCharMetrics)iter.next();
  213. if (charMetrics.getCharCode() >= 0) {
  214. String u = charMetrics.getUnicodeChars();
  215. if (u != null) {
  216. mappingCount += u.length();
  217. }
  218. }
  219. }
  220. //...and now build the table.
  221. int[] table = new int[mappingCount * 2];
  222. iter = chars.iterator();
  223. int idx = 0;
  224. while (iter.hasNext()) {
  225. AFMCharMetrics charMetrics = (AFMCharMetrics)iter.next();
  226. if (charMetrics.getCharCode() >= 0) {
  227. String unicodes = charMetrics.getUnicodeChars();
  228. for (int i = 0, c = unicodes.length(); i < c; i++) {
  229. table[idx] = charMetrics.getCharCode();
  230. idx++;
  231. table[idx] = unicodes.charAt(i);
  232. idx++;
  233. }
  234. }
  235. }
  236. return new CodePointMapping(encodingName, table);
  237. }
  238. }