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.

Font.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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. import java.util.Collections;
  20. import java.util.List;
  21. import java.util.Map;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import org.apache.fop.complexscripts.fonts.Positionable;
  25. import org.apache.fop.complexscripts.fonts.Substitutable;
  26. import org.apache.fop.render.java2d.CustomFontMetricsMapper;
  27. import org.apache.fop.util.CharUtilities;
  28. /**
  29. * This class holds font state information and provides access to the font
  30. * metrics.
  31. */
  32. public class Font implements Substitutable, Positionable {
  33. /** Extra Bold font weight */
  34. public static final int WEIGHT_EXTRA_BOLD = 800;
  35. /** Bold font weight */
  36. public static final int WEIGHT_BOLD = 700;
  37. /** Normal font weight */
  38. public static final int WEIGHT_NORMAL = 400;
  39. /** Light font weight */
  40. public static final int WEIGHT_LIGHT = 200;
  41. /** Normal font style */
  42. public static final String STYLE_NORMAL = "normal";
  43. /** Italic font style */
  44. public static final String STYLE_ITALIC = "italic";
  45. /** Oblique font style */
  46. public static final String STYLE_OBLIQUE = "oblique";
  47. /** Inclined font style */
  48. public static final String STYLE_INCLINED = "inclined";
  49. /** Default selection priority */
  50. public static final int PRIORITY_DEFAULT = 0;
  51. /** Default fallback key */
  52. public static final FontTriplet DEFAULT_FONT = new FontTriplet(
  53. "any", STYLE_NORMAL, WEIGHT_NORMAL, PRIORITY_DEFAULT);
  54. /** logger */
  55. private static Log log = LogFactory.getLog(Font.class);
  56. private final String fontName;
  57. private final FontTriplet triplet;
  58. private final int fontSize;
  59. /**
  60. * normal or small-caps font
  61. */
  62. //private int fontVariant;
  63. private final FontMetrics metric;
  64. /**
  65. * Main constructor
  66. * @param key key of the font
  67. * @param triplet the font triplet that was used to lookup this font (may be null)
  68. * @param met font metrics
  69. * @param fontSize font size
  70. */
  71. public Font(String key, FontTriplet triplet, FontMetrics met, int fontSize) {
  72. this.fontName = key;
  73. this.triplet = triplet;
  74. this.metric = met;
  75. this.fontSize = fontSize;
  76. }
  77. /**
  78. * Returns the associated font metrics object.
  79. * @return the font metrics
  80. */
  81. public FontMetrics getFontMetrics() {
  82. return this.metric;
  83. }
  84. /**
  85. * Determines whether the font is a multibyte font.
  86. * @return True if it is multibyte
  87. */
  88. public boolean isMultiByte() {
  89. return getFontMetrics().isMultiByte();
  90. }
  91. /**
  92. * Returns the font's ascender.
  93. * @return the ascender
  94. */
  95. public int getAscender() {
  96. return metric.getAscender(fontSize) / 1000;
  97. }
  98. /**
  99. * Returns the font's CapHeight.
  100. * @return the capital height
  101. */
  102. public int getCapHeight() {
  103. return metric.getCapHeight(fontSize) / 1000;
  104. }
  105. /**
  106. * Returns the font's Descender.
  107. * @return the descender
  108. */
  109. public int getDescender() {
  110. return metric.getDescender(fontSize) / 1000;
  111. }
  112. /**
  113. * Returns the font's name.
  114. * @return the font name
  115. */
  116. public String getFontName() {
  117. return fontName;
  118. }
  119. /** @return the font triplet that selected this font */
  120. public FontTriplet getFontTriplet() {
  121. return this.triplet;
  122. }
  123. /**
  124. * Returns the font size
  125. * @return the font size
  126. */
  127. public int getFontSize() {
  128. return fontSize;
  129. }
  130. /**
  131. * Returns the XHeight
  132. * @return the XHeight
  133. */
  134. public int getXHeight() {
  135. return metric.getXHeight(fontSize) / 1000;
  136. }
  137. /** @return true if the font has kerning info */
  138. public boolean hasKerning() {
  139. return metric.hasKerningInfo();
  140. }
  141. /** @return true if the font has feature (i.e., at least one lookup matches) */
  142. public boolean hasFeature(int tableType, String script, String language, String feature) {
  143. return metric.hasFeature(tableType, script, language, feature);
  144. }
  145. /**
  146. * Returns the font's kerning table
  147. * @return the kerning table
  148. */
  149. public Map<Integer, Map<Integer, Integer>> getKerning() {
  150. if (metric.hasKerningInfo()) {
  151. return metric.getKerningInfo();
  152. } else {
  153. return Collections.emptyMap();
  154. }
  155. }
  156. /**
  157. * Returns the amount of kerning between two characters.
  158. *
  159. * The value returned measures in pt. So it is already adjusted for font size.
  160. *
  161. * @param ch1 first character
  162. * @param ch2 second character
  163. * @return the distance to adjust for kerning, 0 if there's no kerning
  164. */
  165. public int getKernValue(int ch1, int ch2) {
  166. // Isolate surrogate pair
  167. if ((ch1 >= 0xD800) && (ch1 <= 0xE000)) {
  168. return 0;
  169. } else if ((ch2 >= 0xD800) && (ch2 <= 0xE000)) {
  170. return 0;
  171. }
  172. Map<Integer, Integer> kernPair = getKerning().get(ch1);
  173. if (kernPair != null) {
  174. Integer width = kernPair.get(ch2);
  175. if (width != null) {
  176. return width * getFontSize() / 1000;
  177. }
  178. }
  179. return 0;
  180. }
  181. /**
  182. * Returns the width of a character
  183. * @param charnum character to look up
  184. * @return width of the character
  185. */
  186. public int getWidth(int charnum) {
  187. // returns width of given character number in millipoints
  188. return (metric.getWidth(charnum, fontSize) / 1000);
  189. }
  190. /**
  191. * Map a java character (unicode) to a font character.
  192. * Default uses CodePointMapping.
  193. * @param c character to map
  194. * @return the mapped character
  195. */
  196. public char mapChar(char c) {
  197. if (metric instanceof org.apache.fop.fonts.Typeface) {
  198. return ((org.apache.fop.fonts.Typeface)metric).mapChar(c);
  199. }
  200. // Use default CodePointMapping
  201. char d = CodePointMapping.getMapping("WinAnsiEncoding").mapChar(c);
  202. if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
  203. c = d;
  204. } else {
  205. log.warn("Glyph " + (int) c + " not available in font " + fontName);
  206. c = Typeface.NOT_FOUND;
  207. }
  208. return c;
  209. }
  210. /**
  211. * Map a unicode code point to a font character.
  212. * Default uses CodePointMapping.
  213. * @param cp code point to map
  214. * @return the mapped character
  215. */
  216. public int mapCodePoint(int cp) {
  217. FontMetrics fontMetrics = getRealFontMetrics();
  218. if (fontMetrics instanceof CIDFont) {
  219. return ((CIDFont) fontMetrics).mapCodePoint(cp);
  220. }
  221. if (CharUtilities.isBmpCodePoint(cp)) {
  222. return mapChar((char) cp);
  223. }
  224. return Typeface.NOT_FOUND;
  225. }
  226. /**
  227. * Determines whether this font contains a particular character/glyph.
  228. * @param c character to check
  229. * @return True if the character is supported, False otherwise
  230. */
  231. public boolean hasChar(char c) {
  232. if (metric instanceof org.apache.fop.fonts.Typeface) {
  233. return ((org.apache.fop.fonts.Typeface)metric).hasChar(c);
  234. } else {
  235. // Use default CodePointMapping
  236. return (CodePointMapping.getMapping("WinAnsiEncoding").mapChar(c) > 0);
  237. }
  238. }
  239. /**
  240. * Determines whether this font contains a particular code point/glyph.
  241. * @param cp code point to check
  242. * @return True if the code point is supported, False otherwise
  243. */
  244. public boolean hasCodePoint(int cp) {
  245. FontMetrics realFont = getRealFontMetrics();
  246. if (realFont instanceof CIDFont) {
  247. return ((CIDFont) realFont).hasCodePoint(cp);
  248. }
  249. if (CharUtilities.isBmpCodePoint(cp)) {
  250. return hasChar((char) cp);
  251. }
  252. return false;
  253. }
  254. /**
  255. * Get the real underlying font if it is wrapped inside some container such as a {@link LazyFont} or a
  256. * {@link CustomFontMetricsMapper}.
  257. *
  258. * @return instance of the font
  259. */
  260. private FontMetrics getRealFontMetrics() {
  261. FontMetrics realFontMetrics = metric;
  262. if (realFontMetrics instanceof CustomFontMetricsMapper) {
  263. realFontMetrics = ((CustomFontMetricsMapper) realFontMetrics).getRealFont();
  264. }
  265. if (realFontMetrics instanceof LazyFont) {
  266. return ((LazyFont) realFontMetrics).getRealFont();
  267. }
  268. return realFontMetrics;
  269. }
  270. /**
  271. * {@inheritDoc}
  272. */
  273. @Override
  274. public String toString() {
  275. StringBuffer sbuf = new StringBuffer(super.toString());
  276. sbuf.append('{');
  277. /*
  278. sbuf.append(fontFamily);
  279. sbuf.append(',');*/
  280. sbuf.append(fontName);
  281. sbuf.append(',');
  282. sbuf.append(fontSize);
  283. /*
  284. sbuf.append(',');
  285. sbuf.append(fontStyle);
  286. sbuf.append(',');
  287. sbuf.append(fontWeight);*/
  288. sbuf.append('}');
  289. return sbuf.toString();
  290. }
  291. /**
  292. * Helper method for getting the width of a unicode char
  293. * from the current fontstate.
  294. * This also performs some guessing on widths on various
  295. * versions of space that might not exists in the font.
  296. * @param c character to inspect
  297. * @return the width of the character or -1 if no width available
  298. */
  299. public int getCharWidth(char c) {
  300. int width;
  301. if ((c == '\n') || (c == '\r') || (c == '\t') || (c == '\u00A0')) {
  302. width = getCharWidth(' ');
  303. } else {
  304. if (hasChar(c)) {
  305. int mappedChar = mapChar(c);
  306. width = getWidth(mappedChar);
  307. } else {
  308. width = -1;
  309. }
  310. if (width <= 0) {
  311. // Estimate the width of spaces not represented in
  312. // the font
  313. int em = getFontSize(); //http://en.wikipedia.org/wiki/Em_(typography)
  314. int en = em / 2; //http://en.wikipedia.org/wiki/En_(typography)
  315. if (c == ' ') {
  316. width = em;
  317. } else if (c == '\u2000') {
  318. width = en;
  319. } else if (c == '\u2001') {
  320. width = em;
  321. } else if (c == '\u2002') {
  322. width = em / 2;
  323. } else if (c == '\u2003') {
  324. width = getFontSize();
  325. } else if (c == '\u2004') {
  326. width = em / 3;
  327. } else if (c == '\u2005') {
  328. width = em / 4;
  329. } else if (c == '\u2006') {
  330. width = em / 6;
  331. } else if (c == '\u2007') {
  332. width = getCharWidth('0');
  333. } else if (c == '\u2008') {
  334. width = getCharWidth('.');
  335. } else if (c == '\u2009') {
  336. width = em / 5;
  337. } else if (c == '\u200A') {
  338. width = em / 10;
  339. } else if (c == '\u200B') {
  340. width = 0;
  341. } else if (c == '\u202F') {
  342. width = getCharWidth(' ') / 2;
  343. } else if (c == '\u2060') {
  344. width = 0;
  345. } else if (c == '\u3000') {
  346. width = getCharWidth(' ') * 2;
  347. } else if (c == '\ufeff') {
  348. width = 0;
  349. } else {
  350. //Will be internally replaced by "#" if not found
  351. width = getWidth(mapChar(c));
  352. }
  353. }
  354. }
  355. return width;
  356. }
  357. /**
  358. * Helper method for getting the width of a unicode char
  359. * from the current fontstate.
  360. * This also performs some guessing on widths on various
  361. * versions of space that might not exists in the font.
  362. * @param c character to inspect
  363. * @return the width of the character or -1 if no width available
  364. */
  365. public int getCharWidth(int c) {
  366. if (c < 0x10000) {
  367. return getCharWidth((char) c);
  368. }
  369. if (hasCodePoint(c)) {
  370. int mappedChar = mapCodePoint(c);
  371. return getWidth(mappedChar);
  372. }
  373. return -1;
  374. }
  375. /**
  376. * Calculates the word width.
  377. * @param word text to get width for
  378. * @return the width of the text
  379. */
  380. public int getWordWidth(String word) {
  381. if (word == null) {
  382. return 0;
  383. }
  384. int wordLength = word.length();
  385. int width = 0;
  386. char[] characters = new char[wordLength];
  387. word.getChars(0, wordLength, characters, 0);
  388. for (int i = 0; i < wordLength; i++) {
  389. width += getCharWidth(characters[i]);
  390. }
  391. return width;
  392. }
  393. /** {@inheritDoc} */
  394. public boolean performsSubstitution() {
  395. if (metric instanceof Substitutable) {
  396. Substitutable s = (Substitutable) metric;
  397. return s.performsSubstitution();
  398. } else {
  399. return false;
  400. }
  401. }
  402. /** {@inheritDoc} */
  403. public CharSequence performSubstitution(CharSequence cs,
  404. String script, String language, List associations, boolean retainControls) {
  405. if (metric instanceof Substitutable) {
  406. Substitutable s = (Substitutable) metric;
  407. return s.performSubstitution(cs, script, language, associations, retainControls);
  408. } else {
  409. throw new UnsupportedOperationException();
  410. }
  411. }
  412. /** {@inheritDoc} */
  413. public CharSequence reorderCombiningMarks(CharSequence cs, int[][] gpa,
  414. String script, String language, List associations) {
  415. if (metric instanceof Substitutable) {
  416. Substitutable s = (Substitutable) metric;
  417. return s.reorderCombiningMarks(cs, gpa, script, language, associations);
  418. } else {
  419. throw new UnsupportedOperationException();
  420. }
  421. }
  422. /** {@inheritDoc} */
  423. public boolean performsPositioning() {
  424. if (metric instanceof Positionable) {
  425. Positionable p = (Positionable) metric;
  426. return p.performsPositioning();
  427. } else {
  428. return false;
  429. }
  430. }
  431. /** {@inheritDoc} */
  432. public int[][] performPositioning(CharSequence cs, String script, String language, int fontSize) {
  433. if (metric instanceof Positionable) {
  434. Positionable p = (Positionable) metric;
  435. return p.performPositioning(cs, script, language, fontSize);
  436. } else {
  437. throw new UnsupportedOperationException();
  438. }
  439. }
  440. /** {@inheritDoc} */
  441. public int[][] performPositioning(CharSequence cs, String script, String language) {
  442. return performPositioning(cs, script, language, fontSize);
  443. }
  444. }