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.

AFMFile.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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.awt.geom.Dimension2D;
  20. import java.awt.geom.RectangularShape;
  21. import java.util.Collections;
  22. import java.util.List;
  23. import java.util.Map;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.apache.xmlgraphics.java2d.Dimension2DDouble;
  27. import org.apache.fop.fonts.NamedCharacter;
  28. import org.apache.fop.fonts.SingleByteEncoding;
  29. /**
  30. * Represents the contents of a Type 1 AFM font metrics file.
  31. */
  32. public class AFMFile {
  33. /** logging instance */
  34. private static final Log LOG = LogFactory.getLog(AFMFile.class);
  35. private String fontName;
  36. private String fullName;
  37. private String familyName;
  38. private String weight;
  39. private RectangularShape fontBBox;
  40. private String encodingScheme;
  41. private String characterSet;
  42. private Number capHeight;
  43. private Number xHeight;
  44. private Number ascender;
  45. private Number descender;
  46. private Number stdHW;
  47. private Number stdVW;
  48. private AFMWritingDirectionMetrics[] writingDirectionMetrics
  49. = new AFMWritingDirectionMetrics[3];
  50. private List<AFMCharMetrics> charMetrics = new java.util.ArrayList<AFMCharMetrics>();
  51. private Map<String, AFMCharMetrics> charNameToMetrics
  52. = new java.util.HashMap<String, AFMCharMetrics>();
  53. private int firstChar = -1;
  54. private int lastChar = -1;
  55. private Map<String, Map<String, Dimension2D>> kerningMap;
  56. /**
  57. * Default constructor.
  58. */
  59. public AFMFile() {
  60. //nop
  61. }
  62. /**
  63. * Returns the FontName value.
  64. * @return the font name
  65. */
  66. public String getFontName() {
  67. return fontName;
  68. }
  69. /**
  70. * Sets the FontName value.
  71. * @param fontName the font name to set
  72. */
  73. public void setFontName(String fontName) {
  74. this.fontName = fontName;
  75. }
  76. /**
  77. * Returns the FullName value.
  78. * @return the full name of the font
  79. */
  80. public String getFullName() {
  81. return fullName;
  82. }
  83. /**
  84. * Sets the FullName value.
  85. * @param fullName the full name to set
  86. */
  87. public void setFullName(String fullName) {
  88. this.fullName = fullName;
  89. }
  90. /**
  91. * Returns the FamilyName value.
  92. * @return the family name of the font
  93. */
  94. public String getFamilyName() {
  95. return familyName;
  96. }
  97. /**
  98. * Sets the FamilyName value.
  99. * @param familyName the family name to set
  100. */
  101. public void setFamilyName(String familyName) {
  102. this.familyName = familyName;
  103. }
  104. /**
  105. * Returns the Weight value.
  106. * @return the weight
  107. */
  108. public String getWeight() {
  109. return weight;
  110. }
  111. /**
  112. * Sets the Weight value.
  113. * @param weight the weight to set
  114. */
  115. public void setWeight(String weight) {
  116. this.weight = weight;
  117. }
  118. /**
  119. * Returns the FontBBox value.
  120. * @return the font's bounding box
  121. */
  122. public RectangularShape getFontBBox() {
  123. return fontBBox;
  124. }
  125. /**
  126. * Returns the FontBBox value as integer array.
  127. * @return the font's bounding box
  128. */
  129. public int[] getFontBBoxAsIntArray() {
  130. RectangularShape rect = getFontBBox();
  131. return new int[] {
  132. (int)Math.floor(rect.getMinX()), (int)Math.floor(rect.getMinY()),
  133. (int)Math.ceil(rect.getMaxX()), (int)Math.ceil(rect.getMaxY())};
  134. }
  135. /**
  136. * Sets the FontBBox value.
  137. * @param fontBBox the fontBBox to set
  138. */
  139. public void setFontBBox(RectangularShape fontBBox) {
  140. this.fontBBox = fontBBox;
  141. }
  142. /**
  143. * Returns the EncodingScheme value.
  144. * @return the encoding scheme
  145. */
  146. public String getEncodingScheme() {
  147. return encodingScheme;
  148. }
  149. /**
  150. * Sets the EncodingScheme value
  151. * @param encodingScheme the encodingScheme to set
  152. */
  153. public void setEncodingScheme(String encodingScheme) {
  154. this.encodingScheme = encodingScheme;
  155. }
  156. /**
  157. * Returns the CharacterSet value.
  158. * @return the characterSet
  159. */
  160. public String getCharacterSet() {
  161. return characterSet;
  162. }
  163. /**
  164. * Sets the CharacterSet value.
  165. * @param characterSet the characterSet to set
  166. */
  167. public void setCharacterSet(String characterSet) {
  168. this.characterSet = characterSet;
  169. }
  170. /**
  171. * Returns the CapHeight value.
  172. * @return the capHeight
  173. */
  174. public Number getCapHeight() {
  175. return capHeight;
  176. }
  177. /**
  178. * Sets the CapHeight value.
  179. * @param capHeight the capHeight to set
  180. */
  181. public void setCapHeight(Number capHeight) {
  182. this.capHeight = capHeight;
  183. }
  184. /**
  185. * Returns the XHeight value.
  186. * @return the xHeight
  187. */
  188. public Number getXHeight() {
  189. return xHeight;
  190. }
  191. /**
  192. * Sets the XHeight value.
  193. * @param height the xHeight to set
  194. */
  195. public void setXHeight(Number height) {
  196. xHeight = height;
  197. }
  198. /**
  199. * Returns the Ascender value.
  200. * @return the ascender
  201. */
  202. public Number getAscender() {
  203. return ascender;
  204. }
  205. /**
  206. * Sets the Ascender value.
  207. * @param ascender the ascender to set
  208. */
  209. public void setAscender(Number ascender) {
  210. this.ascender = ascender;
  211. }
  212. /**
  213. * Returns the Descender value.
  214. * @return the descender
  215. */
  216. public Number getDescender() {
  217. return descender;
  218. }
  219. /**
  220. * Sets the Descender value.
  221. * @param descender the descender to set
  222. */
  223. public void setDescender(Number descender) {
  224. this.descender = descender;
  225. }
  226. /**
  227. * Returns the StdHW value.
  228. * @return the descender
  229. */
  230. public Number getStdHW() {
  231. return stdHW;
  232. }
  233. /**
  234. * Sets the StdHW value.
  235. * @param stdHW the StdHW to set
  236. */
  237. public void setStdHW(Number stdHW) {
  238. this.stdHW = stdHW;
  239. }
  240. /**
  241. * Returns the StdVW value.
  242. * @return the descender
  243. */
  244. public Number getStdVW() {
  245. return stdVW;
  246. }
  247. /**
  248. * Sets the StdVW value.
  249. * @param stdVW the StdVW to set
  250. */
  251. public void setStdVW(Number stdVW) {
  252. this.stdVW = stdVW;
  253. }
  254. /**
  255. * Gets writing direction metrics.
  256. * @param index the writing direction (0, 1 or 2)
  257. * @return the writing direction metrics
  258. */
  259. public AFMWritingDirectionMetrics getWritingDirectionMetrics(int index) {
  260. return this.writingDirectionMetrics[index];
  261. }
  262. /**
  263. * Sets writing direction metrics.
  264. * @param index the writing direction (0, 1 or 2)
  265. * @param metrics the writing direction metrics
  266. */
  267. public void setWritingDirectionMetrics(int index, AFMWritingDirectionMetrics metrics) {
  268. this.writingDirectionMetrics[index] = metrics;
  269. }
  270. /**
  271. * Adds new character metrics.
  272. * @param metrics the character metrics
  273. */
  274. public void addCharMetrics(AFMCharMetrics metrics) {
  275. String name = metrics.getCharName();
  276. if (metrics.getUnicodeSequence() == null && name.equals(".notdef")) {
  277. //Ignore as no Unicode assignment is possible
  278. return;
  279. }
  280. this.charMetrics.add(metrics);
  281. if (name != null) {
  282. this.charNameToMetrics.put(name, metrics);
  283. }
  284. int idx = metrics.getCharCode();
  285. if (idx >= 0) { //Only if the character is part of the encoding
  286. if (firstChar < 0 || idx < firstChar) {
  287. firstChar = idx;
  288. }
  289. if (lastChar < 0 || idx > lastChar) {
  290. lastChar = idx;
  291. }
  292. }
  293. }
  294. /**
  295. * Returns the number of character available for this font.
  296. * @return the number of character
  297. */
  298. public int getCharCount() {
  299. return this.charMetrics.size();
  300. }
  301. /**
  302. * Returns the first character index in the encoding that has a glyph.
  303. * @return the first character index with a glyph
  304. */
  305. public int getFirstChar() {
  306. return this.firstChar;
  307. }
  308. /**
  309. * Returns the last character index in the encoding that has a glyph.
  310. * @return the last character index with a glyph
  311. */
  312. public int getLastChar() {
  313. return this.lastChar;
  314. }
  315. /**
  316. * Returns the character metrics associated with the character name.
  317. * @param name the character name
  318. * @return the character metrics or null if there's no such character
  319. */
  320. public AFMCharMetrics getChar(String name) {
  321. return this.charNameToMetrics.get(name);
  322. }
  323. /**
  324. * Returns the list of AFMCharMetrics instances representing all the available characters.
  325. * @return a List of AFMCharMetrics instances
  326. */
  327. public List<AFMCharMetrics> getCharMetrics() {
  328. return Collections.unmodifiableList(this.charMetrics);
  329. }
  330. /**
  331. * Adds a X-kerning entry.
  332. * @param name1 the name of the first character
  333. * @param name2 the name of the second character
  334. * @param kx kerning value in x-direction
  335. */
  336. public void addXKerning(String name1, String name2, double kx) {
  337. if (this.kerningMap == null) {
  338. this.kerningMap = new java.util.HashMap<String, Map<String, Dimension2D>>();
  339. }
  340. Map<String, Dimension2D> entries = this.kerningMap.get(name1);
  341. if (entries == null) {
  342. entries = new java.util.HashMap<String, Dimension2D>();
  343. this.kerningMap.put(name1, entries);
  344. }
  345. entries.put(name2, new Dimension2DDouble(kx, 0));
  346. }
  347. /**
  348. * Indicates whether the font has kerning information.
  349. * @return true if there is kerning information
  350. */
  351. public boolean hasKerning() {
  352. return this.kerningMap != null;
  353. }
  354. /**
  355. * Creates and returns a kerning map for writing mode 0 (ltr) with character codes.
  356. * @return the kerning map or null if there is no kerning information.
  357. */
  358. public Map<Integer, Map<Integer, Integer>> createXKerningMapEncoded() {
  359. if (!hasKerning()) {
  360. return null;
  361. }
  362. Map<Integer, Map<Integer, Integer>> m
  363. = new java.util.HashMap<Integer, Map<Integer, Integer>>();
  364. for (Map.Entry<String, Map<String, Dimension2D>> entryFrom : this.kerningMap.entrySet()) {
  365. String name1 = entryFrom.getKey();
  366. AFMCharMetrics chm1 = getChar(name1);
  367. if (chm1 == null || !chm1.hasCharCode()) {
  368. continue;
  369. }
  370. Map<Integer, Integer> container = null;
  371. Map<String, Dimension2D> entriesTo = entryFrom.getValue();
  372. for (Map.Entry<String, Dimension2D> entryTo : entriesTo.entrySet()) {
  373. String name2 = entryTo.getKey();
  374. AFMCharMetrics chm2 = getChar(name2);
  375. if (chm2 == null || !chm2.hasCharCode()) {
  376. continue;
  377. }
  378. if (container == null) {
  379. Integer k1 = Integer.valueOf(chm1.getCharCode());
  380. container = m.get(k1);
  381. if (container == null) {
  382. container = new java.util.HashMap<Integer, Integer>();
  383. m.put(k1, container);
  384. }
  385. }
  386. Dimension2D dim = entryTo.getValue();
  387. container.put(Integer.valueOf(chm2.getCharCode()),
  388. Integer.valueOf((int)Math.round(dim.getWidth())));
  389. }
  390. }
  391. return m;
  392. }
  393. /**
  394. * The character codes in an AFM cannot always be trusted to be the same values as in the
  395. * font's primary encoding. Therefore, we provide a way to override this primary encoding.
  396. * @param encoding the encoding to replace the one given in the AFM
  397. */
  398. public void overridePrimaryEncoding(SingleByteEncoding encoding) {
  399. if (LOG.isDebugEnabled()) {
  400. LOG.debug("Overriding primary encoding of " + getFontName() + " with: " + encoding);
  401. }
  402. AFMCharMetrics[] mapped = new AFMCharMetrics[256];
  403. for (AFMCharMetrics cm : this.charMetrics) {
  404. NamedCharacter nc = cm.getCharacter();
  405. if (nc.hasSingleUnicodeValue()) {
  406. int codePoint = encoding.mapChar(nc.getSingleUnicodeValue());
  407. if (codePoint > 0) {
  408. if (mapped[codePoint] != null) {
  409. if (LOG.isDebugEnabled()) {
  410. AFMCharMetrics other = mapped[codePoint];
  411. String msg = "Not mapping character " + nc + " to code point "
  412. + codePoint + " (" + Integer.toHexString(codePoint) + ") in "
  413. + encoding + ". "
  414. + other + " has already been assigned that code point.";
  415. if (other.getUnicodeSequence()
  416. .equals(nc.getUnicodeSequence())) {
  417. msg += " This is a specialized glyph for the"
  418. + " same Unicode character.";
  419. //TODO should these be mapped to a private Unicode area to make
  420. //them accessible?
  421. } else {
  422. msg += " This is a similar character.";
  423. }
  424. if (cm.getWidthX() != other.getWidthX()) {
  425. msg += " They have differing widths: "
  426. + cm.getWidthX() + " vs. " + other.getWidthX();
  427. }
  428. LOG.debug(msg);
  429. }
  430. } else {
  431. cm.setCharCode(codePoint);
  432. mapped[codePoint] = cm;
  433. }
  434. } else {
  435. cm.setCharCode(-1);
  436. }
  437. } else {
  438. //No Unicode equivalent
  439. cm.setCharCode(-1);
  440. }
  441. }
  442. }
  443. /** {@inheritDoc} */
  444. @Override
  445. public String toString() {
  446. return "AFM: " + getFullName();
  447. }
  448. }