/*
 * 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: CustomFont.java 1805173 2017-08-16 10:50:04Z ssteiner $ */

package org.apache.fop.fonts;

import java.awt.Rectangle;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.fop.apps.io.InternalResourceResolver;


Abstract base class for custom fonts loaded from files, for example.
/** * Abstract base class for custom fonts loaded from files, for example. */
public abstract class CustomFont extends Typeface implements FontDescriptor, MutableFont {
Fallback thickness for underline and strikeout when not provided by the font.
/** Fallback thickness for underline and strikeout when not provided by the font. */
private static final int DEFAULT_LINE_THICKNESS = 50; private URI fontFileURI; private String fontName; private String fullName; private Set<String> familyNames; private String fontSubName; private URI embedFileURI; private String embedResourceName; private final InternalResourceResolver resourceResolver; private EmbeddingMode embeddingMode = EmbeddingMode.AUTO; private int capHeight; private int xHeight; private int ascender; private int descender; private int[] fontBBox = {0, 0, 0, 0}; private int flags = 4; private int weight; //0 means unknown weight private int stemV; private int italicAngle; private int missingWidth; private FontType fontType = FontType.TYPE1; private int firstChar; private int lastChar = 255; private int underlinePosition; private int underlineThickness; private int strikeoutPosition; private int strikeoutThickness; private Map<Integer, Map<Integer, Integer>> kerning; private boolean useKerning = true;
the character map, mapping Unicode ranges to glyph indices.
/** the character map, mapping Unicode ranges to glyph indices. */
protected List<CMapSegment> cmap = new ArrayList<CMapSegment>(); private boolean useAdvanced = true; private boolean simulateStyle; protected List<SimpleSingleByteEncoding> additionalEncodings; protected Map<Character, SingleByteFont.UnencodedCharacter> unencodedCharacters;
Params:
  • resourceResolver – the URI resource resolver for controlling file access
/** * @param resourceResolver the URI resource resolver for controlling file access */
public CustomFont(InternalResourceResolver resourceResolver) { this.resourceResolver = resourceResolver; }
{@inheritDoc}
/** {@inheritDoc} */
public URI getFontURI() { return fontFileURI; }
{@inheritDoc}
/** {@inheritDoc} */
public String getFontName() { return fontName; }
{@inheritDoc}
/** {@inheritDoc} */
public String getEmbedFontName() { return getFontName(); }
{@inheritDoc}
/** {@inheritDoc} */
public String getFullName() { return fullName; }
Returns the font family names.
Returns:the font family names (a Set of Strings)
/** * Returns the font family names. * @return the font family names (a Set of Strings) */
public Set<String> getFamilyNames() { return Collections.unmodifiableSet(this.familyNames); }
Returns the font family name stripped of whitespace.
See Also:
Returns:the stripped font family
/** * Returns the font family name stripped of whitespace. * @return the stripped font family * @see FontUtil#stripWhiteSpace(String) */
public String getStrippedFontName() { return FontUtil.stripWhiteSpace(getFontName()); }
Returns font's subfamily name.
Returns:the font's subfamily name
/** * Returns font's subfamily name. * @return the font's subfamily name */
public String getFontSubName() { return fontSubName; }
Returns an URI representing an embeddable font file.
Returns:URI to an embeddable font file or null if not available.
/** * Returns an URI representing an embeddable font file. * * @return URI to an embeddable font file or null if not available. */
public URI getEmbedFileURI() { return embedFileURI; }
Returns the embedding mode for this font.
Returns:embedding mode
/** * Returns the embedding mode for this font. * @return embedding mode */
public EmbeddingMode getEmbeddingMode() { return embeddingMode; }
Returns an InputStream representing an embeddable font file.
Throws:
  • IOException – if embedFileName is not null but Source is not found
Returns:InputStream for an embeddable font file
/** * Returns an {@link InputStream} representing an embeddable font file. * * @return {@link InputStream} for an embeddable font file * @throws IOException if embedFileName is not null but Source is not found */
public InputStream getInputStream() throws IOException { return resourceResolver.getResource(embedFileURI); }
Returns the lookup name to an embeddable font file available as a resource. (todo) Remove this method, this should be done using a resource: URI.
Returns:the lookup name
/** * Returns the lookup name to an embeddable font file available as a * resource. * (todo) Remove this method, this should be done using a resource: URI. * @return the lookup name */
public String getEmbedResourceName() { return embedResourceName; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getAscender() { return ascender; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getDescender() { return descender; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getCapHeight() { return capHeight; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getAscender(int size) { return size * ascender; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getDescender(int size) { return size * descender; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getCapHeight(int size) { return size * capHeight; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getXHeight(int size) { return size * xHeight; }
{@inheritDoc}
/** * {@inheritDoc} */
public int[] getFontBBox() { return fontBBox; }
{@inheritDoc}
/** {@inheritDoc} */
public int getFlags() { return flags; }
{@inheritDoc}
/** {@inheritDoc} */
public boolean isSymbolicFont() { return ((getFlags() & 4) != 0) || "ZapfDingbatsEncoding".equals(getEncodingName()); //Note: The check for ZapfDingbats is necessary as the PFM does not reliably indicate //if a font is symbolic. }
Returns the font weight (100, 200...800, 900). This value may be different from the one that was actually used to register the font.
Returns:the font weight (or 0 if the font weight is unknown)
/** * Returns the font weight (100, 200...800, 900). This value may be different from the * one that was actually used to register the font. * @return the font weight (or 0 if the font weight is unknown) */
public int getWeight() { return this.weight; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getStemV() { return stemV; }
{@inheritDoc}
/** * {@inheritDoc} */
public int getItalicAngle() { return italicAngle; }
Returns the width to be used when no width is available.
Returns:a character width
/** * Returns the width to be used when no width is available. * @return a character width */
public int getMissingWidth() { return missingWidth; }
{@inheritDoc}
/** * {@inheritDoc} */
public FontType getFontType() { return fontType; }
Returns the index of the first character defined in this font.
Returns:the index of the first character
/** * Returns the index of the first character defined in this font. * @return the index of the first character */
public int getFirstChar() { return firstChar; }
Returns the index of the last character defined in this font.
Returns:the index of the last character
/** * Returns the index of the last character defined in this font. * @return the index of the last character */
public int getLastChar() { return lastChar; }
Used to determine if kerning is enabled.
Returns:True if kerning is enabled.
/** * Used to determine if kerning is enabled. * @return True if kerning is enabled. */
public boolean isKerningEnabled() { return useKerning; }
{@inheritDoc}
/** * {@inheritDoc} */
public final boolean hasKerningInfo() { return (isKerningEnabled() && (kerning != null) && !kerning.isEmpty()); }
{@inheritDoc}
/** * {@inheritDoc} */
public final Map<Integer, Map<Integer, Integer>> getKerningInfo() { if (hasKerningInfo()) { return kerning; } else { return Collections.emptyMap(); } }
Used to determine if advanced typographic features are enabled. By default, this is false, but may be overridden by subclasses.
Returns:true if enabled.
/** * Used to determine if advanced typographic features are enabled. * By default, this is false, but may be overridden by subclasses. * @return true if enabled. */
public boolean isAdvancedEnabled() { return useAdvanced; } /* ---- MutableFont interface ---- */
{@inheritDoc}
/** {@inheritDoc} */
public void setFontURI(URI uri) { this.fontFileURI = uri; }
{@inheritDoc}
/** {@inheritDoc} */
public void setFontName(String name) { this.fontName = name; }
{@inheritDoc}
/** {@inheritDoc} */
public void setFullName(String name) { this.fullName = name; }
{@inheritDoc}
/** {@inheritDoc} */
public void setFamilyNames(Set<String> names) { this.familyNames = new HashSet<String>(names); }
Sets the font's subfamily name.
Params:
  • subFamilyName – the subfamily name of the font
/** * Sets the font's subfamily name. * @param subFamilyName the subfamily name of the font */
public void setFontSubFamilyName(String subFamilyName) { this.fontSubName = subFamilyName; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setEmbedURI(URI path) { this.embedFileURI = path; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setEmbedResourceName(String name) { this.embedResourceName = name; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setEmbeddingMode(EmbeddingMode embeddingMode) { this.embeddingMode = embeddingMode; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setCapHeight(int capHeight) { this.capHeight = capHeight; }
Returns the XHeight value of the font.
Params:
  • xHeight – the XHeight value
/** * Returns the XHeight value of the font. * @param xHeight the XHeight value */
public void setXHeight(int xHeight) { this.xHeight = xHeight; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setAscender(int ascender) { this.ascender = ascender; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setDescender(int descender) { this.descender = descender; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setFontBBox(int[] bbox) { this.fontBBox = bbox; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setFlags(int flags) { this.flags = flags; }
Sets the font weight. Valid values are 100, 200...800, 900.
Params:
  • weight – the font weight
/** * Sets the font weight. Valid values are 100, 200...800, 900. * @param weight the font weight */
public void setWeight(int weight) { weight = (weight / 100) * 100; weight = Math.max(100, weight); weight = Math.min(900, weight); this.weight = weight; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setStemV(int stemV) { this.stemV = stemV; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setItalicAngle(int italicAngle) { this.italicAngle = italicAngle; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setMissingWidth(int width) { this.missingWidth = width; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setFontType(FontType fontType) { this.fontType = fontType; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setFirstChar(int index) { this.firstChar = index; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setLastChar(int index) { this.lastChar = index; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setKerningEnabled(boolean enabled) { this.useKerning = enabled; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setAdvancedEnabled(boolean enabled) { this.useAdvanced = enabled; }
{@inheritDoc}
/** * {@inheritDoc} */
public void setSimulateStyle(boolean enabled) { this.simulateStyle = enabled; } public boolean getSimulateStyle() { return this.simulateStyle; }
{@inheritDoc}
/** {@inheritDoc} */
public void putKerningEntry(Integer key, Map<Integer, Integer> value) { if (kerning == null) { kerning = new HashMap<Integer, Map<Integer, Integer>>(); } this.kerning.put(key, value); }
Replaces the existing kerning map with a new one.
Params:
  • kerningMap – the kerning map (the integers are character codes)
/** * Replaces the existing kerning map with a new one. * @param kerningMap the kerning map (the integers are * character codes) */
public void replaceKerningMap(Map<Integer, Map<Integer, Integer>> kerningMap) { if (kerningMap == null) { this.kerning = Collections.emptyMap(); } else { this.kerning = kerningMap; } }
Sets the character map for this font. It maps all available Unicode characters to their glyph indices inside the font.
Params:
  • cmap – the character map
/** * Sets the character map for this font. It maps all available Unicode characters * to their glyph indices inside the font. * @param cmap the character map */
public void setCMap(CMapSegment[] cmap) { this.cmap.clear(); Collections.addAll(this.cmap, cmap); }
Returns the character map for this font. It maps all available Unicode characters to their glyph indices inside the font.
Returns:the character map
/** * Returns the character map for this font. It maps all available Unicode characters * to their glyph indices inside the font. * @return the character map */
public CMapSegment[] getCMap() { return cmap.toArray(new CMapSegment[cmap.size()]); } public int getUnderlinePosition(int size) { return (underlinePosition == 0) ? getDescender(size) / 2 : size * underlinePosition; } public void setUnderlinePosition(int underlinePosition) { this.underlinePosition = underlinePosition; } public int getUnderlineThickness(int size) { return size * ((underlineThickness == 0) ? DEFAULT_LINE_THICKNESS : underlineThickness); } public void setUnderlineThickness(int underlineThickness) { this.underlineThickness = underlineThickness; } public int getStrikeoutPosition(int size) { return (strikeoutPosition == 0) ? getXHeight(size) / 2 : size * strikeoutPosition; } public void setStrikeoutPosition(int strikeoutPosition) { this.strikeoutPosition = strikeoutPosition; } public int getStrikeoutThickness(int size) { return (strikeoutThickness == 0) ? getUnderlineThickness(size) : size * strikeoutThickness; } public void setStrikeoutThickness(int strikeoutThickness) { this.strikeoutThickness = strikeoutThickness; }
Returns a Map of used Glyphs.
Returns:Map Map of used Glyphs
/** * Returns a Map of used Glyphs. * @return Map Map of used Glyphs */
public abstract Map<Integer, Integer> getUsedGlyphs();
Returns the character from it's original glyph index in the font
Params:
  • glyphIndex – The original index of the character
Returns:The character
/** * Returns the character from it's original glyph index in the font * @param glyphIndex The original index of the character * @return The character */
public abstract char getUnicodeFromGID(int glyphIndex);
Indicates whether the encoding has additional encodings besides the primary encoding.
Returns:true if there are additional encodings.
/** * Indicates whether the encoding has additional encodings besides the primary encoding. * @return true if there are additional encodings. */
public boolean hasAdditionalEncodings() { return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0); }
Returns the number of additional encodings this single-byte font maintains.
Returns:the number of additional encodings
/** * Returns the number of additional encodings this single-byte font maintains. * @return the number of additional encodings */
public int getAdditionalEncodingCount() { if (hasAdditionalEncodings()) { return this.additionalEncodings.size(); } else { return 0; } }
Returns an additional encoding.
Params:
  • index – the index of the additional encoding
Throws:
Returns:the additional encoding
/** * Returns an additional encoding. * @param index the index of the additional encoding * @return the additional encoding * @throws IndexOutOfBoundsException if the index is out of bounds */
public SimpleSingleByteEncoding getAdditionalEncoding(int index) throws IndexOutOfBoundsException { if (hasAdditionalEncodings()) { return this.additionalEncodings.get(index); } else { throw new IndexOutOfBoundsException("No additional encodings available"); } }
Adds an unencoded character (one that is not supported by the primary encoding).
Params:
  • ch – the named character
  • width – the width of the character
/** * Adds an unencoded character (one that is not supported by the primary encoding). * @param ch the named character * @param width the width of the character */
public void addUnencodedCharacter(NamedCharacter ch, int width, Rectangle bbox) { if (this.unencodedCharacters == null) { this.unencodedCharacters = new HashMap<Character, SingleByteFont.UnencodedCharacter>(); } if (ch.hasSingleUnicodeValue()) { SingleByteFont.UnencodedCharacter uc = new SingleByteFont.UnencodedCharacter(ch, width, bbox); this.unencodedCharacters.put(ch.getSingleUnicodeValue(), uc); } else { //Cannot deal with unicode sequences, so ignore this character } }
Adds a character to additional encodings
Params:
  • ch – character to map
/** * Adds a character to additional encodings * @param ch character to map */
protected char mapUnencodedChar(char ch) { if (this.unencodedCharacters != null) { SingleByteFont.UnencodedCharacter unencoded = this.unencodedCharacters.get(ch); if (unencoded != null) { if (this.additionalEncodings == null) { this.additionalEncodings = new ArrayList<SimpleSingleByteEncoding>(); } SimpleSingleByteEncoding encoding = null; char mappedStart = 0; int additionalsCount = this.additionalEncodings.size(); for (int i = 0; i < additionalsCount; i++) { mappedStart += 256; encoding = getAdditionalEncoding(i); char alt = encoding.mapChar(ch); if (alt != 0) { return (char)(mappedStart + alt); } } if (encoding != null && encoding.isFull()) { encoding = null; } if (encoding == null) { encoding = new SimpleSingleByteEncoding( getFontName() + "EncodingSupp" + (additionalsCount + 1)); this.additionalEncodings.add(encoding); mappedStart += 256; } return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter())); } } return 0; } }