/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for Additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace HH.WMS.Utils.NPOI.DDF
{
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Drawing;
using HH.WMS.Utils.NPOI.Util;
using HH.WMS.Utils.Ionic.Zlib;
///
/// @author Daniel Noll
///
public class EscherMetafileBlip:EscherBlipRecord
{
private static POILogger log = POILogFactory.GetLogger(typeof(EscherMetafileBlip));
public const short RECORD_ID_EMF = unchecked((short) 0xF018) + 2;
public const short RECORD_ID_WMF = unchecked((short)0xF018) + 3;
public const short RECORD_ID_PICT = unchecked((short)0xF018) + 4;
/**
* BLIP signatures as defined in the escher spec
*/
public const short SIGNATURE_EMF = 0x3D40;
public const short SIGNATURE_WMF = 0x2160;
public const short SIGNATURE_PICT = 0x5420;
private const int HEADER_SIZE = 8;
private byte[] field_1_UID;
/**
* The primary UID is only saved to disk if (blip_instance ^ blip_signature == 1)
*/
private byte[] field_2_UID;
private int field_2_cb;
private int field_3_rcBounds_x1;
private int field_3_rcBounds_y1;
private int field_3_rcBounds_x2;
private int field_3_rcBounds_y2;
private int field_4_ptSize_w;
private int field_4_ptSize_h;
private int field_5_cbSave;
private byte field_6_fCompression;
private byte field_7_fFilter;
private byte[] raw_pictureData;
private byte[] remainingData;
///
/// This method deSerializes the record from a byte array.
///
/// The byte array containing the escher record information
/// The starting offset into
/// May be null since this is not a container record.
///
/// The number of bytes Read from the byte array.
///
public override int FillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
{
int bytesAfterHeader = ReadHeader( data, offset );
int pos = offset + HEADER_SIZE;
field_1_UID = new byte[16];
Array.Copy( data, pos, field_1_UID, 0, 16 ); pos += 16;
if((Options ^ Signature) == 0x10){
field_2_UID = new byte[16];
Array.Copy( data, pos, field_2_UID, 0, 16 ); pos += 16;
}
field_2_cb = LittleEndian.GetInt( data, pos ); pos += 4;
field_3_rcBounds_x1 = LittleEndian.GetInt( data, pos ); pos += 4;
field_3_rcBounds_y1 = LittleEndian.GetInt( data, pos ); pos += 4;
field_3_rcBounds_x2 = LittleEndian.GetInt( data, pos ); pos += 4;
field_3_rcBounds_y2 = LittleEndian.GetInt( data, pos ); pos += 4;
field_4_ptSize_w = LittleEndian.GetInt( data, pos ); pos += 4;
field_4_ptSize_h = LittleEndian.GetInt( data, pos ); pos += 4;
field_5_cbSave = LittleEndian.GetInt( data, pos ); pos += 4;
field_6_fCompression = data[pos]; pos++;
field_7_fFilter = data[pos]; pos++;
raw_pictureData = new byte[field_5_cbSave];
Array.Copy( data, pos, raw_pictureData, 0, field_5_cbSave );
pos += field_5_cbSave;
// 0 means DEFLATE compression
// 0xFE means no compression
if (field_6_fCompression == 0)
{
field_pictureData = InflatePictureData(raw_pictureData);
}
else
{
field_pictureData = raw_pictureData;
}
int remaining = bytesAfterHeader - pos + offset + HEADER_SIZE;
if (remaining > 0)
{
remainingData = new byte[remaining];
Array.Copy(data, pos, remainingData, 0, remaining);
}
return bytesAfterHeader + HEADER_SIZE;
}
///
/// Serializes the record to an existing byte array.
///
/// the offset within the byte array
/// the data array to Serialize to
/// a listener for begin and end serialization events.
/// the number of bytes written.
public override int Serialize(int offset, byte[] data, EscherSerializationListener listener)
{
listener.BeforeRecordSerialize(offset, RecordId, this);
int pos = offset;
LittleEndian.PutShort( data, pos, Options ); pos += 2;
LittleEndian.PutShort( data, pos, RecordId ); pos += 2;
LittleEndian.PutInt( data, pos, RecordSize - HEADER_SIZE ); pos += 4;
Array.Copy( field_1_UID, 0, data, pos, field_1_UID.Length ); pos += field_1_UID.Length;
if((Options ^ Signature) == 0x10){
Array.Copy( field_2_UID, 0, data, pos, field_2_UID.Length ); pos += field_2_UID.Length;
}
LittleEndian.PutInt( data, pos, field_2_cb ); pos += 4;
LittleEndian.PutInt( data, pos, field_3_rcBounds_x1 ); pos += 4;
LittleEndian.PutInt( data, pos, field_3_rcBounds_y1 ); pos += 4;
LittleEndian.PutInt( data, pos, field_3_rcBounds_x2 ); pos += 4;
LittleEndian.PutInt( data, pos, field_3_rcBounds_y2 ); pos += 4;
LittleEndian.PutInt( data, pos, field_4_ptSize_w ); pos += 4;
LittleEndian.PutInt( data, pos, field_4_ptSize_h ); pos += 4;
LittleEndian.PutInt( data, pos, field_5_cbSave ); pos += 4;
data[pos] = field_6_fCompression; pos++;
data[pos] = field_7_fFilter; pos++;
Array.Copy( raw_pictureData, 0, data, pos, raw_pictureData.Length );
pos += raw_pictureData.Length;
if (remainingData != null)
{
Array.Copy(remainingData, 0, data, pos, remainingData.Length);
pos += remainingData.Length;
}
listener.AfterRecordSerialize(offset + RecordSize, RecordId, RecordSize, this);
return RecordSize;
}
///
/// Decompresses the provided data, returning the inflated result.
///
/// the deflated picture data.
/// the inflated picture data.
private static byte[] InflatePictureData(byte[] data)
{
using (MemoryStream in1 = new MemoryStream(data))
{
using (MemoryStream out1 = new MemoryStream())
{
ZlibStream zIn = null;
try
{
zIn = new ZlibStream(in1, CompressionMode.Decompress, true);
byte[] buf = new byte[4096];
int ReadBytes;
while ((ReadBytes = zIn.Read(buf, 0, buf.Length)) > 0)
{
out1.Write(buf, 0, ReadBytes);
}
return out1.ToArray();
}
catch (IOException e)
{
log.Log(POILogger.WARN, "Possibly corrupt compression or non-compressed data", e);
return data;
}
}
}
}
///
/// Returns the number of bytes that are required to Serialize this record.
///
/// Number of bytes
public override int RecordSize
{
get
{
int size = 8 + 50 + raw_pictureData.Length;
if (remainingData != null) size += remainingData.Length;
if ((Options ^ Signature) == 0x10)
{
size += field_2_UID.Length;
}
return size;
}
}
///
/// Gets or sets the UID.
///
/// The UID.
public byte[] UID
{
get { return field_1_UID; }
set { this.field_1_UID = value; }
}
///
/// Gets or sets the primary UID.
///
/// The primary UID.
public byte[] PrimaryUID
{
get{return field_2_UID;}
set { this.field_2_UID = value; }
}
///
/// Gets or sets the size of the uncompressed.
///
/// The size of the uncompressed.
public int UncompressedSize
{
get { return field_2_cb; }
set { field_2_cb = value; }
}
///
/// Gets or sets the bounds.
///
/// The bounds.
public Rectangle Bounds
{
get
{
return new Rectangle(field_3_rcBounds_x1,
field_3_rcBounds_y1,
field_3_rcBounds_x2 - field_3_rcBounds_x1,
field_3_rcBounds_y2 - field_3_rcBounds_y1);
}
set
{
field_3_rcBounds_x1 = value.X;
field_3_rcBounds_y1 = value.Y;
field_3_rcBounds_x2 = value.X + value.Width;
field_3_rcBounds_y2 = value.Y + value.Height;
}
}
///
/// Gets or sets the size EMU.
///
/// The size EMU.
public Size SizeEMU
{
get{
return new Size(field_4_ptSize_w, field_4_ptSize_h);
}
set
{
field_4_ptSize_w = value.Width;
field_4_ptSize_h = value.Height;
}
}
///
/// Gets or sets the size of the compressed.
///
/// The size of the compressed.
public int CompressedSize
{
get{return field_5_cbSave;}
set{field_5_cbSave=value;}
}
///
/// Gets or sets a value indicating whether this instance is compressed.
///
///
/// true if this instance is compressed; otherwise, false.
///
public bool IsCompressed
{
get{return (field_6_fCompression == 0);}
set { field_6_fCompression = value ? (byte)0 : (byte)0xFE; }
}
public byte[] RemainingData
{
get
{
return remainingData;
}
}
///
/// Returns a that represents the current .
///
///
/// A that represents the current .
///
public override String ToString()
{
String nl = Environment.NewLine;
String extraData;
using (MemoryStream b = new MemoryStream())
{
try
{
HexDump.Dump(this.field_pictureData, 0, b, 0);
extraData = b.ToString();
}
catch (Exception e)
{
extraData = e.ToString();
}
return GetType().Name + ":" + nl +
" RecordId: 0x" + HexDump.ToHex(RecordId) + nl +
" Options: 0x" + HexDump.ToHex(Options) + nl +
" UID: 0x" + HexDump.ToHex(field_1_UID) + nl +
(field_2_UID == null ? "" : (" UID2: 0x" + HexDump.ToHex(field_2_UID) + nl)) +
" Uncompressed Size: " + HexDump.ToHex(field_2_cb) + nl +
" Bounds: " + Bounds + nl +
" Size in EMU: " + SizeEMU + nl +
" Compressed Size: " + HexDump.ToHex(field_5_cbSave) + nl +
" Compression: " + HexDump.ToHex(field_6_fCompression) + nl +
" Filter: " + HexDump.ToHex(field_7_fFilter) + nl +
" Extra Data:" + nl + extraData +
(remainingData == null ? null : ("\n" +
" Remaining Data: " + HexDump.ToHex(remainingData, 32)));
}
}
///
/// Return the blip signature
///
/// the blip signature
public short Signature
{
get{
short sig = 0;
switch(RecordId){
case RECORD_ID_EMF:
sig = SIGNATURE_EMF;
break;
case RECORD_ID_WMF:
sig = SIGNATURE_WMF;
break;
case RECORD_ID_PICT:
sig = SIGNATURE_PICT;
break;
default: log.Log(POILogger.WARN, "Unknown metafile: " + RecordId); break;
}
return sig;
}
}
}
}