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 package org.apache.pdfbox.pdmodel.font; 18 19 import java.io.IOException; 20 import java.util.Enumeration; 21 import java.util.HashMap; 22 import java.util.Properties; 23 24 import org.apache.pdfbox.util.ResourceLoader; 25 26 /** 27 * This class is used as font manager. 28 * @author <a href="mailto:andreas@lehmi.de">Andreas Lehmkühler</a> 29 * @version $Revision: 1.0 $ 30 */ 31 32 public class FontManager 33 { 34 35 // HashMap with all known fonts 36 private static HashMap envFonts = new HashMap(); 37 // the standard font 38 private static String standardFont = null; 39 private static Properties fontMapping = new Properties(); 40 41 static { 42 try 43 { 44 ResourceLoader.loadProperties( "Resources/FontMapping.properties", fontMapping ); 45 } 46 catch( IOException io ) 47 { 48 io.printStackTrace(); 49 throw new RuntimeException( "Error loading font mapping" ); 50 } 51 loadFonts(); 52 loadFontMapping(); 53 loadBasefontMapping(); 54 setStandardFont(); 55 } 56 57 private FontManager() 58 { 59 } 60 /** 61 * Get the standard font from the environment, usually Arial or Times New Roman. 62 * 63 * @return The standard font 64 * 65 */ 66 public static java.awt.Font getStandardFont() 67 { 68 if (standardFont != null) 69 { 70 return getAwtFont(standardFont); 71 } 72 return null; 73 } 74 75 /** 76 * Get the font for the given fontname. 77 * 78 * @param font The name of the font. 79 * 80 * @return The font we are looking for or a similar font or null if nothing is found. 81 * 82 */ 83 public static java.awt.Font getAwtFont(String font) 84 { 85 String fontname = normalizeFontname(font); 86 if (envFonts.containsKey(fontname)) 87 { 88 return (java.awt.Font)envFonts.get(fontname); 89 } 90 return null; 91 } 92 93 /** 94 * Load all available fonts from the environment. 95 */ 96 private static void loadFonts() 97 { 98 java.awt.Font[] allFonts = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); 99 int numberOfFonts = allFonts.length; 100 for (int i=0;i<numberOfFonts;i++) 101 { 102 java.awt.Font font = allFonts[i]; 103 String family = normalizeFontname(font.getFamily()); 104 String psname = normalizeFontname(font.getPSName()); 105 if (isBoldItalic(font)) 106 { 107 envFonts.put(family+"bolditalic", font); 108 } 109 else if (isBold(font)) 110 { 111 envFonts.put(family+"bold", font); 112 } 113 else if (isItalic(font)) 114 { 115 envFonts.put(family+"italic", font); 116 } 117 else 118 { 119 envFonts.put(family, font); 120 } 121 if (!family.equals(psname)) 122 { 123 envFonts.put(normalizeFontname(font.getPSName()),font); 124 } 125 } 126 } 127 128 private static void setStandardFont() 129 { 130 // One of the following fonts will be the standard-font 131 if (envFonts.containsKey("arial")) 132 { 133 standardFont = "arial"; 134 } 135 else if (envFonts.containsKey("timesnewroman")) 136 { 137 standardFont = "timesnewroman"; 138 } 139 } 140 141 /** 142 * Normalize the fontname. 143 * 144 * @param fontname The name of the font. 145 * 146 * @return The normalized name of the font. 147 * 148 */ 149 private static String normalizeFontname(String fontname) 150 { 151 // Terminate all whitespaces, commas and hyphens 152 String normalizedFontname = fontname.toLowerCase().replaceAll(" ","").replaceAll(",","").replaceAll("-",""); 153 // Terminate trailing characters up to the "+". 154 // As far as I know, these characters are used in names of embedded fonts 155 // If the embedded font can't be read, we'll try to find it here 156 if (normalizedFontname.indexOf("+") > -1) 157 { 158 normalizedFontname = normalizedFontname.substring(normalizedFontname.indexOf("+")+1); 159 } 160 // normalize all kinds of fonttypes. There are several possible version which have to be normalized 161 // e.g. Arial,Bold Arial-BoldMT Helevtica-oblique ... 162 boolean isBold = normalizedFontname.indexOf("bold") > -1; 163 boolean isItalic = normalizedFontname.indexOf("italic") > -1 || normalizedFontname.indexOf("oblique") > -1; 164 normalizedFontname = normalizedFontname.toLowerCase().replaceAll("bold" , "") 165 .replaceAll("italic" , "").replaceAll("oblique" , ""); 166 if (isBold) 167 { 168 normalizedFontname += "bold"; 169 } 170 if (isItalic) 171 { 172 normalizedFontname += "italic"; 173 } 174 return normalizedFontname; 175 } 176 177 178 /** 179 * Add a font-mapping. 180 * 181 * @param font The name of the font. 182 * 183 * @param mappedName The name of the mapped font. 184 * 185 */ 186 private static boolean addFontMapping(String font, String mappedName) 187 { 188 String fontname = normalizeFontname(font); 189 // is there already a font mapping ? 190 if (envFonts.containsKey(fontname)) 191 { 192 return false; 193 } 194 String mappedFontname = normalizeFontname(mappedName); 195 // is the mapped font available ? 196 if (!envFonts.containsKey(mappedFontname)) 197 { 198 return false; 199 } 200 envFonts.put(fontname, envFonts.get(mappedFontname)); 201 return true; 202 } 203 204 /** 205 * Load the mapping for the well knwon font-substitutions. 206 * 207 */ 208 private static void loadFontMapping() 209 { 210 boolean addedMapping = true; 211 // There could be some recursive mappings in the fontmapping, so that we have to 212 // read the list until no more additional mapping is added to it 213 while (addedMapping) 214 { 215 int counter = 0; 216 Enumeration keys = fontMapping.keys(); 217 while (keys.hasMoreElements()) 218 { 219 String key = (String)keys.nextElement(); 220 if (addFontMapping(key,(String)fontMapping.get(key))) 221 { 222 counter++; 223 } 224 } 225 if (counter == 0) 226 { 227 addedMapping = false; 228 } 229 } 230 } 231 232 /** 233 * Mapping for the basefonts. 234 */ 235 private static void loadBasefontMapping() 236 { 237 addFontMapping("Times-Roman","TimesNewRoman"); 238 addFontMapping("Times-Bold","TimesNewRoman,Bold"); 239 addFontMapping("Times-Italic","TimesNewRoman,Italic"); 240 addFontMapping("Times-BoldItalic","TimesNewRoman,Bold,Italic"); 241 addFontMapping("Helvetica-Oblique","Helvetica,Italic"); 242 addFontMapping("Helvetica-BoldOblique","Helvetica,Bold,Italic"); 243 addFontMapping("Courier-Oblique","Courier,Italic"); 244 addFontMapping("Courier-BoldOblique","Courier,Bold,Italic"); 245 } 246 247 /** 248 * Try to determine if the font has both a BOLD and an ITALIC-type. 249 * 250 * @param name The font. 251 * 252 * @return font has BOLD and ITALIC-type or not 253 */ 254 private static boolean isBoldItalic(java.awt.Font font) 255 { 256 return isBold(font) && isItalic(font); 257 } 258 259 /** 260 * Try to determine if the font has a BOLD-type. 261 * 262 * @param name The font. 263 * 264 * @return font has BOLD-type or not 265 */ 266 private static boolean isBold(java.awt.Font font) 267 { 268 String name = font.getName().toLowerCase(); 269 if (name.indexOf("bold") > -1) 270 { 271 return true; 272 } 273 String psname = font.getPSName().toLowerCase(); 274 if (psname.indexOf("bold") > -1) 275 { 276 return true; 277 } 278 return false; 279 } 280 281 /** 282 * Try to determine if the font has an ITALIC-type. 283 * 284 * @param name The font. 285 * 286 * @return font has ITALIC-type or not 287 */ 288 private static boolean isItalic(java.awt.Font font) 289 { 290 String name = font.getName().toLowerCase(); 291 // oblique is the same as italic 292 if (name.indexOf("italic") > -1 || name.indexOf("oblique") > -1) 293 { 294 return true; 295 } 296 String psname = font.getPSName().toLowerCase(); 297 if (psname.indexOf("italic") > -1 || psname.indexOf("oblique") > -1) 298 { 299 return true; 300 } 301 return false; 302 } 303 }