/*
* 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) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.libraries.pixie.wmf;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
/**
* A buffer which represents a Metafile header.
* <p/>
* The meta file header has the following structure <table border="1"> <tr> <th>offset</th> <th>length in bytes</th>
* <th>name</th> <th>meaning</th> </tr> <tr> <td>0x00</td> <td>2</td> <td>mfType</td> <td>MetaFile type: 0x1 = memory
* based meta file, 0x2 = disk based meta file</td> </tr> <tr> <td>0x02</td> <td>2</td> <td>mfHeader</td> <td>length of
* header in words (16bit)</td> </tr> <tr> <td>0x04</td> <td>2</td> <td>mfVersion</td> <td>Windows version used to save
* the file as BCD number. 0x30 for windows 3.0, 0x31 for win3.1 etc.</td> </tr> <tr> <td>0x06</td> <td>4</td>
* <td>mfSize</td> <td>File length in words</td> </tr> <tr> <td>0x0A</td> <td>2</td> <td>mfNoObj</td> <td>maximum number
* of objects in the file</td> </tr> <tr> <td>0x0c</td> <td>4</td> <td>mfMaxRec</td> <td>Maximum record length</td>
* </tr> <tr> <td>0x10</td> <td>2</td> <td>mfnoPar</td> <td>Not used</td> </tr> </table>
*/
public class MfHeader extends Buffer {
/**
* A constant stating that the given file is not Wmf-File at all.
*/
public static final int QUALITY_NO = 0; // Can't convert.
/**
* A constant stating that the given file could be a Wmf-File.
*/
public static final int QUALITY_MAYBE = 1; // Might be able to convert.
/**
* A constant stating that the given file is a Wmf-File.
*/
public static final int QUALITY_YES = 2; // Can convert.
private static final int PLACEABLE_HEADER_SIZE = 22;
private static final int STANDARD_HEADER_SIZE = 18;
/**
* Metadata Positions This implementation always reserves space for both the standard and the extended wmf header; the
* standard header is always placed after the extended header.
*/
private static final int WMF_FILE_TYPE = PLACEABLE_HEADER_SIZE; // WORD
private static final int WMF_HEADER_SIZE = PLACEABLE_HEADER_SIZE + 0x2; // WORD
// private static final int WMF_VERSION = PLACEABLE_HEADER_SIZE + 0x4; // WORD
private static final int WMF_FILE_SIZE = PLACEABLE_HEADER_SIZE + 0x06; // DWORD
private static final int WMF_NUM_OF_REC = PLACEABLE_HEADER_SIZE + 0x0a; // WORD
private static final int WMF_MAX_REC_SIZE = PLACEABLE_HEADER_SIZE + 0x0c; // DWORD
// private static final int WMF_NUM_PARAMS = PLACEABLE_HEADER_SIZE + 0x10; // WORD always 0 not used
/**
* MetaData type: WmfFile is a memory copy.
*/
private static final int WMF_TYPE_MEM = 0;
/** MetaData type: WmfFile is a disk copy. */
// private static final int WMF_TYPE_DISK = 1;
/**
* A magic number indicating that this is a Aldus WMF file.
*/
private static final int ALDUS_MAGIC_NUMBER_VAL = 0x9ac6cdd7;
private static final int ALDUS_MAGIC_NUMBER_POS = 0;
// private static final int ALDUS_HANDLE_POS = 4;
private static final int ALDUS_POS_LEFT = 6;
private static final int ALDUS_POS_TOP = 8;
private static final int ALDUS_POS_RIGHT = 10;
private static final int ALDUS_POS_BOTTOM = 12;
private static final int ALDUS_RESOLUTION = 14; // units per inch
public MfHeader() {
}
// private static final int ALDUS_RESERVED = 16;
// private static final int ALDUS_CHECKSUM = 20;
/**
* Is the given input a metafile? We have to guess by reading the header and/or by looking at the file name.
*
* @param inName the file name of the stream source
* @param in the input stream.
* @return either QUALITY_NO, QUALITY_MAYBE or QUALITY_YES.
* @throws IOException if an error occured.
*/
public static int isMetafile( final String inName, final InputStream in ) throws IOException {
if ( in != null ) {
// See if we have a valid header.
in.mark( PLACEABLE_HEADER_SIZE + STANDARD_HEADER_SIZE );
final MfHeader header = new MfHeader();
header.read( in );
in.reset();
if ( !header.isValid() ) {
return QUALITY_NO;
}
if ( header.isPlaceable() ) {
return QUALITY_YES;
}
// We are not so confident of identifying non-placeable
// metafiles, so we require both isValid() and the file
// extension to match.
}
// True if the extension is .wmf.
if ( inName.regionMatches( true, inName.length() - 4, ".wmf", 0, 4 ) ) {
return QUALITY_MAYBE;
}
return QUALITY_NO;
}
/**
* Read the header from the given input.
*
* @param in the input stream
* @throws IOException if an error occured.
*/
public void read( final InputStream in )
throws IOException {
final int total = PLACEABLE_HEADER_SIZE + STANDARD_HEADER_SIZE;
setCapacity( total );
read( in, 0, 4 );
if ( isPlaceable() ) {
// read the standard header and the extended Aldus header
read( in, 4, total - 4 );
} else {
// Ignore the space for the placeable header, move the (already read)
// standard header information to the correct position (after the space
// of the (non-existent) extended header
move( 0, PLACEABLE_HEADER_SIZE, 4 );
// read the remaining bytes of the standard header ...
read( in, PLACEABLE_HEADER_SIZE + 4, STANDARD_HEADER_SIZE - 4 );
}
// Now have the placeable header at the start of the headers buffer,
// and the windows header following it.
}
/**
* Return true if this is an Aldus placeable header.
*
* @return true, if this is an Aldus placeable header, false otherwise.
*/
private boolean isPlaceable() {
// Verify magic number.
return getInt( ALDUS_MAGIC_NUMBER_POS ) == ALDUS_MAGIC_NUMBER_VAL;
}
/**
* Returns true if it looks like a real metafile. This implementation does not support Memory-WmfFiles.
*
* @return true, if this file is valid, false otherwise.
*/
public boolean isValid() {
final int type = getShort( WMF_FILE_TYPE ); // Memory or disk.
if ( type == WMF_TYPE_MEM ) {
// type == null means this is a wmf from memory. we don't want that
return false;
}
if ( getShort( WMF_HEADER_SIZE ) != 9 ) // Header size.
{
// A VALID wmf-File has always a standard-header size of 9 WORDS == 18 bytes
return false;
}
return true;
}
/**
* Return the bounding box of this metafile. This returns an empty (0,0,0,0) rectangle if this file is not placeable.
*
* @return the bounding box of the metafile.
*/
public Rectangle getBBox() {
final int left = getShort( ALDUS_POS_LEFT );
final int top = getShort( ALDUS_POS_TOP );
final int right = getShort( ALDUS_POS_RIGHT );
final int bottom = getShort( ALDUS_POS_BOTTOM );
return new Rectangle( left, top, right - left, bottom - top );
}
/**
* Gets the defined resolution, if this is an Aldus-File, null otherwise.
*
* @return the image resolution or 0 if not defined.
*/
public int getUnitsPerInch() {
return getShort( ALDUS_RESOLUTION );
}
/**
* Gets the file size of the WmfFile.
*
* @return the filesize in bytes.
*/
public int getFileSize() {
return getInt( WMF_FILE_SIZE ) * 2;
}
/**
* Gets the number of records stored in this metafile.
*
* @return the number of records.
*/
public int getObjectsSize() {
return getShort( WMF_NUM_OF_REC );
}
/**
* Gets the size of the largest Record.
*
* @return the maximum record size.
*/
public int getMaxRecordSize() {
return getInt( WMF_MAX_REC_SIZE ) * 2;
}
/**
* Gets the header size.
*
* @return the header size.
*/
public int getHeaderSize() {
if ( isPlaceable() ) {
return PLACEABLE_HEADER_SIZE + STANDARD_HEADER_SIZE;
} else {
return STANDARD_HEADER_SIZE;
}
}
}