1 31 package org.pdfbox.pdmodel.font; 32 33 import org.fontbox.ttf.CMAPEncodingEntry; 34 import org.fontbox.ttf.CMAPTable; 35 import org.fontbox.ttf.GlyphData; 36 import org.fontbox.ttf.GlyphTable; 37 import org.fontbox.ttf.HeaderTable; 38 import org.fontbox.ttf.HorizontalHeaderTable; 39 import org.fontbox.ttf.HorizontalMetricsTable; 40 import org.fontbox.ttf.NamingTable; 41 import org.fontbox.ttf.NameRecord; 42 import org.fontbox.ttf.OS2WindowsMetricsTable; 43 import org.fontbox.ttf.PostScriptTable; 44 import org.fontbox.ttf.TTFParser; 45 import org.fontbox.ttf.TrueTypeFont; 46 47 import org.pdfbox.cos.COSDictionary; 48 import org.pdfbox.cos.COSName; 49 50 import org.pdfbox.pdmodel.PDDocument; 51 52 import org.pdfbox.pdmodel.common.PDRectangle; 53 import org.pdfbox.pdmodel.common.PDStream; 54 55 import org.pdfbox.encoding.WinAnsiEncoding; 56 import org.pdfbox.exceptions.WrappedIOException; 57 58 import org.pdfbox.util.ResourceLoader; 59 60 import java.awt.Font ; 61 import java.awt.FontFormatException ; 62 import java.awt.Graphics ; 63 import java.awt.Graphics2D ; 64 import java.awt.RenderingHints ; 65 import java.awt.geom.AffineTransform ; 66 67 import java.util.ArrayList ; 68 import java.util.HashMap ; 69 import java.util.List ; 70 import java.util.Map ; 71 import java.util.Properties ; 72 73 import java.io.File ; 74 import java.io.FileInputStream ; 75 import java.io.IOException ; 76 import java.io.InputStream ; 77 78 84 public class PDTrueTypeFont extends PDSimpleFont 85 { 86 90 public static final String UNKNOWN_FONT = "UNKNOWN_FONT"; 91 92 private Font awtFont = null; 93 94 private static Properties externalFonts = new Properties (); 95 private static Map loadedExternalFonts = new HashMap (); 96 97 static 98 { 99 try 100 { 101 ResourceLoader.loadProperties( "Resources/PDFBox_External_Fonts.properties", externalFonts ); 102 } 103 catch( IOException io ) 104 { 105 io.printStackTrace(); 106 throw new RuntimeException ( "Error loading font resources" ); 107 } 108 } 109 110 111 114 public PDTrueTypeFont() 115 { 116 super(); 117 font.setItem( COSName.SUBTYPE, COSName.TRUE_TYPE ); 118 } 119 120 125 public PDTrueTypeFont( COSDictionary fontDictionary ) 126 { 127 super( fontDictionary ); 128 } 129 130 138 public static PDTrueTypeFont loadTTF( PDDocument doc, String file ) throws IOException 139 { 140 return loadTTF( doc, new File ( file ) ); 141 } 142 143 151 public static PDTrueTypeFont loadTTF( PDDocument doc, File file ) throws IOException 152 { 153 PDTrueTypeFont retval = new PDTrueTypeFont(); 154 PDFontDescriptorDictionary fd = new PDFontDescriptorDictionary(); 155 PDStream fontStream = new PDStream(doc, new FileInputStream ( file ), false ); 156 fontStream.getStream().setInt( COSName.LENGTH1, (int)file.length() ); 157 fontStream.addCompression(); 158 fd.setFontFile2( fontStream ); 159 retval.setFontDescriptor( fd ); 160 retval.setEncoding( new WinAnsiEncoding() ); 163 TrueTypeFont ttf = null; 164 try 165 { 166 TTFParser parser = new TTFParser(); 167 ttf = parser.parseTTF( file ); 168 NamingTable naming = ttf.getNaming(); 169 List records = naming.getNameRecords(); 170 for( int i=0; i<records.size(); i++ ) 171 { 172 NameRecord nr = (NameRecord)records.get( i ); 173 if( nr.getNameId() == NameRecord.NAME_POSTSCRIPT_NAME ) 174 { 175 retval.setBaseFont( nr.getString() ); 176 fd.setFontName( nr.getString() ); 177 } 178 else if( nr.getNameId() == NameRecord.NAME_FONT_FAMILY_NAME ) 179 { 180 fd.setFontFamily( nr.getString() ); 181 } 182 } 183 184 OS2WindowsMetricsTable os2 = ttf.getOS2Windows(); 185 fd.setNonSymbolic( true ); 186 switch( os2.getFamilyClass() ) 187 { 188 case OS2WindowsMetricsTable.FAMILY_CLASS_SYMBOLIC: 189 fd.setSymbolic( true ); 190 fd.setNonSymbolic( false ); 191 break; 192 case OS2WindowsMetricsTable.FAMILY_CLASS_SCRIPTS: 193 fd.setScript( true ); 194 break; 195 case OS2WindowsMetricsTable.FAMILY_CLASS_CLAREDON_SERIFS: 196 case OS2WindowsMetricsTable.FAMILY_CLASS_FREEFORM_SERIFS: 197 case OS2WindowsMetricsTable.FAMILY_CLASS_MODERN_SERIFS: 198 case OS2WindowsMetricsTable.FAMILY_CLASS_OLDSTYLE_SERIFS: 199 case OS2WindowsMetricsTable.FAMILY_CLASS_SLAB_SERIFS: 200 fd.setSerif( true ); 201 break; 202 default: 203 } 205 switch( os2.getWidthClass() ) 206 { 207 case OS2WindowsMetricsTable.WIDTH_CLASS_ULTRA_CONDENSED: 208 fd.setFontStretch( "UltraCondensed" ); 209 break; 210 case OS2WindowsMetricsTable.WIDTH_CLASS_EXTRA_CONDENSED: 211 fd.setFontStretch( "ExtraCondensed" ); 212 break; 213 case OS2WindowsMetricsTable.WIDTH_CLASS_CONDENSED: 214 fd.setFontStretch( "Condensed" ); 215 break; 216 case OS2WindowsMetricsTable.WIDTH_CLASS_SEMI_CONDENSED: 217 fd.setFontStretch( "SemiCondensed" ); 218 break; 219 case OS2WindowsMetricsTable.WIDTH_CLASS_MEDIUM: 220 fd.setFontStretch( "Normal" ); 221 break; 222 case OS2WindowsMetricsTable.WIDTH_CLASS_SEMI_EXPANDED: 223 fd.setFontStretch( "SemiExpanded" ); 224 break; 225 case OS2WindowsMetricsTable.WIDTH_CLASS_EXPANDED: 226 fd.setFontStretch( "Expanded" ); 227 break; 228 case OS2WindowsMetricsTable.WIDTH_CLASS_EXTRA_EXPANDED: 229 fd.setFontStretch( "ExtraExpanded" ); 230 break; 231 case OS2WindowsMetricsTable.WIDTH_CLASS_ULTRA_EXPANDED: 232 fd.setFontStretch( "UltraExpanded" ); 233 break; 234 default: 235 } 237 fd.setFontWeight( os2.getWeightClass() ); 238 239 246 HeaderTable header = ttf.getHeader(); 247 PDRectangle rect = new PDRectangle(); 248 rect.setLowerLeftX( header.getXMin() * 1000f/header.getUnitsPerEm() ); 249 rect.setLowerLeftY( header.getYMin() * 1000f/header.getUnitsPerEm() ); 250 rect.setUpperRightX( header.getXMax() * 1000f/header.getUnitsPerEm() ); 251 rect.setUpperRightY( header.getYMax() * 1000f/header.getUnitsPerEm() ); 252 fd.setFontBoundingBox( rect ); 253 254 HorizontalHeaderTable hHeader = ttf.getHorizontalHeader(); 255 fd.setAscent( hHeader.getAscender() * 1000f/header.getUnitsPerEm() ); 256 fd.setDescent( hHeader.getDescender() * 1000f/header.getUnitsPerEm() ); 257 258 GlyphTable glyphTable = ttf.getGlyph(); 259 GlyphData[] glyphs = glyphTable.getGlyphs(); 260 261 PostScriptTable ps = ttf.getPostScript(); 262 fd.setFixedPitch( ps.getIsFixedPitch() > 0 ); 263 fd.setItalicAngle( ps.getItalicAngle() ); 264 265 String [] names = ps.getGlyphNames(); 266 if( names != null ) 267 { 268 for( int i=0; i<names.length; i++ ) 269 { 270 if( names[i].equals( "H" ) ) 273 { 274 fd.setCapHeight( (glyphs[i].getBoundingBox().getUpperRightY()* 1000f)/ 275 header.getUnitsPerEm() ); 276 } 277 if( names[i].equals( "x" ) ) 278 { 279 fd.setXHeight( (glyphs[i].getBoundingBox().getUpperRightY()* 1000f)/header.getUnitsPerEm() ); 280 } 281 } 282 } 283 284 fd.setStemV( (fd.getFontBoundingBox().getWidth() * .13f) ); 287 288 289 CMAPTable cmapTable = ttf.getCMAP(); 290 CMAPEncodingEntry[] cmaps = cmapTable.getCmaps(); 291 int[] glyphToCCode = null; 292 for( int i=0; i<cmaps.length; i++ ) 293 { 294 if( cmaps[i].getPlatformId() == CMAPTable.PLATFORM_WINDOWS && 295 cmaps[i].getPlatformEncodingId() == CMAPTable.ENCODING_UNICODE ) 296 { 297 glyphToCCode = cmaps[i].getGlyphIdToCharacterCode(); 298 } 299 } 300 int firstChar = 0; 301 309 310 int maxWidths=256; 311 HorizontalMetricsTable hMet = ttf.getHorizontalMetrics(); 312 int[] widthValues = hMet.getAdvanceWidth(); 313 List widths = new ArrayList ( widthValues.length ); 314 Integer zero = new Integer ( 250 ); 315 for( int i=0; i<widthValues.length && i<maxWidths; i++ ) 316 { 317 widths.add( zero ); 318 } 319 for( int i=0; i<widthValues.length; i++ ) 320 { 321 if(glyphToCCode[i]-firstChar < widths.size() && 322 glyphToCCode[i]-firstChar >= 0 && 323 widths.get( glyphToCCode[i]-firstChar) == zero ) 324 { 325 widths.set( glyphToCCode[i]-firstChar, 326 new Integer ( (int)(widthValues[i]* 1000f)/header.getUnitsPerEm() ) ); 327 } 328 } 329 retval.setWidths( widths ); 330 331 retval.setFirstChar( firstChar ); 332 retval.setLastChar( firstChar + widths.size()-1 ); 333 334 } 335 finally 336 { 337 if( ttf != null ) 338 { 339 ttf.close(); 340 } 341 } 342 343 return retval; 344 } 345 346 349 public void drawString( String string, Graphics g, float fontSize, 350 float xScale, float yScale, float x, float y ) throws IOException 351 { 352 PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary)getFontDescriptor(); 353 if( awtFont == null ) 354 { 355 try 356 { 357 PDStream ff2Stream = fd.getFontFile2(); 358 String fontName = fd.getFontName(); 359 if( ff2Stream != null ) 360 { 361 awtFont = Font.createFont( Font.TRUETYPE_FONT, ff2Stream.createInputStream() ); 362 } 363 else 364 { 365 TrueTypeFont ttf = getExternalFontFile2( fd ); 370 if( ttf != null ) 371 { 372 awtFont = Font.createFont( Font.TRUETYPE_FONT, ttf.getOriginalData() ); 373 } 374 else 375 { 376 awtFont = Font.getFont( fontName, null ); 377 } 378 } 379 } 380 catch( FontFormatException f ) 381 { 382 throw new WrappedIOException( f ); 383 } 384 } 385 AffineTransform at = new AffineTransform (); 386 at.scale( xScale, yScale ); 387 Graphics2D g2d = (Graphics2D )g; 388 g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); 389 g2d.setFont( awtFont.deriveFont( at ).deriveFont( fontSize ) ); 390 g2d.drawString( string, (int)x, (int)y ); 391 } 392 393 405 private TrueTypeFont getExternalFontFile2(PDFontDescriptorDictionary fd) 406 throws IOException 407 { 408 TrueTypeFont retval = null; 409 410 if ( fd != null ) 411 { 412 String baseFont = getBaseFont(); 413 String fontResource = externalFonts.getProperty( UNKNOWN_FONT ); 414 if( (baseFont != null) && 415 (externalFonts.containsKey(baseFont)) ) 416 { 417 fontResource = externalFonts.getProperty(baseFont); 418 } 419 if( fontResource != null ) 420 { 421 retval = (TrueTypeFont)loadedExternalFonts.get( baseFont ); 422 if( retval == null ) 423 { 424 TTFParser ttfParser = new TTFParser(); 425 InputStream fontStream = ResourceLoader.loadResource( fontResource ); 426 if( fontStream == null ) 427 { 428 throw new IOException( "Error missing font resource '" + externalFonts.get(baseFont) + "'" ); 429 } 430 retval = ttfParser.parseTTF( fontStream ); 431 loadedExternalFonts.put( baseFont, retval ); 432 } 433 } 434 } 435 436 return retval; 437 } 438 } | Popular Tags |