/*
 * 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: Font.java 1827168 2018-03-19 08:49:57Z ssteiner $ */

package org.apache.fop.fonts;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.complexscripts.fonts.Positionable;
import org.apache.fop.complexscripts.fonts.Substitutable;
import org.apache.fop.render.java2d.CustomFontMetricsMapper;
import org.apache.fop.util.CharUtilities;

This class holds font state information and provides access to the font metrics.
/** * This class holds font state information and provides access to the font * metrics. */
public class Font implements Substitutable, Positionable {
Extra Bold font weight
/** Extra Bold font weight */
public static final int WEIGHT_EXTRA_BOLD = 800;
Bold font weight
/** Bold font weight */
public static final int WEIGHT_BOLD = 700;
Normal font weight
/** Normal font weight */
public static final int WEIGHT_NORMAL = 400;
Light font weight
/** Light font weight */
public static final int WEIGHT_LIGHT = 200;
Normal font style
/** Normal font style */
public static final String STYLE_NORMAL = "normal";
Italic font style
/** Italic font style */
public static final String STYLE_ITALIC = "italic";
Oblique font style
/** Oblique font style */
public static final String STYLE_OBLIQUE = "oblique";
Inclined font style
/** Inclined font style */
public static final String STYLE_INCLINED = "inclined";
Default selection priority
/** Default selection priority */
public static final int PRIORITY_DEFAULT = 0;
Default fallback key
/** Default fallback key */
public static final FontTriplet DEFAULT_FONT = new FontTriplet( "any", STYLE_NORMAL, WEIGHT_NORMAL, PRIORITY_DEFAULT);
logger
/** logger */
private static Log log = LogFactory.getLog(Font.class); private final String fontName; private final FontTriplet triplet; private final int fontSize;
normal or small-caps font
/** * normal or small-caps font */
//private int fontVariant; private final FontMetrics metric;
Main constructor
Params:
  • key – key of the font
  • triplet – the font triplet that was used to lookup this font (may be null)
  • met – font metrics
  • fontSize – font size
/** * Main constructor * @param key key of the font * @param triplet the font triplet that was used to lookup this font (may be null) * @param met font metrics * @param fontSize font size */
public Font(String key, FontTriplet triplet, FontMetrics met, int fontSize) { this.fontName = key; this.triplet = triplet; this.metric = met; this.fontSize = fontSize; }
Returns the associated font metrics object.
Returns:the font metrics
/** * Returns the associated font metrics object. * @return the font metrics */
public FontMetrics getFontMetrics() { return this.metric; }
Determines whether the font is a multibyte font.
Returns:True if it is multibyte
/** * Determines whether the font is a multibyte font. * @return True if it is multibyte */
public boolean isMultiByte() { return getFontMetrics().isMultiByte(); }
Returns the font's ascender.
Returns:the ascender
/** * Returns the font's ascender. * @return the ascender */
public int getAscender() { return metric.getAscender(fontSize) / 1000; }
Returns the font's CapHeight.
Returns:the capital height
/** * Returns the font's CapHeight. * @return the capital height */
public int getCapHeight() { return metric.getCapHeight(fontSize) / 1000; }
Returns the font's Descender.
Returns:the descender
/** * Returns the font's Descender. * @return the descender */
public int getDescender() { return metric.getDescender(fontSize) / 1000; }
Returns the font's name.
Returns:the font name
/** * Returns the font's name. * @return the font name */
public String getFontName() { return fontName; }
Returns:the font triplet that selected this font
/** @return the font triplet that selected this font */
public FontTriplet getFontTriplet() { return this.triplet; }
Returns the font size
Returns:the font size
/** * Returns the font size * @return the font size */
public int getFontSize() { return fontSize; }
Returns the XHeight
Returns:the XHeight
/** * Returns the XHeight * @return the XHeight */
public int getXHeight() { return metric.getXHeight(fontSize) / 1000; }
Returns:true if the font has kerning info
/** @return true if the font has kerning info */
public boolean hasKerning() { return metric.hasKerningInfo(); }
Returns:true if the font has feature (i.e., at least one lookup matches)
/** @return true if the font has feature (i.e., at least one lookup matches) */
public boolean hasFeature(int tableType, String script, String language, String feature) { return metric.hasFeature(tableType, script, language, feature); }
Returns the font's kerning table
Returns:the kerning table
/** * Returns the font's kerning table * @return the kerning table */
public Map<Integer, Map<Integer, Integer>> getKerning() { if (metric.hasKerningInfo()) { return metric.getKerningInfo(); } else { return Collections.emptyMap(); } }
Returns the amount of kerning between two characters. The value returned measures in pt. So it is already adjusted for font size.
Params:
  • ch1 – first character
  • ch2 – second character
Returns:the distance to adjust for kerning, 0 if there's no kerning
/** * Returns the amount of kerning between two characters. * * The value returned measures in pt. So it is already adjusted for font size. * * @param ch1 first character * @param ch2 second character * @return the distance to adjust for kerning, 0 if there's no kerning */
public int getKernValue(int ch1, int ch2) { // Isolate surrogate pair if ((ch1 >= 0xD800) && (ch1 <= 0xE000)) { return 0; } else if ((ch2 >= 0xD800) && (ch2 <= 0xE000)) { return 0; } Map<Integer, Integer> kernPair = getKerning().get(ch1); if (kernPair != null) { Integer width = kernPair.get(ch2); if (width != null) { return width * getFontSize() / 1000; } } return 0; }
Returns the width of a character
Params:
  • charnum – character to look up
Returns:width of the character
/** * Returns the width of a character * @param charnum character to look up * @return width of the character */
public int getWidth(int charnum) { // returns width of given character number in millipoints return (metric.getWidth(charnum, fontSize) / 1000); }
Map a java character (unicode) to a font character. Default uses CodePointMapping.
Params:
  • c – character to map
Returns:the mapped character
/** * Map a java character (unicode) to a font character. * Default uses CodePointMapping. * @param c character to map * @return the mapped character */
public char mapChar(char c) { if (metric instanceof org.apache.fop.fonts.Typeface) { return ((org.apache.fop.fonts.Typeface)metric).mapChar(c); } // Use default CodePointMapping char d = CodePointMapping.getMapping("WinAnsiEncoding").mapChar(c); if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) { c = d; } else { log.warn("Glyph " + (int) c + " not available in font " + fontName); c = Typeface.NOT_FOUND; } return c; }
Map a unicode code point to a font character. Default uses CodePointMapping.
Params:
  • cp – code point to map
Returns:the mapped character
/** * Map a unicode code point to a font character. * Default uses CodePointMapping. * @param cp code point to map * @return the mapped character */
public int mapCodePoint(int cp) { FontMetrics fontMetrics = getRealFontMetrics(); if (fontMetrics instanceof CIDFont) { return ((CIDFont) fontMetrics).mapCodePoint(cp); } if (CharUtilities.isBmpCodePoint(cp)) { return mapChar((char) cp); } return Typeface.NOT_FOUND; }
Determines whether this font contains a particular character/glyph.
Params:
  • c – character to check
Returns:True if the character is supported, False otherwise
/** * Determines whether this font contains a particular character/glyph. * @param c character to check * @return True if the character is supported, False otherwise */
public boolean hasChar(char c) { if (metric instanceof org.apache.fop.fonts.Typeface) { return ((org.apache.fop.fonts.Typeface)metric).hasChar(c); } else { // Use default CodePointMapping return (CodePointMapping.getMapping("WinAnsiEncoding").mapChar(c) > 0); } }
Determines whether this font contains a particular code point/glyph.
Params:
  • cp – code point to check
Returns:True if the code point is supported, False otherwise
/** * Determines whether this font contains a particular code point/glyph. * @param cp code point to check * @return True if the code point is supported, False otherwise */
public boolean hasCodePoint(int cp) { FontMetrics realFont = getRealFontMetrics(); if (realFont instanceof CIDFont) { return ((CIDFont) realFont).hasCodePoint(cp); } if (CharUtilities.isBmpCodePoint(cp)) { return hasChar((char) cp); } return false; }
Get the real underlying font if it is wrapped inside some container such as a LazyFont or a CustomFontMetricsMapper.
Returns:instance of the font
/** * Get the real underlying font if it is wrapped inside some container such as a {@link LazyFont} or a * {@link CustomFontMetricsMapper}. * * @return instance of the font */
private FontMetrics getRealFontMetrics() { FontMetrics realFontMetrics = metric; if (realFontMetrics instanceof CustomFontMetricsMapper) { realFontMetrics = ((CustomFontMetricsMapper) realFontMetrics).getRealFont(); } if (realFontMetrics instanceof LazyFont) { return ((LazyFont) realFontMetrics).getRealFont(); } return realFontMetrics; }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public String toString() { StringBuffer sbuf = new StringBuffer(super.toString()); sbuf.append('{'); /* sbuf.append(fontFamily); sbuf.append(',');*/ sbuf.append(fontName); sbuf.append(','); sbuf.append(fontSize); /* sbuf.append(','); sbuf.append(fontStyle); sbuf.append(','); sbuf.append(fontWeight);*/ sbuf.append('}'); return sbuf.toString(); }
Helper method for getting the width of a unicode char from the current fontstate. This also performs some guessing on widths on various versions of space that might not exists in the font.
Params:
  • c – character to inspect
Returns:the width of the character or -1 if no width available
/** * Helper method for getting the width of a unicode char * from the current fontstate. * This also performs some guessing on widths on various * versions of space that might not exists in the font. * @param c character to inspect * @return the width of the character or -1 if no width available */
public int getCharWidth(char c) { int width; if ((c == '\n') || (c == '\r') || (c == '\t') || (c == '\u00A0')) { width = getCharWidth(' '); } else { if (hasChar(c)) { int mappedChar = mapChar(c); width = getWidth(mappedChar); } else { width = -1; } if (width <= 0) { // Estimate the width of spaces not represented in // the font int em = getFontSize(); //http://en.wikipedia.org/wiki/Em_(typography) int en = em / 2; //http://en.wikipedia.org/wiki/En_(typography) if (c == ' ') { width = em; } else if (c == '\u2000') { width = en; } else if (c == '\u2001') { width = em; } else if (c == '\u2002') { width = em / 2; } else if (c == '\u2003') { width = getFontSize(); } else if (c == '\u2004') { width = em / 3; } else if (c == '\u2005') { width = em / 4; } else if (c == '\u2006') { width = em / 6; } else if (c == '\u2007') { width = getCharWidth('0'); } else if (c == '\u2008') { width = getCharWidth('.'); } else if (c == '\u2009') { width = em / 5; } else if (c == '\u200A') { width = em / 10; } else if (c == '\u200B') { width = 0; } else if (c == '\u202F') { width = getCharWidth(' ') / 2; } else if (c == '\u2060') { width = 0; } else if (c == '\u3000') { width = getCharWidth(' ') * 2; } else if (c == '\ufeff') { width = 0; } else { //Will be internally replaced by "#" if not found width = getWidth(mapChar(c)); } } } return width; }
Helper method for getting the width of a unicode char from the current fontstate. This also performs some guessing on widths on various versions of space that might not exists in the font.
Params:
  • c – character to inspect
Returns:the width of the character or -1 if no width available
/** * Helper method for getting the width of a unicode char * from the current fontstate. * This also performs some guessing on widths on various * versions of space that might not exists in the font. * @param c character to inspect * @return the width of the character or -1 if no width available */
public int getCharWidth(int c) { if (c < 0x10000) { return getCharWidth((char) c); } if (hasCodePoint(c)) { int mappedChar = mapCodePoint(c); return getWidth(mappedChar); } return -1; }
Calculates the word width.
Params:
  • word – text to get width for
Returns:the width of the text
/** * Calculates the word width. * @param word text to get width for * @return the width of the text */
public int getWordWidth(String word) { if (word == null) { return 0; } int wordLength = word.length(); int width = 0; char[] characters = new char[wordLength]; word.getChars(0, wordLength, characters, 0); for (int i = 0; i < wordLength; i++) { width += getCharWidth(characters[i]); } return width; }
{@inheritDoc}
/** {@inheritDoc} */
public boolean performsSubstitution() { if (metric instanceof Substitutable) { Substitutable s = (Substitutable) metric; return s.performsSubstitution(); } else { return false; } }
{@inheritDoc}
/** {@inheritDoc} */
public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, boolean retainControls) { if (metric instanceof Substitutable) { Substitutable s = (Substitutable) metric; return s.performSubstitution(cs, script, language, associations, retainControls); } else { throw new UnsupportedOperationException(); } }
{@inheritDoc}
/** {@inheritDoc} */
public CharSequence reorderCombiningMarks(CharSequence cs, int[][] gpa, String script, String language, List associations) { if (metric instanceof Substitutable) { Substitutable s = (Substitutable) metric; return s.reorderCombiningMarks(cs, gpa, script, language, associations); } else { throw new UnsupportedOperationException(); } }
{@inheritDoc}
/** {@inheritDoc} */
public boolean performsPositioning() { if (metric instanceof Positionable) { Positionable p = (Positionable) metric; return p.performsPositioning(); } else { return false; } }
{@inheritDoc}
/** {@inheritDoc} */
public int[][] performPositioning(CharSequence cs, String script, String language, int fontSize) { if (metric instanceof Positionable) { Positionable p = (Positionable) metric; return p.performPositioning(cs, script, language, fontSize); } else { throw new UnsupportedOperationException(); } }
{@inheritDoc}
/** {@inheritDoc} */
public int[][] performPositioning(CharSequence cs, String script, String language) { return performPositioning(cs, script, language, fontSize); } }