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 18 /* $Id: RasterFont.java 1761019 2016-09-16 10:43:45Z ssteiner $ */ 19 20 package org.apache.fop.afp.fonts; 21 22 import java.awt.Rectangle; 23 import java.util.HashMap; 24 import java.util.Map; 25 import java.util.SortedMap; 26 import java.util.TreeMap; 27 28 import org.apache.commons.logging.Log; 29 import org.apache.commons.logging.LogFactory; 30 31 /** 32 * A font where each character is stored as an array of pixels (a bitmap). Such 33 * fonts are not easily scalable, in contrast to vectored fonts. With this type 34 * of font, the font metrics information is held in character set files (one for 35 * each size and style). 36 */ 37 public class RasterFont extends AFPFont { 38 39 /** Static logging instance */ 40 protected static final Log LOG = LogFactory.getLog("org.apache.fop.afp.fonts"); 41 42 private final SortedMap<Integer, CharacterSet> charSets = new TreeMap<Integer, CharacterSet>(); 43 private Map<Integer, CharacterSet> substitutionCharSets; 44 45 private CharacterSet charSet; 46 47 /** 48 * Constructor for the raster font requires the name, weight and style 49 * attribute to be available as this forms the key to the font. 50 * 51 * @param name the name of the font 52 * @param embeddable {@code true} if the font is embeddable 53 */ RasterFont(String name, boolean embeddable)54 public RasterFont(String name, boolean embeddable) { 55 super(name, embeddable); 56 } 57 58 /** 59 * Adds the character set for the given point size 60 * @param size point size (in mpt) 61 * @param characterSet character set 62 */ addCharacterSet(int size, CharacterSet characterSet)63 public void addCharacterSet(int size, CharacterSet characterSet) { 64 //TODO: replace with Integer.valueOf() once we switch to Java 5 65 this.charSets.put(size, characterSet); 66 this.charSet = characterSet; 67 } 68 69 /** 70 * Get the character set metrics for the specified point size. 71 * 72 * @param sizeInMpt the point size (in mpt) 73 * @return the character set metrics 74 */ getCharacterSet(int sizeInMpt)75 public CharacterSet getCharacterSet(int sizeInMpt) { 76 77 Integer requestedSize = sizeInMpt; 78 CharacterSet csm = charSets.get(requestedSize); 79 double sizeInPt = sizeInMpt / 1000.0; 80 81 if (csm != null) { 82 return csm; 83 } 84 85 if (substitutionCharSets != null) { 86 //Check first if a substitution has already been added 87 csm = substitutionCharSets.get(requestedSize); 88 } 89 90 if (csm == null && !charSets.isEmpty()) { 91 // No match or substitution found, but there exist entries 92 // for other sizes 93 // Get char set with nearest, smallest font size 94 SortedMap<Integer, CharacterSet> smallerSizes = charSets.headMap(requestedSize); 95 SortedMap<Integer, CharacterSet> largerSizes = charSets.tailMap(requestedSize); 96 int smallerSize = smallerSizes.isEmpty() ? 0 97 : smallerSizes.lastKey(); 98 int largerSize = largerSizes.isEmpty() ? Integer.MAX_VALUE 99 : largerSizes.firstKey(); 100 101 Integer fontSize; 102 if (!smallerSizes.isEmpty() 103 && (sizeInMpt - smallerSize) <= (largerSize - sizeInMpt)) { 104 fontSize = smallerSize; 105 } else { 106 fontSize = largerSize; 107 } 108 csm = charSets.get(fontSize); 109 110 if (csm != null) { 111 // Add the substitute mapping, so subsequent calls will 112 // find it immediately 113 if (substitutionCharSets == null) { 114 substitutionCharSets = new HashMap<Integer, CharacterSet>(); 115 } 116 substitutionCharSets.put(requestedSize, csm); 117 // do not output the warning if the font size is closer to an integer less than 0.1 118 if (!(Math.abs(fontSize / 1000.0 - sizeInPt) < 0.1)) { 119 String msg = "No " + sizeInPt + "pt font " + getFontName() 120 + " found, substituted with " + fontSize / 1000f + "pt font"; 121 LOG.warn(msg); 122 } 123 } 124 } 125 126 if (csm == null) { 127 // Still no match -> error 128 String msg = "No font found for font " + getFontName() + " with point size " + sizeInPt; 129 LOG.error(msg); 130 throw new FontRuntimeException(msg); 131 } 132 133 return csm; 134 135 } 136 metricsToAbsoluteSize(CharacterSet cs, int value, int givenSize)137 private int metricsToAbsoluteSize(CharacterSet cs, int value, int givenSize) { 138 int nominalVerticalSize = cs.getNominalVerticalSize(); 139 if (nominalVerticalSize != 0) { 140 return value * nominalVerticalSize; 141 } else { 142 return value * givenSize; 143 } 144 } 145 metricsToAbsoluteSize(CharacterSet cs, double value, int givenSize)146 private int metricsToAbsoluteSize(CharacterSet cs, double value, int givenSize) { 147 int nominalVerticalSize = cs.getNominalVerticalSize(); 148 if (nominalVerticalSize != 0) { 149 return (int) (value * nominalVerticalSize); 150 } else { 151 return (int) (value * givenSize); 152 } 153 } 154 155 /** 156 * The ascender is the part of a lowercase letter that extends above the 157 * "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also 158 * used to denote the part of the letter extending above the x-height. 159 * 160 * @param size the font size (in mpt) 161 * @return the ascender for the given point size 162 */ getAscender(int size)163 public int getAscender(int size) { 164 CharacterSet cs = getCharacterSet(size); 165 return metricsToAbsoluteSize(cs, cs.getAscender(), size); 166 } 167 168 /** {@inheritDoc} */ getUnderlinePosition(int size)169 public int getUnderlinePosition(int size) { 170 CharacterSet cs = getCharacterSet(size); 171 return metricsToAbsoluteSize(cs, cs.getUnderscorePosition(), size); 172 } 173 174 @Override getUnderlineThickness(int size)175 public int getUnderlineThickness(int size) { 176 CharacterSet cs = getCharacterSet(size); 177 int underscoreWidth = cs.getUnderscoreWidth(); 178 return underscoreWidth == 0 ? super.getUnderlineThickness(size) 179 : metricsToAbsoluteSize(cs, underscoreWidth, size); 180 } 181 182 /** 183 * Obtains the height of capital letters for the specified point size. 184 * 185 * @param size the font size (in mpt) 186 * @return the cap height for the specified point size 187 */ getCapHeight(int size)188 public int getCapHeight(int size) { 189 CharacterSet cs = getCharacterSet(size); 190 return metricsToAbsoluteSize(cs, cs.getCapHeight(), size); 191 } 192 193 /** 194 * The descender is the part of a lowercase letter that extends below the 195 * base line, such as "g", "j", or "p". Also used to denote the part of the 196 * letter extending below the base line. 197 * 198 * @param size the font size (in mpt) 199 * @return the descender for the specified point size 200 */ getDescender(int size)201 public int getDescender(int size) { 202 CharacterSet cs = getCharacterSet(size); 203 return metricsToAbsoluteSize(cs, cs.getDescender(), size); 204 } 205 206 /** 207 * The "x-height" (the height of the letter "x"). 208 * 209 * @param size the font size (in mpt) 210 * @return the x height for the given point size 211 */ getXHeight(int size)212 public int getXHeight(int size) { 213 CharacterSet cs = getCharacterSet(size); 214 return metricsToAbsoluteSize(cs, cs.getXHeight(), size); 215 } 216 217 /** 218 * Obtain the width of the character for the specified point size. 219 * @param character the character 220 * @param size the font size (in mpt) 221 * @return the width for the given point size 222 */ getWidth(int character, int size)223 public int getWidth(int character, int size) { 224 CharacterSet cs = getCharacterSet(size); 225 return metricsToAbsoluteSize(cs, cs.getWidth(toUnicodeCodepoint(character), 1), size); 226 } 227 228 /** 229 * TODO 230 */ getBoundingBox(int character, int size)231 public Rectangle getBoundingBox(int character, int size) { 232 CharacterSet cs = getCharacterSet(size); 233 Rectangle characterBox = cs.getCharacterBox(toUnicodeCodepoint(character), 1); 234 int x = metricsToAbsoluteSize(cs, characterBox.getX(), size); 235 int y = metricsToAbsoluteSize(cs, characterBox.getY(), size); 236 int w = metricsToAbsoluteSize(cs, characterBox.getWidth(), size); 237 int h = metricsToAbsoluteSize(cs, characterBox.getHeight(), size); 238 return new Rectangle(x, y, w, h); 239 } 240 241 /** {@inheritDoc} */ hasChar(char c)242 public boolean hasChar(char c) { 243 return charSet.hasChar(c); 244 } 245 246 /** 247 * Map a Unicode character to a code point in the font. 248 * @param c character to map 249 * @return the mapped character 250 */ mapChar(char c)251 public char mapChar(char c) { 252 return charSet.mapChar(c); 253 } 254 255 /** {@inheritDoc} */ getEncodingName()256 public String getEncodingName() { 257 return charSet.getEncoding(); 258 } 259 } 260