/// <summary> /// Writes a variant value To an output stream. This method ensures that /// always a multiple of 4 bytes is written. /// If the codepage is UTF-16, which is encouraged, strings /// <strong>must</strong> always be written as {@link Variant#VT_LPWSTR} /// strings, not as {@link Variant#VT_LPSTR} strings. This method ensure this /// by Converting strings appropriately, if needed. /// </summary> /// <param name="out1">The stream To Write the value To.</param> /// <param name="type">The variant's type.</param> /// <param name="value">The variant's value.</param> /// <param name="codepage">The codepage To use To Write non-wide strings</param> /// <returns>The number of entities that have been written. In many cases an /// "entity" is a byte but this is not always the case.</returns> public static int Write(Stream out1, long type, Object value, int codepage) { int length = 0; switch ((int)type) { case Variant.VT_BOOL: { byte[] data = new byte[2]; if ((bool)value) { out1.WriteByte(0xFF); out1.WriteByte(0xFF); } else { out1.WriteByte(0x00); out1.WriteByte(0x00); } length += 2; break; } case Variant.VT_LPSTR: { CodePageString codePageString = new CodePageString((String)value, codepage); length += codePageString.Write(out1); break; } case Variant.VT_LPWSTR: { int nrOfChars = ((String)value).Length + 1; length += TypeWriter.WriteUIntToStream(out1, (uint)nrOfChars); char[] s = ((String)value).ToCharArray(); for (int i = 0; i < s.Length; i++) { int high = ((s[i] & 0x0000ff00) >> 8); int low = (s[i] & 0x000000ff); byte highb = (byte)high; byte lowb = (byte)low; out1.WriteByte(lowb); out1.WriteByte(highb); length += 2; } // NullTerminator out1.WriteByte(0x00); out1.WriteByte(0x00); length += 2; break; } case Variant.VT_CF: { byte[] b = (byte[])value; out1.Write(b, 0, b.Length); length = b.Length; break; } case Variant.VT_EMPTY: { length += TypeWriter.WriteUIntToStream(out1, Variant.VT_EMPTY); break; } case Variant.VT_I2: { short x; try { x = Convert.ToInt16(value, CultureInfo.InvariantCulture); } catch (OverflowException) { x = (short)((int)value); } length += TypeWriter.WriteToStream(out1, x); //length = LittleEndianConsts.SHORT_SIZE; break; } case Variant.VT_I4: { if (!(value is int)) { throw new Exception("Could not cast an object To " + "int" + ": " + value.GetType().Name + ", " + value.ToString()); } length += TypeWriter.WriteToStream(out1, (int)value); break; } case Variant.VT_I8: { length += TypeWriter.WriteToStream(out1, Convert.ToInt64(value, CultureInfo.CurrentCulture)); break; } case Variant.VT_R8: { length += TypeWriter.WriteToStream(out1, (Double)value); break; } case Variant.VT_FILETIME: { long filetime; if (value != null) { filetime = Util.DateToFileTime((DateTime)value); } else { filetime = 0; } int high = (int)((filetime >> 32) & 0x00000000FFFFFFFFL); int low = (int)(filetime & 0x00000000FFFFFFFFL); Filetime filetimeValue = new Filetime(low, high); length += filetimeValue.Write(out1); //length += TypeWriter.WriteUIntToStream // (out1, (uint)(0x0000000FFFFFFFFL & low)); //length += TypeWriter.WriteUIntToStream // (out1, (uint)(0x0000000FFFFFFFFL & high)); break; } default: { /* The variant type is not supported yet. However, if the value * is a byte array we can Write it nevertheless. */ if (value is byte[]) { byte[] b = (byte[])value; out1.Write(b, 0, b.Length); length = b.Length; WriteUnsupportedTypeMessage (new WritingNotSupportedException(type, value)); } else { throw new WritingNotSupportedException(type, value); } break; } } /* pad values to 4-bytes */ while ((length & 0x3) != 0) { out1.WriteByte(0x00); length++; } return(length); }
/// <summary> /// Reads a variant type from a byte array /// </summary> /// <param name="src">The byte array</param> /// <param name="offset">The offset in the byte array where the variant starts</param> /// <param name="length">The Length of the variant including the variant type field</param> /// <param name="type">The variant type To Read</param> /// <param name="codepage">The codepage To use for non-wide strings</param> /// <returns>A Java object that corresponds best To the variant field. For /// example, a VT_I4 is returned as a {@link long}, a VT_LPSTR as a /// {@link String}.</returns> public static Object Read(byte[] src, int offset, int length, long type, int codepage) { TypedPropertyValue typedPropertyValue = new TypedPropertyValue( (int)type, null); int unpadded; try { unpadded = typedPropertyValue.ReadValue(src, offset); } catch (InvalidOperationException) { int propLength = Math.Min(length, src.Length - offset); byte[] v = new byte[propLength]; System.Array.Copy(src, offset, v, 0, propLength); throw new ReadingNotSupportedException(type, v); } switch ((int)type) { case Variant.VT_EMPTY: case Variant.VT_I4: case Variant.VT_I8: case Variant.VT_R8: /* * we have more property types that can be converted into Java * objects, but current API need to be preserved, and it returns * other types as byte arrays. In future major versions it shall be * changed -- sergey */ return(typedPropertyValue.Value); case Variant.VT_I2: { /* * also for backward-compatibility with prev. versions of POI * --sergey */ return((short)typedPropertyValue.Value); } case Variant.VT_FILETIME: { Filetime filetime = (Filetime)typedPropertyValue.Value; return(Util.FiletimeToDate((int)filetime.High, (int)filetime.Low)); } case Variant.VT_LPSTR: { CodePageString string1 = (CodePageString)typedPropertyValue.Value; return(string1.GetJavaValue(codepage)); } case Variant.VT_LPWSTR: { UnicodeString string1 = (UnicodeString)typedPropertyValue.Value; return(string1.ToJavaString()); } case Variant.VT_CF: { // if(l1 < 0) { /* * YK: reading the ClipboardData packet (VT_CF) is not quite * correct. The size of the data is determined by the first four * bytes of the packet while the current implementation calculates * it in the Section constructor. Test files in Bugzilla 42726 and * 45583 clearly show that this approach does not always work. The * workaround below attempts to gracefully handle such cases instead * of throwing exceptions. * * August 20, 2009 */ // l1 = LittleEndian.getInt(src, o1); o1 += LittleEndian.INT_SIZE; // } // final byte[] v = new byte[l1]; // System.arraycopy(src, o1, v, 0, v.length); // value = v; // break; ClipboardData clipboardData = (ClipboardData)typedPropertyValue.Value; return(clipboardData.ToByteArray()); } case Variant.VT_BOOL: { VariantBool bool1 = (VariantBool)typedPropertyValue.Value; return((bool)bool1.Value); } default: { /* * it is not very good, but what can do without breaking current * API? --sergey */ byte[] v = new byte[unpadded]; System.Array.Copy(src, offset, v, 0, unpadded); throw new ReadingNotSupportedException(type, v); } } }