/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2006 - 2013 Pentaho Corporation and Contributors. All rights reserved. */ package org.pentaho.reporting.libraries.fonts.truetype; import org.pentaho.reporting.libraries.fonts.LibFontsDefaults; import org.pentaho.reporting.libraries.fonts.tools.FontStrictGeomUtility; import java.io.IOException; /** * This is the scalable backend for truetype fonts. To make any use of it, you have to apply the font size to these * metrics. * * @author Thomas Morgner */ public class ScalableTrueTypeFontMetrics { private TrueTypeFont font; private long ascent; private long descent; private long leading; private long xHeight; private long strikethroughPosition; private long maxAscent; private long maxDescent; private long underlinePosition; private long italicAngle; private long maxCharAdvance; public ScalableTrueTypeFontMetrics( final TrueTypeFont font ) throws IOException { if ( font == null ) { throw new NullPointerException( "The font must not be null" ); } this.font = font; final FontHeaderTable head = (FontHeaderTable) font.getTable( FontHeaderTable.TABLE_ID ); if ( head == null ) { throw new IllegalStateException( "Font has no HEAD table and is not a usable font." ); } final int unitsPerEm = head.getUnitsPerEm(); final long strictScaleFactor = FontStrictGeomUtility.toInternalValue( 1 ); maxAscent = ( strictScaleFactor * head.getyMax() ) / unitsPerEm; maxDescent = ( strictScaleFactor * -head.getyMin() ) / unitsPerEm; // prefer the mac table, as at least for the old version of Arial // I use, the mac table is consistent with the Java-Font-Metrics final HorizontalHeaderTable hhea = (HorizontalHeaderTable) font.getTable( HorizontalHeaderTable.TABLE_ID ); if ( hhea == null ) { throw new IllegalStateException( "The font has no HHEA table and is not a valid font." ); } // Mac metrics must always be present.. createMacMetrics( hhea, unitsPerEm, strictScaleFactor ); final OS2Table table = (OS2Table) font.getTable( OS2Table.TABLE_ID ); if ( table != null ) { computeWindowsMetrics( table, unitsPerEm, strictScaleFactor ); } final PostscriptInformationTable postTable = (PostscriptInformationTable) font.getTable( PostscriptInformationTable.TABLE_ID ); if ( postTable != null ) { this.italicAngle = FontStrictGeomUtility.toInternalValue( postTable.getItalicAngle() ); this.underlinePosition = getAscent() + ( strictScaleFactor * ( -postTable.getUnderlinePosition() + ( postTable.getUnderlineThickness() / 2 ) ) ) / unitsPerEm; } font.dispose(); } private void createMacMetrics( final HorizontalHeaderTable hhea, final int unitsPerEm, final long scaleFactor ) { this.maxCharAdvance = ( scaleFactor * hhea.getMaxAdvanceWidth() ) / unitsPerEm; this.ascent = ( scaleFactor * hhea.getAscender() ) / unitsPerEm; this.descent = ( scaleFactor * -hhea.getDescender() ) / unitsPerEm; this.leading = ( scaleFactor * hhea.getLineGap() ) / unitsPerEm; this.xHeight = (long) ( ascent * LibFontsDefaults.DEFAULT_XHEIGHT_SIZE / LibFontsDefaults.DEFAULT_ASCENT_SIZE ); this.strikethroughPosition = getMaxAscent() - (long) ( this.xHeight * LibFontsDefaults.DEFAULT_STRIKETHROUGH_POSITION ); this.italicAngle = FontStrictGeomUtility.toInternalValue ( -StrictMath.atan2( hhea.getCaretSlopeRun(), hhea.getCaretSlopeRise() ) * 180 / Math.PI ); } private void computeWindowsMetrics( final OS2Table table, final int unitsPerEm, final long scaleFactor ) { final short xHeightRaw = table.getxHeight(); if ( xHeightRaw != 0 ) { this.xHeight = ( scaleFactor * xHeightRaw ) / unitsPerEm; } final short strikethroughPosition = table.getyStrikeoutPosition(); if ( strikethroughPosition != 0 ) { this.strikethroughPosition = getMaxAscent() - ( scaleFactor * strikethroughPosition / unitsPerEm ); } } /** * From the baseline to the * * @return */ public long getAscent() { return ascent; } public long getDescent() { return descent; } public long getLeading() { return leading; } public long getXHeight() { return xHeight; } public long getUnderlinePosition() { return underlinePosition; } public long getStrikeThroughPosition() { return strikethroughPosition; } public TrueTypeFont getFont() { return font; } public long getItalicAngle() { return italicAngle; } public long getMaxAscent() { return maxAscent; } public long getMaxDescent() { return maxDescent; } public long getMaxCharAdvance() { return maxCharAdvance; } }