/* * 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.afm; import org.pentaho.reporting.libraries.fonts.io.FileFontDataInputSource; import org.pentaho.reporting.libraries.fonts.io.FontDataInputSource; import java.io.File; import java.io.IOException; import java.io.Serializable; /** * An AFM font is a simple text file. * * @author Thomas Morgner */ public class AfmFont implements Serializable { private static final int PRE_HEADER = 0; private static final int IN_HEADER = 1; private static final int IN_METRICS = 2; private static final int IN_KERNDATA = 3; private static final int IN_COMPOSITES = 4; private static final int END_OF_FILE = 5; private static final int IN_DIRECTION = 6; private AfmHeader header; private AfmDirectionSection[] directionSections; private AfmCharMetricsSection charMetricsSection; private AfmKernDataSection kernDataSection; private AfmCompositeCharDataSection compositeCharDataSection; private boolean embeddable; private String fontName; private String familyName; private String filename; private FontDataInputSource input; public AfmFont( final File font, final boolean embeddable ) throws IOException { final FontDataInputSource fis = new FileFontDataInputSource( font ); initialize( fis, embeddable ); fis.dispose(); } public AfmFont( final FontDataInputSource inputSource, final boolean embeddable ) throws IOException { initialize( inputSource, embeddable ); } private void initialize( final FontDataInputSource inputSource, final boolean embeddable ) throws IOException { if ( inputSource == null ) { throw new NullPointerException(); } this.filename = inputSource.getFileName(); this.input = inputSource; this.embeddable = embeddable; header = new AfmHeader(); directionSections = new AfmDirectionSection[ 2 ]; directionSections[ 0 ] = new AfmDirectionSection(); directionSections[ 1 ] = new AfmDirectionSection(); charMetricsSection = new AfmCharMetricsSection(); kernDataSection = new AfmKernDataSection(); parseFontFile( inputSource ); fontName = header.getFontName(); if ( fontName == null ) { throw new IOException( "This font does not define a font-name, therefore it is invalid." ); } familyName = header.getFamilyName(); if ( familyName == null ) { familyName = fontName; } } private void parseFontFile( final FontDataInputSource inputSource ) throws IOException { int parseState = PRE_HEADER; int sectionType = 0; final FontDataAsciiReader reader = new FontDataAsciiReader( inputSource ); String line; while ( ( line = reader.readLine() ) != null ) { if ( line.length() == 0 ) { continue; } switch( parseState ) { case PRE_HEADER: { if ( line.startsWith( "StartFontMetrics" ) == false ) { throw new IOException( "Expected 'StartMetrics' as initial command line." ); } parseState = IN_HEADER; break; } case IN_HEADER: { if ( line.startsWith( "EndFontMetrics" ) ) { parseState = END_OF_FILE; } else if ( line.startsWith( "StartDirection" ) ) { parseState = IN_DIRECTION; sectionType = AfmParseUtilities.parseInt( "StartDirection ", line ); } else if ( line.startsWith( "StartCharMetrics" ) ) { parseState = IN_METRICS; sectionType = AfmParseUtilities.parseInt( "StartCharMetrics ", line ); } else if ( line.startsWith( "StartKernData" ) ) { parseState = IN_KERNDATA; } else if ( line.startsWith( "StartComposites" ) ) { parseState = IN_COMPOSITES; compositeCharDataSection = new AfmCompositeCharDataSection(); } else { header.addData( line ); directionSections[ 0 ].add( line ); } break; } case IN_METRICS: { if ( line.startsWith( "EndCharMetrics" ) ) { parseState = IN_HEADER; sectionType = 0; } else { charMetricsSection.add( line ); } break; } case IN_KERNDATA: { if ( line.startsWith( "EndKernData" ) ) { parseState = IN_HEADER; sectionType = 0; } else { kernDataSection.add( line ); } break; } case IN_COMPOSITES: { if ( line.startsWith( "EndComposites" ) ) { parseState = IN_HEADER; sectionType = 0; } else { compositeCharDataSection.add( line ); } break; } case END_OF_FILE: { // Extra lines after the 'EndFontMetrics' line are ignored. break; } case IN_DIRECTION: { if ( line.startsWith( "EndDirection" ) ) { parseState = IN_HEADER; sectionType = 0; } else { switch( sectionType ) { case 0: { directionSections[ 0 ].add( line ); break; } case 1: { directionSections[ 1 ].add( line ); break; } case 2: { directionSections[ 0 ].add( line ); directionSections[ 1 ].add( line ); break; } default: { throw new IllegalStateException( "The Type " + sectionType + " for the Direction-section was invalid." ); } } } break; } default: { throw new IllegalStateException( "In Parse State " + parseState + ": Encountered line " + line ); } } } } public int getMetricsSets() { return header.getMetricsSets(); } public AfmDirectionSection getDirectionSection( final int index ) { return directionSections[ index ]; } public FontDataInputSource getInput() { return input; } public AfmHeader getHeader() { return header; } public String getFilename() { return filename; } public String getFamilyName() { return familyName; } public String getFontName() { return fontName; } public boolean isEmbeddable() { return embeddable; } public void dispose() { input.dispose(); } }