Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

Java2DFontMetrics.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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.render.java2d;
  19. // Java
  20. import java.awt.Font;
  21. import java.awt.FontMetrics;
  22. import java.awt.Graphics2D;
  23. import java.awt.RenderingHints;
  24. import java.awt.font.LineMetrics;
  25. import java.awt.font.TextAttribute;
  26. import java.awt.font.TextLayout;
  27. import java.awt.geom.Rectangle2D;
  28. import java.awt.image.BufferedImage;
  29. import java.util.Map;
  30. /**
  31. * This is a FontMetrics to be used for AWT rendering.
  32. * It instanciates a font, depening on family and style
  33. * values. The java.awt.FontMetrics for this font is then
  34. * created to be used for the actual measurement.
  35. * Since layout is word by word and since it is expected that
  36. * two subsequent words often share the same style, the
  37. * Font and FontMetrics is buffered and only changed if needed.
  38. * <p>
  39. * Since FontState and FontInfo multiply all factors by
  40. * size, we assume a "standard" font of FONT_SIZE.
  41. */
  42. public class Java2DFontMetrics {
  43. /**
  44. * Font size standard used for metric measurements
  45. */
  46. public static final int FONT_SIZE = 1;
  47. /**
  48. * This factor multiplies the calculated values to scale
  49. * to FOP internal measurements
  50. */
  51. public static final int FONT_FACTOR = (1000 * 1000) / FONT_SIZE;
  52. /**
  53. * The width of all 256 character, if requested
  54. */
  55. private int[] width;
  56. /**
  57. * The typical height of a small cap latter (often derived from "x", value in mpt)
  58. */
  59. private int xHeight;
  60. /**
  61. * The highest point of the font above the baseline (usually derived from "d", value in mpt)
  62. */
  63. private int ascender;
  64. /**
  65. * The lowest point of the font under the baseline (usually derived from "p", value in mpt)
  66. */
  67. private int descender;
  68. /**
  69. * Buffered font.
  70. * f1 is bufferd for metric measurements during layout.
  71. * fSized is buffered for display purposes
  72. */
  73. private Font f1; // , fSized;
  74. /**
  75. * The family type of the font last used
  76. */
  77. private String family = "";
  78. /**
  79. * The style of the font last used
  80. */
  81. private int style;
  82. /**
  83. * The size of the font last used
  84. */
  85. private float size;
  86. /**
  87. * The FontMetrics object used to calculate character width etc.
  88. */
  89. private FontMetrics fmt;
  90. /** A LineMetrics to access high-resolution metrics information. */
  91. private LineMetrics lineMetrics;
  92. /**
  93. * Temp graphics object needed to get the font metrics
  94. */
  95. private final Graphics2D graphics;
  96. /**
  97. * Creates a Graphics2D object for the sole purpose of getting font metrics.
  98. * @return a Graphics2D object
  99. */
  100. private static Graphics2D createFontMetricsGraphics2D() {
  101. BufferedImage fontImage = new BufferedImage(100, 100,
  102. BufferedImage.TYPE_INT_RGB);
  103. Graphics2D graphics2D = fontImage.createGraphics();
  104. //The next line is important to get accurate font metrics!
  105. graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
  106. RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  107. return graphics2D;
  108. }
  109. /**
  110. * Constructs a new Font-metrics.
  111. */
  112. public Java2DFontMetrics() {
  113. this.graphics = createFontMetricsGraphics2D();
  114. }
  115. /**
  116. * Determines the font's maximum ascent of the Font described by the current
  117. * FontMetrics object
  118. * @param family font family (java name) to use
  119. * @param style font style (java def.) to use
  120. * @param size font size
  121. * @return ascent in milliponts
  122. */
  123. public int getMaxAscent(String family, int style, int size) {
  124. setFont(family, style, size);
  125. return Math.round(lineMetrics.getAscent() * FONT_FACTOR);
  126. }
  127. /**
  128. * Determines the font ascent of the Font described by this
  129. * FontMetrics object
  130. * @param family font family (java name) to use
  131. * @param style font style (java def.) to use
  132. * @param size font size
  133. * @return ascent in milliponts
  134. */
  135. public int getAscender(String family, int style, int size) {
  136. setFont(family, style, size);
  137. return ascender * 1000;
  138. // workaround for sun bug on FontMetrics.getAscent()
  139. // http://developer.java.sun.com/developer/bugParade/bugs/4399887.html
  140. /*
  141. * Bug 4399887 has status Closed, not a bug. The comments on the bug
  142. * are:
  143. * The submitter is incorrectly assuming that the string he has used
  144. * is displaying characters which represent those with the maximum
  145. * ascent in the font. If (for example) the unicode character
  146. * \u00c1 which is the A-acute character used in many European
  147. * languages is placed in the bodu of the "Wrong" string it can be
  148. * seen that the JDK is exactly correct in its determination of the
  149. * ascent of the font.
  150. * If the bounds of a particular string are interesting then the
  151. * Rectangle FontMetrics.getStringBounds(..) method can be called.
  152. * The y value of the rectangle is the offset from the origin
  153. * (baseline) apparently needed by the sample test program
  154. *
  155. * xxxxx@xxxxx 2001-05-15
  156. */
  157. /* I don't think this is right.
  158. int realAscent = fmt.getAscent()
  159. - (fmt.getDescent() + fmt.getLeading());
  160. return FONT_FACTOR * realAscent;
  161. */
  162. }
  163. /**
  164. * The size of a capital letter measured from the font's baseline
  165. * @param family font family
  166. * @param style font style
  167. * @param size font size
  168. * @return capital height in millipoints
  169. */
  170. public int getCapHeight(String family, int style, int size) {
  171. // currently just gets Ascent value but maybe should use
  172. // getMaxAcent() at some stage
  173. return getAscender(family, style, size);
  174. }
  175. /**
  176. * Determines the font descent of the Font described by this
  177. * FontMetrics object
  178. * @param family font family (jave name) to use
  179. * @param style font style (jave def.) to use
  180. * @param size font size
  181. * @return descent in milliponts
  182. */
  183. public int getDescender(String family, int style, int size) {
  184. setFont(family, style, size);
  185. return descender * 1000;
  186. }
  187. /**
  188. * Determines the typical font height of a small cap letter
  189. * FontMetrics object
  190. * @param family font family (jave name) to use
  191. * @param style font style (jave def.) to use
  192. * @param size font size
  193. * @return font height in milliponts
  194. */
  195. public int getXHeight(String family, int style, int size) {
  196. setFont(family, style, size);
  197. return xHeight * 1000;
  198. }
  199. public int getUnderlinePosition(String family, int style, int size) {
  200. setFont(family, style, size);
  201. return -Math.round(lineMetrics.getUnderlineOffset());
  202. }
  203. public int getUnderlineThickness(String family, int style, int size) {
  204. setFont(family, style, size);
  205. return Math.round(lineMetrics.getUnderlineThickness());
  206. }
  207. public int getStrikeoutPosition(String family, int style, int size) {
  208. setFont(family, style, size);
  209. return -Math.round(lineMetrics.getStrikethroughOffset());
  210. }
  211. public int getStrikeoutThickness(String family, int style, int size) {
  212. setFont(family, style, size);
  213. return Math.round(lineMetrics.getStrikethroughThickness());
  214. }
  215. /**
  216. * Returns width (in 1/1000ths of point size) of character at
  217. * code point i
  218. * @param i the character for which to get the width
  219. * @param family font family (jave name) to use
  220. * @param style font style (jave def.) to use
  221. * @param size font size
  222. * @return character width in millipoints
  223. */
  224. public int width(int i, String family, int style, int size) {
  225. int w;
  226. setFont(family, style, size);
  227. w = internalCharWidth(i) * 1000;
  228. return w;
  229. }
  230. private int internalCharWidth(int i) {
  231. //w = (int)(fmt.charWidth(i) * 1000); //Not accurate enough!
  232. char[] ch = {(char)i};
  233. Rectangle2D rect = fmt.getStringBounds(ch, 0, 1, this.graphics);
  234. return (int)Math.round(rect.getWidth() * 1000);
  235. }
  236. /**
  237. * Return widths (in 1/1000ths of point size) of all
  238. * characters
  239. * @param family font family (jave name) to use
  240. * @param style font style (jave def.) to use
  241. * @param size font size
  242. * @return array of character widths in millipoints
  243. */
  244. public int[] getWidths(String family, int style, int size) {
  245. int i;
  246. if (width == null) {
  247. width = new int[256];
  248. }
  249. setFont(family, style, size);
  250. for (i = 0; i < 256; i++) {
  251. width[i] = 1000 * internalCharWidth(i);
  252. }
  253. return width;
  254. }
  255. private Font getBaseFont(String family, int style, float size) {
  256. Map atts = new java.util.HashMap();
  257. atts.put(TextAttribute.FAMILY, family);
  258. if ((style & Font.BOLD) != 0) {
  259. atts.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
  260. }
  261. if ((style & Font.ITALIC) != 0) {
  262. atts.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
  263. }
  264. atts.put(TextAttribute.SIZE, size); //size in pt
  265. return new Font(atts);
  266. }
  267. /**
  268. * Checks whether the font for which values are
  269. * requested is the one used immediately before or
  270. * whether it is a new one
  271. * @param family font family (jave name) to use
  272. * @param style font style (jave def.) to use
  273. * @param size font size
  274. * @return true if the font was changed, false otherwise
  275. */
  276. private boolean setFont(String family, int style, int size) {
  277. boolean changed = false;
  278. float s = size / 1000f;
  279. if (f1 == null) {
  280. f1 = getBaseFont(family, style, s);
  281. fmt = graphics.getFontMetrics(f1);
  282. changed = true;
  283. } else {
  284. if ((this.style != style) || !this.family.equals(family)
  285. || this.size != s) {
  286. if (family.equals(this.family)) {
  287. f1 = f1.deriveFont(style, s);
  288. } else {
  289. f1 = getBaseFont(family, style, s);
  290. }
  291. fmt = graphics.getFontMetrics(f1);
  292. changed = true;
  293. }
  294. // else the font is unchanged from last time
  295. }
  296. if (changed) {
  297. //x-Height
  298. TextLayout layout = new TextLayout("x", f1, graphics.getFontRenderContext());
  299. Rectangle2D rect = layout.getBounds();
  300. xHeight = (int)Math.round(-rect.getY() * 1000);
  301. //PostScript-compatible ascent
  302. layout = new TextLayout("d", f1, graphics.getFontRenderContext());
  303. rect = layout.getBounds();
  304. ascender = (int)Math.round(-rect.getY() * 1000);
  305. //PostScript-compatible descent
  306. layout = new TextLayout("p", f1, graphics.getFontRenderContext());
  307. rect = layout.getBounds();
  308. descender = (int)Math.round((rect.getY() + rect.getHeight()) * -1000);
  309. //Alternative way to get metrics but the ascender is again wrong for our purposes
  310. lineMetrics = f1.getLineMetrics("", graphics.getFontRenderContext());
  311. }
  312. // save the family and style for later comparison
  313. this.family = family;
  314. this.style = style;
  315. this.size = s;
  316. return changed;
  317. }
  318. /**
  319. * Returns a java.awt.Font instance for the desired
  320. * family, style and size type.
  321. * This is here, so that the font-mapping
  322. * of FOP-defined fonts to java-fonts can be done
  323. * in one place and does not need to occur in
  324. * AWTFontRenderer.
  325. * @param family font family (jave name) to use
  326. * @param style font style (jave def.) to use
  327. * @param size font size
  328. * @return font with the desired characeristics.
  329. */
  330. public java.awt.Font getFont(String family, int style, int size) {
  331. setFont(family, style, size);
  332. return f1;
  333. /*
  334. * if( setFont(family,style, size) ) fSized = null;
  335. * if( fSized == null || this.size != size ) {
  336. * fSized = f1.deriveFont( size / 1000f );
  337. * }
  338. * this.size = size;
  339. * return fSized;
  340. */
  341. }
  342. /**
  343. * Indicates whether the font contains a particular character/glyph.
  344. * @param family font family (jave name) to use
  345. * @param style font style (jave def.) to use
  346. * @param size font size
  347. * @param c the glyph to check
  348. * @return true if the character is supported
  349. */
  350. public boolean hasChar(String family, int style, int size, char c) {
  351. setFont(family, style, size);
  352. return f1.canDisplay(c);
  353. }
  354. }