| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.fop.render.java2d; |
| |
| // Java |
| import java.awt.Font; |
| import java.awt.FontMetrics; |
| import java.awt.Graphics2D; |
| import java.awt.RenderingHints; |
| import java.awt.font.LineMetrics; |
| import java.awt.font.TextAttribute; |
| import java.awt.font.TextLayout; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.image.BufferedImage; |
| import java.util.Map; |
| |
| /** |
| * This is a FontMetrics to be used for AWT rendering. |
| * It instanciates a font, depening on family and style |
| * values. The java.awt.FontMetrics for this font is then |
| * created to be used for the actual measurement. |
| * Since layout is word by word and since it is expected that |
| * two subsequent words often share the same style, the |
| * Font and FontMetrics is buffered and only changed if needed. |
| * <p> |
| * Since FontState and FontInfo multiply all factors by |
| * size, we assume a "standard" font of FONT_SIZE. |
| */ |
| public class Java2DFontMetrics { |
| |
| /** |
| * Font size standard used for metric measurements |
| */ |
| public static final int FONT_SIZE = 1; |
| |
| /** |
| * This factor multiplies the calculated values to scale |
| * to FOP internal measurements |
| */ |
| public static final int FONT_FACTOR = (1000 * 1000) / FONT_SIZE; |
| |
| /** |
| * The width of all 256 character, if requested |
| */ |
| private int[] width = null; |
| |
| /** |
| * The typical height of a small cap latter (often derived from "x", value in mpt) |
| */ |
| private int xHeight = 0; |
| |
| /** |
| * The highest point of the font above the baseline (usually derived from "d", value in mpt) |
| */ |
| private int ascender = 0; |
| |
| /** |
| * The lowest point of the font under the baseline (usually derived from "p", value in mpt) |
| */ |
| private int descender = 0; |
| |
| /** |
| * Buffered font. |
| * f1 is bufferd for metric measurements during layout. |
| * fSized is buffered for display purposes |
| */ |
| private Font f1 = null; // , fSized = null; |
| |
| /** |
| * The family type of the font last used |
| */ |
| private String family = ""; |
| |
| /** |
| * The style of the font last used |
| */ |
| private int style = 0; |
| |
| /** |
| * The size of the font last used |
| */ |
| private float size = 0; |
| |
| /** |
| * The FontMetrics object used to calculate character width etc. |
| */ |
| private FontMetrics fmt = null; |
| |
| /** A LineMetrics to access high-resolution metrics information. */ |
| private LineMetrics lineMetrics; |
| |
| /** |
| * Temp graphics object needed to get the font metrics |
| */ |
| private final Graphics2D graphics; |
| |
| /** |
| * Creates a Graphics2D object for the sole purpose of getting font metrics. |
| * @return a Graphics2D object |
| */ |
| private static Graphics2D createFontMetricsGraphics2D() { |
| BufferedImage fontImage = new BufferedImage(100, 100, |
| BufferedImage.TYPE_INT_RGB); |
| Graphics2D graphics2D = fontImage.createGraphics(); |
| //The next line is important to get accurate font metrics! |
| graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, |
| RenderingHints.VALUE_FRACTIONALMETRICS_ON); |
| return graphics2D; |
| } |
| |
| /** |
| * Constructs a new Font-metrics. |
| */ |
| public Java2DFontMetrics() { |
| this.graphics = createFontMetricsGraphics2D(); |
| } |
| |
| /** |
| * Determines the font's maximum ascent of the Font described by the current |
| * FontMetrics object |
| * @param family font family (java name) to use |
| * @param style font style (java def.) to use |
| * @param size font size |
| * @return ascent in milliponts |
| */ |
| public int getMaxAscent(String family, int style, int size) { |
| setFont(family, style, size); |
| return Math.round(lineMetrics.getAscent() * FONT_FACTOR); |
| } |
| |
| /** |
| * Determines the font ascent of the Font described by this |
| * FontMetrics object |
| * @param family font family (java name) to use |
| * @param style font style (java def.) to use |
| * @param size font size |
| * @return ascent in milliponts |
| */ |
| public int getAscender(String family, int style, int size) { |
| setFont(family, style, size); |
| return ascender * 1000; |
| |
| // workaround for sun bug on FontMetrics.getAscent() |
| // http://developer.java.sun.com/developer/bugParade/bugs/4399887.html |
| /* |
| * Bug 4399887 has status Closed, not a bug. The comments on the bug |
| * are: |
| * The submitter is incorrectly assuming that the string he has used |
| * is displaying characters which represent those with the maximum |
| * ascent in the font. If (for example) the unicode character |
| * \u00c1 which is the A-acute character used in many European |
| * languages is placed in the bodu of the "Wrong" string it can be |
| * seen that the JDK is exactly correct in its determination of the |
| * ascent of the font. |
| * If the bounds of a particular string are interesting then the |
| * Rectangle FontMetrics.getStringBounds(..) method can be called. |
| * The y value of the rectangle is the offset from the origin |
| * (baseline) apparently needed by the sample test program |
| * |
| * xxxxx@xxxxx 2001-05-15 |
| */ |
| /* I don't think this is right. |
| int realAscent = fmt.getAscent() |
| - (fmt.getDescent() + fmt.getLeading()); |
| return FONT_FACTOR * realAscent; |
| */ |
| } |
| |
| |
| /** |
| * The size of a capital letter measured from the font's baseline |
| * @param family font family |
| * @param style font style |
| * @param size font size |
| * @return capital height in millipoints |
| */ |
| public int getCapHeight(String family, int style, int size) { |
| // currently just gets Ascent value but maybe should use |
| // getMaxAcent() at some stage |
| return getAscender(family, style, size); |
| } |
| |
| /** |
| * Determines the font descent of the Font described by this |
| * FontMetrics object |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @return descent in milliponts |
| */ |
| public int getDescender(String family, int style, int size) { |
| setFont(family, style, size); |
| return descender * 1000; |
| } |
| |
| /** |
| * Determines the typical font height of a small cap letter |
| * FontMetrics object |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @return font height in milliponts |
| */ |
| public int getXHeight(String family, int style, int size) { |
| setFont(family, style, size); |
| return xHeight * 1000; |
| } |
| |
| /** |
| * Returns width (in 1/1000ths of point size) of character at |
| * code point i |
| * @param i the character for which to get the width |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @return character width in millipoints |
| */ |
| public int width(int i, String family, int style, int size) { |
| int w; |
| setFont(family, style, size); |
| w = internalCharWidth(i) * 1000; |
| return w; |
| } |
| |
| private int internalCharWidth(int i) { |
| //w = (int)(fmt.charWidth(i) * 1000); //Not accurate enough! |
| char[] ch = {(char)i}; |
| Rectangle2D rect = fmt.getStringBounds(ch, 0, 1, this.graphics); |
| return (int)Math.round(rect.getWidth() * 1000); |
| } |
| |
| /** |
| * Return widths (in 1/1000ths of point size) of all |
| * characters |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @return array of character widths in millipoints |
| */ |
| public int[] getWidths(String family, int style, int size) { |
| int i; |
| |
| if (width == null) { |
| width = new int[256]; |
| } |
| setFont(family, style, size); |
| for (i = 0; i < 256; i++) { |
| width[i] = 1000 * internalCharWidth(i); |
| } |
| return width; |
| } |
| |
| private Font getBaseFont(String family, int style, float size) { |
| Map atts = new java.util.HashMap(); |
| atts.put(TextAttribute.FAMILY, family); |
| if ((style & Font.BOLD) != 0) { |
| atts.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); |
| } |
| if ((style & Font.ITALIC) != 0) { |
| atts.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); |
| } |
| atts.put(TextAttribute.SIZE, new Float(size)); //size in pt |
| return new Font(atts); |
| } |
| |
| /** |
| * Checks whether the font for which values are |
| * requested is the one used immediately before or |
| * whether it is a new one |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @return true if the font was changed, false otherwise |
| */ |
| private boolean setFont(String family, int style, int size) { |
| boolean changed = false; |
| float s = size / 1000f; |
| |
| if (f1 == null) { |
| f1 = getBaseFont(family, style, s); |
| fmt = graphics.getFontMetrics(f1); |
| changed = true; |
| } else { |
| if ((this.style != style) || !this.family.equals(family) |
| || this.size != s) { |
| if (family.equals(this.family)) { |
| f1 = f1.deriveFont(style, s); |
| } else { |
| f1 = getBaseFont(family, style, s); |
| } |
| fmt = graphics.getFontMetrics(f1); |
| changed = true; |
| } |
| // else the font is unchanged from last time |
| } |
| if (changed) { |
| //x-Height |
| TextLayout layout = new TextLayout("x", f1, graphics.getFontRenderContext()); |
| Rectangle2D rect = layout.getBounds(); |
| xHeight = (int)Math.round(-rect.getY() * 1000); |
| |
| //PostScript-compatible ascent |
| layout = new TextLayout("d", f1, graphics.getFontRenderContext()); |
| rect = layout.getBounds(); |
| ascender = (int)Math.round(-rect.getY() * 1000); |
| |
| //PostScript-compatible descent |
| layout = new TextLayout("p", f1, graphics.getFontRenderContext()); |
| rect = layout.getBounds(); |
| descender = (int)Math.round((rect.getY() + rect.getHeight()) * -1000); |
| |
| //Alternative way to get metrics but the ascender is again wrong for our purposes |
| lineMetrics = f1.getLineMetrics("", graphics.getFontRenderContext()); |
| } |
| // save the family and style for later comparison |
| this.family = family; |
| this.style = style; |
| this.size = s; |
| return changed; |
| } |
| |
| |
| /** |
| * Returns a java.awt.Font instance for the desired |
| * family, style and size type. |
| * This is here, so that the font-mapping |
| * of FOP-defined fonts to java-fonts can be done |
| * in one place and does not need to occur in |
| * AWTFontRenderer. |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @return font with the desired characeristics. |
| */ |
| public java.awt.Font getFont(String family, int style, int size) { |
| setFont(family, style, size); |
| return f1; |
| /* |
| * if( setFont(family,style, size) ) fSized = null; |
| * if( fSized == null || this.size != size ) { |
| * fSized = f1.deriveFont( size / 1000f ); |
| * } |
| * this.size = size; |
| * return fSized; |
| */ |
| } |
| |
| /** |
| * Indicates whether the font contains a particular character/glyph. |
| * @param family font family (jave name) to use |
| * @param style font style (jave def.) to use |
| * @param size font size |
| * @param c the glyph to check |
| * @return true if the character is supported |
| */ |
| public boolean hasChar(String family, int style, int size, char c) { |
| setFont(family, style, size); |
| return f1.canDisplay(c); |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |