/*
 * 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: AFMFile.java 1758773 2016-09-01 13:02:29Z ssteiner $ */

package org.apache.fop.fonts.type1;

import java.awt.geom.Dimension2D;
import java.awt.geom.RectangularShape;
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.xmlgraphics.java2d.Dimension2DDouble;

import org.apache.fop.fonts.NamedCharacter;
import org.apache.fop.fonts.SingleByteEncoding;

Represents the contents of a Type 1 AFM font metrics file.
/** * Represents the contents of a Type 1 AFM font metrics file. */
public class AFMFile {
logging instance
/** logging instance */
private static final Log LOG = LogFactory.getLog(AFMFile.class); private String fontName; private String fullName; private String familyName; private String weight; private RectangularShape fontBBox; private String encodingScheme; private String characterSet; private Number capHeight; private Number xHeight; private Number ascender; private Number descender; private Number stdHW; private Number stdVW; private AFMWritingDirectionMetrics[] writingDirectionMetrics = new AFMWritingDirectionMetrics[3]; private List<AFMCharMetrics> charMetrics = new java.util.ArrayList<AFMCharMetrics>(); private Map<String, AFMCharMetrics> charNameToMetrics = new java.util.HashMap<String, AFMCharMetrics>(); private int firstChar = -1; private int lastChar = -1; private Map<String, Map<String, Dimension2D>> kerningMap;
Default constructor.
/** * Default constructor. */
public AFMFile() { //nop }
Returns the FontName value.
Returns:the font name
/** * Returns the FontName value. * @return the font name */
public String getFontName() { return fontName; }
Sets the FontName value.
Params:
  • fontName – the font name to set
/** * Sets the FontName value. * @param fontName the font name to set */
public void setFontName(String fontName) { this.fontName = fontName; }
Returns the FullName value.
Returns:the full name of the font
/** * Returns the FullName value. * @return the full name of the font */
public String getFullName() { return fullName; }
Sets the FullName value.
Params:
  • fullName – the full name to set
/** * Sets the FullName value. * @param fullName the full name to set */
public void setFullName(String fullName) { this.fullName = fullName; }
Returns the FamilyName value.
Returns:the family name of the font
/** * Returns the FamilyName value. * @return the family name of the font */
public String getFamilyName() { return familyName; }
Sets the FamilyName value.
Params:
  • familyName – the family name to set
/** * Sets the FamilyName value. * @param familyName the family name to set */
public void setFamilyName(String familyName) { this.familyName = familyName; }
Returns the Weight value.
Returns:the weight
/** * Returns the Weight value. * @return the weight */
public String getWeight() { return weight; }
Sets the Weight value.
Params:
  • weight – the weight to set
/** * Sets the Weight value. * @param weight the weight to set */
public void setWeight(String weight) { this.weight = weight; }
Returns the FontBBox value.
Returns:the font's bounding box
/** * Returns the FontBBox value. * @return the font's bounding box */
public RectangularShape getFontBBox() { return fontBBox; }
Returns the FontBBox value as integer array.
Returns:the font's bounding box
/** * Returns the FontBBox value as integer array. * @return the font's bounding box */
public int[] getFontBBoxAsIntArray() { RectangularShape rect = getFontBBox(); return new int[] { (int)Math.floor(rect.getMinX()), (int)Math.floor(rect.getMinY()), (int)Math.ceil(rect.getMaxX()), (int)Math.ceil(rect.getMaxY())}; }
Sets the FontBBox value.
Params:
  • fontBBox – the fontBBox to set
/** * Sets the FontBBox value. * @param fontBBox the fontBBox to set */
public void setFontBBox(RectangularShape fontBBox) { this.fontBBox = fontBBox; }
Returns the EncodingScheme value.
Returns:the encoding scheme
/** * Returns the EncodingScheme value. * @return the encoding scheme */
public String getEncodingScheme() { return encodingScheme; }
Sets the EncodingScheme value
Params:
  • encodingScheme – the encodingScheme to set
/** * Sets the EncodingScheme value * @param encodingScheme the encodingScheme to set */
public void setEncodingScheme(String encodingScheme) { this.encodingScheme = encodingScheme; }
Returns the CharacterSet value.
Returns:the characterSet
/** * Returns the CharacterSet value. * @return the characterSet */
public String getCharacterSet() { return characterSet; }
Sets the CharacterSet value.
Params:
  • characterSet – the characterSet to set
/** * Sets the CharacterSet value. * @param characterSet the characterSet to set */
public void setCharacterSet(String characterSet) { this.characterSet = characterSet; }
Returns the CapHeight value.
Returns:the capHeight
/** * Returns the CapHeight value. * @return the capHeight */
public Number getCapHeight() { return capHeight; }
Sets the CapHeight value.
Params:
  • capHeight – the capHeight to set
/** * Sets the CapHeight value. * @param capHeight the capHeight to set */
public void setCapHeight(Number capHeight) { this.capHeight = capHeight; }
Returns the XHeight value.
Returns:the xHeight
/** * Returns the XHeight value. * @return the xHeight */
public Number getXHeight() { return xHeight; }
Sets the XHeight value.
Params:
  • height – the xHeight to set
/** * Sets the XHeight value. * @param height the xHeight to set */
public void setXHeight(Number height) { xHeight = height; }
Returns the Ascender value.
Returns:the ascender
/** * Returns the Ascender value. * @return the ascender */
public Number getAscender() { return ascender; }
Sets the Ascender value.
Params:
  • ascender – the ascender to set
/** * Sets the Ascender value. * @param ascender the ascender to set */
public void setAscender(Number ascender) { this.ascender = ascender; }
Returns the Descender value.
Returns:the descender
/** * Returns the Descender value. * @return the descender */
public Number getDescender() { return descender; }
Sets the Descender value.
Params:
  • descender – the descender to set
/** * Sets the Descender value. * @param descender the descender to set */
public void setDescender(Number descender) { this.descender = descender; }
Returns the StdHW value.
Returns:the descender
/** * Returns the StdHW value. * @return the descender */
public Number getStdHW() { return stdHW; }
Sets the StdHW value.
Params:
  • stdHW – the StdHW to set
/** * Sets the StdHW value. * @param stdHW the StdHW to set */
public void setStdHW(Number stdHW) { this.stdHW = stdHW; }
Returns the StdVW value.
Returns:the descender
/** * Returns the StdVW value. * @return the descender */
public Number getStdVW() { return stdVW; }
Sets the StdVW value.
Params:
  • stdVW – the StdVW to set
/** * Sets the StdVW value. * @param stdVW the StdVW to set */
public void setStdVW(Number stdVW) { this.stdVW = stdVW; }
Gets writing direction metrics.
Params:
  • index – the writing direction (0, 1 or 2)
Returns:the writing direction metrics
/** * Gets writing direction metrics. * @param index the writing direction (0, 1 or 2) * @return the writing direction metrics */
public AFMWritingDirectionMetrics getWritingDirectionMetrics(int index) { return this.writingDirectionMetrics[index]; }
Sets writing direction metrics.
Params:
  • index – the writing direction (0, 1 or 2)
  • metrics – the writing direction metrics
/** * Sets writing direction metrics. * @param index the writing direction (0, 1 or 2) * @param metrics the writing direction metrics */
public void setWritingDirectionMetrics(int index, AFMWritingDirectionMetrics metrics) { this.writingDirectionMetrics[index] = metrics; }
Adds new character metrics.
Params:
  • metrics – the character metrics
/** * Adds new character metrics. * @param metrics the character metrics */
public void addCharMetrics(AFMCharMetrics metrics) { String name = metrics.getCharName(); if (metrics.getUnicodeSequence() == null && name.equals(".notdef")) { //Ignore as no Unicode assignment is possible return; } this.charMetrics.add(metrics); if (name != null) { this.charNameToMetrics.put(name, metrics); } int idx = metrics.getCharCode(); if (idx >= 0) { //Only if the character is part of the encoding if (firstChar < 0 || idx < firstChar) { firstChar = idx; } if (lastChar < 0 || idx > lastChar) { lastChar = idx; } } }
Returns the number of character available for this font.
Returns:the number of character
/** * Returns the number of character available for this font. * @return the number of character */
public int getCharCount() { return this.charMetrics.size(); }
Returns the first character index in the encoding that has a glyph.
Returns:the first character index with a glyph
/** * Returns the first character index in the encoding that has a glyph. * @return the first character index with a glyph */
public int getFirstChar() { return this.firstChar; }
Returns the last character index in the encoding that has a glyph.
Returns:the last character index with a glyph
/** * Returns the last character index in the encoding that has a glyph. * @return the last character index with a glyph */
public int getLastChar() { return this.lastChar; }
Returns the character metrics associated with the character name.
Params:
  • name – the character name
Returns:the character metrics or null if there's no such character
/** * Returns the character metrics associated with the character name. * @param name the character name * @return the character metrics or null if there's no such character */
public AFMCharMetrics getChar(String name) { return this.charNameToMetrics.get(name); }
Returns the list of AFMCharMetrics instances representing all the available characters.
Returns:a List of AFMCharMetrics instances
/** * Returns the list of AFMCharMetrics instances representing all the available characters. * @return a List of AFMCharMetrics instances */
public List<AFMCharMetrics> getCharMetrics() { return Collections.unmodifiableList(this.charMetrics); }
Adds a X-kerning entry.
Params:
  • name1 – the name of the first character
  • name2 – the name of the second character
  • kx – kerning value in x-direction
/** * Adds a X-kerning entry. * @param name1 the name of the first character * @param name2 the name of the second character * @param kx kerning value in x-direction */
public void addXKerning(String name1, String name2, double kx) { if (this.kerningMap == null) { this.kerningMap = new java.util.HashMap<String, Map<String, Dimension2D>>(); } Map<String, Dimension2D> entries = this.kerningMap.get(name1); if (entries == null) { entries = new java.util.HashMap<String, Dimension2D>(); this.kerningMap.put(name1, entries); } entries.put(name2, new Dimension2DDouble(kx, 0)); }
Indicates whether the font has kerning information.
Returns:true if there is kerning information
/** * Indicates whether the font has kerning information. * @return true if there is kerning information */
public boolean hasKerning() { return this.kerningMap != null; }
Creates and returns a kerning map for writing mode 0 (ltr) with character codes.
Returns:the kerning map or null if there is no kerning information.
/** * Creates and returns a kerning map for writing mode 0 (ltr) with character codes. * @return the kerning map or null if there is no kerning information. */
public Map<Integer, Map<Integer, Integer>> createXKerningMapEncoded() { if (!hasKerning()) { return null; } Map<Integer, Map<Integer, Integer>> m = new java.util.HashMap<Integer, Map<Integer, Integer>>(); for (Map.Entry<String, Map<String, Dimension2D>> entryFrom : this.kerningMap.entrySet()) { String name1 = entryFrom.getKey(); AFMCharMetrics chm1 = getChar(name1); if (chm1 == null || !chm1.hasCharCode()) { continue; } Map<Integer, Integer> container = null; Map<String, Dimension2D> entriesTo = entryFrom.getValue(); for (Map.Entry<String, Dimension2D> entryTo : entriesTo.entrySet()) { String name2 = entryTo.getKey(); AFMCharMetrics chm2 = getChar(name2); if (chm2 == null || !chm2.hasCharCode()) { continue; } if (container == null) { Integer k1 = chm1.getCharCode(); container = m.get(k1); if (container == null) { container = new java.util.HashMap<Integer, Integer>(); m.put(k1, container); } } Dimension2D dim = entryTo.getValue(); container.put(chm2.getCharCode(), (int) Math.round(dim.getWidth())); } } return m; }
The character codes in an AFM cannot always be trusted to be the same values as in the font's primary encoding. Therefore, we provide a way to override this primary encoding.
Params:
  • encoding – the encoding to replace the one given in the AFM
/** * The character codes in an AFM cannot always be trusted to be the same values as in the * font's primary encoding. Therefore, we provide a way to override this primary encoding. * @param encoding the encoding to replace the one given in the AFM */
public void overridePrimaryEncoding(SingleByteEncoding encoding) { if (LOG.isDebugEnabled()) { LOG.debug("Overriding primary encoding of " + getFontName() + " with: " + encoding); } AFMCharMetrics[] mapped = new AFMCharMetrics[256]; for (AFMCharMetrics cm : this.charMetrics) { NamedCharacter nc = cm.getCharacter(); if (nc.hasSingleUnicodeValue()) { int codePoint = encoding.mapChar(nc.getSingleUnicodeValue()); if (codePoint > 0) { if (mapped[codePoint] != null) { if (LOG.isDebugEnabled()) { AFMCharMetrics other = mapped[codePoint]; String msg = "Not mapping character " + nc + " to code point " + codePoint + " (" + Integer.toHexString(codePoint) + ") in " + encoding + ". " + other + " has already been assigned that code point."; if (other.getUnicodeSequence() .equals(nc.getUnicodeSequence())) { msg += " This is a specialized glyph for the" + " same Unicode character."; //TODO should these be mapped to a private Unicode area to make //them accessible? } else { msg += " This is a similar character."; } if (cm.getWidthX() != other.getWidthX()) { msg += " They have differing widths: " + cm.getWidthX() + " vs. " + other.getWidthX(); } LOG.debug(msg); } } else { cm.setCharCode(codePoint); mapped[codePoint] = cm; } } else { cm.setCharCode(-1); } } else { //No Unicode equivalent cm.setCharCode(-1); } } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public String toString() { return "AFM: " + getFullName(); } }