Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / Ink / InkSerializedFormat / InkSerializer.cs / 1 / InkSerializer.cs
//#define OLD_ISF //------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------- using MS.Utility; using System; using System.Diagnostics; using System.Security; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.IO; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Security.Permissions; using System.Runtime.Serialization; using System.Collections; using System.Collections.Generic; using System.Windows.Input; using System.Windows.Ink; using MS.Internal.IO.Packaging; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace MS.Internal.Ink.InkSerializedFormat { internal class StrokeCollectionSerializer { #region Constants (Static Fields) internal static readonly double AvalonToHimetricMultiplier = 2540F / 96.0F; internal static readonly double HimetricToAvalonMultiplier = 96.0F / 2540.0F; internal static readonly TransformDescriptor IdentityTransformDescriptor; static StrokeCollectionSerializer() { TransformDescriptor transformDescriptor = new TransformDescriptor(); transformDescriptor.Transform[0] = 1.0f; transformDescriptor.Tag = KnownTagCache.KnownTagIndex.TransformIsotropicScale; transformDescriptor.Size = 1; StrokeCollectionSerializer.IdentityTransformDescriptor = transformDescriptor; } #endregion #region Constructors // disable default constructor private StrokeCollectionSerializer() { } ////// Initialize the Ink serializer /// /// Pointer to the core stroke collection - avoids recreation of collections internal StrokeCollectionSerializer(StrokeCollection coreStrokes) { _coreStrokes = coreStrokes; } #endregion #region Public Fields internal PersistenceFormat CurrentPersistenceFormat = PersistenceFormat.InkSerializedFormat; internal CompressionMode CurrentCompressionMode = CompressionMode.Compressed; internal System.Collections.Generic.ListStrokeIds = null; #endregion #region Decoding #region Public Methods /// /// Loads a Ink object from a spcified byte array in the form of Ink Serialzied Format /// This method checks for the 'base64:' prefix in the byte[] because that is how V1 /// saved ISF /// /// internal void DecodeISF(Stream inkData) { try { // First examine the input data header bool isBase64; bool isGif; uint cbData; ExamineStreamHeader(inkData, out isBase64, out isGif, out cbData); if (isBase64) { // // this is a funky tablet v1 based byte[] that is base64 encoded... // each 4 bytes in this array corresponds to 3 bytes of ISF data. // EXCEPT the first 7 bytes which are saved with the value // 'base64:' and must not be base64 decoded. // and the last null terminator (if present) // // The following code does two things: // 1) Convert each byte to a char so it can be base64 decoded // 2) Strips out the first 7 resulting characters // int isfBase64PrefixLength = Base64HeaderBytes.Length; // the previous call to ExamineStreamHeader guarantees that inkData.Length > isfBase64PrefixLength System.Diagnostics.Debug.Assert(inkData.Length > isfBase64PrefixLength); inkData.Position = (long)isfBase64PrefixLength; ListcharData = new List ((int)inkData.Length); int intByte = inkData.ReadByte(); while (intByte != -1) { byte b = (byte)intByte; charData.Add((char)b); intByte = inkData.ReadByte(); } if (0 == (byte)(charData[charData.Count - 1])) { //strip the null terminator charData.RemoveAt(charData.Count - 1); } char[] chars = charData.ToArray(); byte[] isfData = Convert.FromBase64CharArray(chars, 0, chars.Length); MemoryStream ms = new MemoryStream(isfData); if (IsGIFData(ms)) { DecodeRawISF(ReadGifData(ms)); } else { DecodeRawISF(ms); } } else if (true == isGif) { DecodeRawISF(ReadGifData(inkData)); } else { DecodeRawISF(inkData); } } #if DEBUG catch (ArgumentException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (InvalidOperationException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (IndexOutOfRangeException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (NullReferenceException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (EndOfStreamException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (OverflowException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } #else catch (ArgumentException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (InvalidOperationException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (IndexOutOfRangeException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (NullReferenceException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (EndOfStreamException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (OverflowException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } #endif } #endregion #region Private Methods /// /// Loads the strokeIds from the stream, we need to do this to decrement the count of bytes /// internal uint LoadStrokeIds(Stream isfStream, uint cbSize) { if (0 == cbSize) return 0; uint cb; uint cbTotal = cbSize; // First decode the no of ids uint count; cb = SerializationHelper.Decode(isfStream, out count); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "isfStream"); cbTotal -= cb; if (0 == count) return (cbSize - cbTotal); cb = cbTotal; byte[] inputdata = new byte[cb]; // read the stream uint bytesRead = StrokeCollectionSerializer.ReliableRead(isfStream, inputdata, cb); if (cb != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "isfStream"); } cbTotal -= cb; if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "isfStream"); return cbSize; } private bool IsGIFData(Stream inkdata) { Debug.Assert(inkdata != null); long currentPosition = inkdata.Position; try { return ((byte)inkdata.ReadByte() == 'G' && (byte)inkdata.ReadByte() == 'I' && (byte)inkdata.ReadByte() == 'F'); } finally { //reset position inkdata.Position = currentPosition; } } private Stream ReadGifData(Stream inkdata) { // Read the GIF header ... System.Drawing.Bitmap img = new System.Drawing.Bitmap(inkdata); // Read the comment as that is where the ISF is stored... // for reference the tag is PropertyTagExifUserComment [0x9286] or 37510 (int) System.Drawing.Imaging.PropertyItem piComment = img.GetPropertyItem(37510); return new MemoryStream(piComment.Value); } private void ExamineStreamHeader(Stream inkdata, out bool fBase64, out bool fGif, out uint cbData) { fGif = false; cbData = 0; fBase64 = false; if (inkdata.Length >= 7) { fBase64 = IsBase64Data(inkdata); } // Check for RAW gif if (!fBase64 && inkdata.Length >= 3) { fGif = IsGIFData(inkdata); } return; } private static readonly byte[] Base64HeaderBytes = new byte[]{(byte)'b', (byte)'a', (byte)'s', (byte)'e', (byte)'6', (byte)'4', (byte)':'}; #if OLD_ISF ////// Takes an ISF byte[] and populates the StrokeCollection /// attached to this StrokeCollectionSerializer. /// /// a byte[] of the raw isf to decode ////// Critical - Calls critical methods in Compressor /// TreatAsSafe - inkdata is a disconnected copy from the one passed in /// underlying unmanaged code has been security reviewed and fuzzed /// [SecurityCritical, SecurityTreatAsSafe] #else ////// Takes an ISF Stream and populates the StrokeCollection /// attached to this StrokeCollectionSerializer. /// /// a Stream the raw isf to decode #endif private void DecodeRawISF(Stream inputStream) { Debug.Assert(inputStream != null); KnownTagCache.KnownTagIndex isfTag; uint remainingBytesInStream; uint bytesDecodedInCurrentTag = 0; bool strokeDescriptorBlockDecoded = false; bool drawingAttributesBlockDecoded = false; bool metricBlockDecoded = false; bool transformDecoded = false; uint strokeDescriptorTableIndex = 0; uint oldStrokeDescriptorTableIndex = 0xFFFFFFFF; uint drawingAttributesTableIndex = 0; uint oldDrawingAttributesTableIndex = 0xFFFFFFFF; uint metricDescriptorTableIndex = 0; uint oldMetricDescriptorTableIndex = 0xFFFFFFFF; uint transformTableIndex = 0; uint oldTransformTableIndex = 0xFFFFFFFF; GuidList guidList = new GuidList(); int strokeIndex = 0; StylusPointDescription currentStylusPointDescription = null; Matrix currentTabletToInkTransform = Matrix.Identity; _strokeDescriptorTable = new System.Collections.Generic.List(); _drawingAttributesTable = new System.Collections.Generic.List (); _transformTable = new System.Collections.Generic.List (); _metricTable = new System.Collections.Generic.List (); // First make sure this ink is empty if (0 != _coreStrokes.Count || _coreStrokes.ExtendedProperties.Count != 0) { throw new InvalidOperationException(ISFDebugMessage("ISF decoder cannot operate on non-empty ink container")); } #if OLD_ISF // // store a compressor reference at this scope, if it is needed (if there is a compresson header) and // therefore instanced during this routine, we will dispose of it // in the finally block // Compressor compressor = null; try { #endif // First read the isfTag uint uiTag; uint localBytesDecoded = SerializationHelper.Decode(inputStream, out uiTag); if (0x00 != uiTag) throw new ArgumentException(SR.Get(SRID.InvalidStream)); // Now read the size of the stream localBytesDecoded = SerializationHelper.Decode(inputStream, out remainingBytesInStream); ISFDebugTrace("Decoded Stream Size in Bytes: " + remainingBytesInStream.ToString()); if (0 == remainingBytesInStream) return; while (0 < remainingBytesInStream) { bytesDecodedInCurrentTag = 0; // First read the isfTag localBytesDecoded = SerializationHelper.Decode(inputStream, out uiTag); isfTag = (KnownTagCache.KnownTagIndex)uiTag; if (remainingBytesInStream >= localBytesDecoded) remainingBytesInStream -= localBytesDecoded; else { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } ISFDebugTrace("Decoding Tag: " + ((KnownTagCache.KnownTagIndex)isfTag).ToString()); switch (isfTag) { case KnownTagCache.KnownTagIndex.GuidTable: case KnownTagCache.KnownTagIndex.DrawingAttributesTable: case KnownTagCache.KnownTagIndex.DrawingAttributesBlock: case KnownTagCache.KnownTagIndex.StrokeDescriptorTable: case KnownTagCache.KnownTagIndex.StrokeDescriptorBlock: case KnownTagCache.KnownTagIndex.MetricTable: case KnownTagCache.KnownTagIndex.MetricBlock: case KnownTagCache.KnownTagIndex.TransformTable: case KnownTagCache.KnownTagIndex.ExtendedTransformTable: case KnownTagCache.KnownTagIndex.Stroke: case KnownTagCache.KnownTagIndex.CompressionHeader: case KnownTagCache.KnownTagIndex.PersistenceFormat: case KnownTagCache.KnownTagIndex.HimetricSize: case KnownTagCache.KnownTagIndex.StrokeIds: { localBytesDecoded = SerializationHelper.Decode(inputStream, out bytesDecodedInCurrentTag); if (remainingBytesInStream < (localBytesDecoded + bytesDecodedInCurrentTag)) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } remainingBytesInStream -= localBytesDecoded; // Based on the isfTag figure out what information we're loading switch (isfTag) { case KnownTagCache.KnownTagIndex.GuidTable: { // Load guid Table localBytesDecoded = guidList.Load(inputStream, bytesDecodedInCurrentTag); break; } case KnownTagCache.KnownTagIndex.DrawingAttributesTable: { // Load drawing attributes table localBytesDecoded = LoadDrawAttrsTable(inputStream, guidList, bytesDecodedInCurrentTag); drawingAttributesBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.DrawingAttributesBlock: { //initialize to V1 defaults, we do it this way as opposed //to dr.DrawingFlags = 0 because this was a perf hot spot //and instancing the epc first mitigates it ExtendedPropertyCollection epc = new ExtendedPropertyCollection(); epc.Add(KnownIds.DrawingFlags, DrawingFlags.Polyline); DrawingAttributes dr = new DrawingAttributes(epc); localBytesDecoded = DrawingAttributeSerializer.DecodeAsISF(inputStream, guidList, bytesDecodedInCurrentTag, dr); _drawingAttributesTable.Add(dr); drawingAttributesBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorTable: { // Load stroke descriptor table localBytesDecoded = DecodeStrokeDescriptorTable(inputStream, bytesDecodedInCurrentTag); strokeDescriptorBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorBlock: { // Load a single stroke descriptor localBytesDecoded = DecodeStrokeDescriptorBlock(inputStream, bytesDecodedInCurrentTag); strokeDescriptorBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.MetricTable: { // Load Metric Table localBytesDecoded = DecodeMetricTable(inputStream, bytesDecodedInCurrentTag); metricBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.MetricBlock: { // Load a single Metric Block MetricBlock blk; localBytesDecoded = DecodeMetricBlock(inputStream, bytesDecodedInCurrentTag, out blk); _metricTable.Clear(); _metricTable.Add(blk); metricBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.TransformTable: { // Load Transform Table localBytesDecoded = DecodeTransformTable(inputStream, bytesDecodedInCurrentTag, false); transformDecoded = true; break; } case KnownTagCache.KnownTagIndex.ExtendedTransformTable: { // non-double transform table should have already been loaded if (!transformDecoded) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } // Load double-sized Transform Table localBytesDecoded = DecodeTransformTable(inputStream, bytesDecodedInCurrentTag, true); break; } case KnownTagCache.KnownTagIndex.PersistenceFormat: { uint fmt; localBytesDecoded = SerializationHelper.Decode(inputStream, out fmt); // Set the appropriate persistence information if (0 == fmt) { CurrentPersistenceFormat = PersistenceFormat.InkSerializedFormat; } else if (0x00000001 == fmt) { CurrentPersistenceFormat = PersistenceFormat.Gif; } break; } case KnownTagCache.KnownTagIndex.HimetricSize: { // Loads the Hi Metric Size for Fortified GIFs int sz; localBytesDecoded = SerializationHelper.SignDecode(inputStream, out sz); if (localBytesDecoded > remainingBytesInStream) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); _himetricSize.X = (double)sz; localBytesDecoded += SerializationHelper.SignDecode(inputStream, out sz); _himetricSize.Y = (double)sz; break; } case KnownTagCache.KnownTagIndex.CompressionHeader: { #if OLD_ISF byte[] data = new byte[bytesDecodedInCurrentTag]; // read the header from the stream uint bytesRead = StrokeCollectionSerializer.ReliableRead(inputStream, data, bytesDecodedInCurrentTag); if (bytesDecodedInCurrentTag != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "isfStream"); } uint size = bytesDecodedInCurrentTag; compressor = new Compressor(data, ref size); // in case the actual number of bytes read by the compressor // is less than the encoder had expected (e.g. compression // header was encoded as 10 bytes, but only 7 bytes were read) // then we don't want to adjust the stream position because // there are likely other following tags that are encoded // after the compression tag. This should never happen, // so just fail if the compressor is broken or the ISF is // corrupted. if (size != bytesDecodedInCurrentTag) { throw new InvalidOperationException(ISFDebugMessage("Compressor intialization reported inconsistent size")); } #else //just advance the inputstream position, we don't need //no compression header in the new isf decoding inputStream.Seek(bytesDecodedInCurrentTag, SeekOrigin.Current); #endif localBytesDecoded = bytesDecodedInCurrentTag; break; } case KnownTagCache.KnownTagIndex.StrokeIds: { localBytesDecoded = LoadStrokeIds(inputStream, bytesDecodedInCurrentTag); break; } case KnownTagCache.KnownTagIndex.Stroke: { ISFDebugTrace(" Decoding Stroke Id#(" + (strokeIndex + 1).ToString() + ")"); StrokeDescriptor strokeDescriptor = null; // Load the stroke descriptor based on the index from the list of unique // stroke descriptors if (strokeDescriptorBlockDecoded) { if (oldStrokeDescriptorTableIndex != strokeDescriptorTableIndex) { if (_strokeDescriptorTable.Count <= strokeDescriptorTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } strokeDescriptor = _strokeDescriptorTable[(int)strokeDescriptorTableIndex]; } // use new transform if the last transform is uninit'd or has changed if (oldTransformTableIndex != transformTableIndex) { // if transform was specified in the ISF stream if (transformDecoded) { if (_transformTable.Count <= transformTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); // Load the transform descriptor based on the index from the list of unique // transforn descriptors currentTabletToInkTransform = LoadTransform(_transformTable[(int)transformTableIndex]); } oldTransformTableIndex = transformTableIndex; // cache the transform by remembering the index // since ISF is stored in HIMETRIC, and we want to expose packet data // as Avalon units, we'll update the convert the transform before loading the stroke currentTabletToInkTransform.Scale(StrokeCollectionSerializer.HimetricToAvalonMultiplier, StrokeCollectionSerializer.HimetricToAvalonMultiplier); } MetricBlock metricBlock = null; // Load the metric block based on the index from the list of unique metric blocks if (metricBlockDecoded) { if (oldMetricDescriptorTableIndex != metricDescriptorTableIndex) { if (_metricTable.Count <= metricDescriptorTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } metricBlock = _metricTable[(int)metricDescriptorTableIndex]; } DrawingAttributes activeDrawingAttributes = null; // Load the drawing attributes based on the index from the list of unique drawing attributes if (drawingAttributesBlockDecoded) { if (oldDrawingAttributesTableIndex != drawingAttributesTableIndex) { if (_drawingAttributesTable.Count <= drawingAttributesTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); oldDrawingAttributesTableIndex = drawingAttributesTableIndex; } DrawingAttributes currDA = (DrawingAttributes)_drawingAttributesTable[(int)drawingAttributesTableIndex]; //we always clone so we don't get strokes that share DAs, which can lead //to all sorts of unpredictable behavior (ex: see Windows OS Bugs 1450047) activeDrawingAttributes = currDA.Clone(); } // if we didn't find an existing da to use, instance a new one if (activeDrawingAttributes == null) { activeDrawingAttributes = new DrawingAttributes(); } // Now create the StylusPacketDescription from the stroke descriptor and metric block if (oldMetricDescriptorTableIndex != metricDescriptorTableIndex || oldStrokeDescriptorTableIndex != strokeDescriptorTableIndex) { currentStylusPointDescription = BuildStylusPointDescription(strokeDescriptor, metricBlock, guidList); oldStrokeDescriptorTableIndex = strokeDescriptorTableIndex; oldMetricDescriptorTableIndex = metricDescriptorTableIndex; } // Load the stroke Stroke localStroke; #if OLD_ISF localBytesDecoded = StrokeSerializer.DecodeStroke(inputStream, bytesDecodedInCurrentTag, guidList, strokeDescriptor, currentStylusPointDescription, activeDrawingAttributes, currentTabletToInkTransform, compressor, out localStroke); #else localBytesDecoded = StrokeSerializer.DecodeStroke(inputStream, bytesDecodedInCurrentTag, guidList, strokeDescriptor, currentStylusPointDescription, activeDrawingAttributes, currentTabletToInkTransform, out localStroke); #endif if (localStroke != null) { _coreStrokes.AddWithoutEvent(localStroke); strokeIndex++; } break; } default: { throw new InvalidOperationException(ISFDebugMessage("Invalid ISF tag logic")); } } // if this isfTag's decoded size != expected size, then error out if (localBytesDecoded != bytesDecodedInCurrentTag) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } break; } case KnownTagCache.KnownTagIndex.Transform: case KnownTagCache.KnownTagIndex.TransformIsotropicScale: case KnownTagCache.KnownTagIndex.TransformAnisotropicScale: case KnownTagCache.KnownTagIndex.TransformRotate: case KnownTagCache.KnownTagIndex.TransformTranslate: case KnownTagCache.KnownTagIndex.TransformScaleAndTranslate: { // Load a single Transform Block TransformDescriptor xform; bytesDecodedInCurrentTag = DecodeTransformBlock(inputStream, isfTag, remainingBytesInStream, false, out xform); transformDecoded = true; _transformTable.Clear(); _transformTable.Add(xform); break; } case KnownTagCache.KnownTagIndex.TransformTableIndex: { // Load the Index into the Transform Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out transformTableIndex); break; } case KnownTagCache.KnownTagIndex.MetricTableIndex: { // Load the Index into the Metric Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out metricDescriptorTableIndex); break; } case KnownTagCache.KnownTagIndex.DrawingAttributesTableIndex: { // Load the Index into the Drawing Attributes Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out drawingAttributesTableIndex); break; } case KnownTagCache.KnownTagIndex.InkSpaceRectangle: { // Loads the Ink Space Rectangle information bytesDecodedInCurrentTag = DecodeInkSpaceRectangle(inputStream, remainingBytesInStream); break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorTableIndex: { // Load the Index into the Stroke Descriptor Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out strokeDescriptorTableIndex); break; } default: { if ((uint)isfTag >= KnownIdCache.CustomGuidBaseIndex || ((uint)isfTag >= KnownTagCache.KnownTagCount && ((uint)isfTag < (KnownTagCache.KnownTagCount + KnownIdCache.OriginalISFIdTable.Length)))) { ISFDebugTrace(" CUSTOM_GUID=" + guidList.FindGuid(isfTag).ToString()); // Loads any custom property data bytesDecodedInCurrentTag = remainingBytesInStream; Guid guid = guidList.FindGuid(isfTag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Global Custom Attribute tag embedded in ISF stream does not match guid table"), "inkdata"); } object data; // load the custom property data from the stream (and decode the type) localBytesDecoded = ExtendedPropertySerializer.DecodeAsISF(inputStream, bytesDecodedInCurrentTag, guidList, isfTag, ref guid, out data); if (localBytesDecoded > bytesDecodedInCurrentTag) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } // add the guid/data pair into the property collection (don't redecode the type) _coreStrokes.ExtendedProperties[guid] = data; } else { // Skip objects that this library doesn't know about // First read the size associated with this unknown isfTag localBytesDecoded = SerializationHelper.Decode(inputStream, out bytesDecodedInCurrentTag); if (remainingBytesInStream < (localBytesDecoded + bytesDecodedInCurrentTag)) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } else { inputStream.Seek(bytesDecodedInCurrentTag + localBytesDecoded, SeekOrigin.Current); } } bytesDecodedInCurrentTag = localBytesDecoded; break; } } ISFDebugTrace(" Size = " + bytesDecodedInCurrentTag.ToString()); if (bytesDecodedInCurrentTag > remainingBytesInStream) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } // update remaining ISF buffer length with decoded so far remainingBytesInStream -= bytesDecodedInCurrentTag; } #if OLD_ISF } finally { if (null != compressor) { compressor.Dispose(); compressor = null; } } #endif if (0 != remainingBytesInStream) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } #if OLD_ISF /// /// Loads a DrawingAttributes Table from the stream and adds individual drawing attributes to the drawattr /// list passed /// ////// /// Critical - Calls the DrawingAttributeSerializer.DecodeAsISF critical method /// [SecurityCritical] #else ////// Loads a DrawingAttributes Table from the stream and adds individual drawing attributes to the drawattr /// list passed /// #endif private uint LoadDrawAttrsTable(Stream strm, GuidList guidList, uint cbSize) { _drawingAttributesTable.Clear(); // First, allocate a temporary buffer and read the stream into it. // These will be compressed DRAW_ATTR structures. uint cbTotal = cbSize; // OK, now we count the number of DRAW_ATTRS compressed into this block uint cbDA = 0; while (cbTotal > 0) { // First read the size of the first drawing attributes block uint cb = SerializationHelper.Decode(strm, out cbDA); if (cbSize < cb) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; if (cbTotal < cbDA) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); // Create a new drawing attribute DrawingAttributes attributes = new DrawingAttributes(); // pull off our defaults onthe drawing attribute as we need to // respect what the ISF has. attributes.DrawingFlags = 0; cb = DrawingAttributeSerializer.DecodeAsISF(strm, guidList, cbDA, attributes); // Load the stream into this attribute if (cbSize < cbDA) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cbDA; // Add this attribute to the global list _drawingAttributesTable.Add(attributes); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } ////// Reads and Decodes a stroke descriptor information from the stream. For details on how it is stored /// please refer the spec /// /// /// /// ///private uint DecodeStrokeDescriptor(Stream strm, uint cbSize, out StrokeDescriptor descr) { descr = new StrokeDescriptor(); if (0 == cbSize) return 0; uint cb; uint cbBlock = cbSize; while (cbBlock > 0) { // first read the tag KnownTagCache.KnownTagIndex tag; uint uiTag; cb = SerializationHelper.Decode(strm, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; descr.Template.Add(tag); // If this is TAG_BUTTONS if (KnownTagCache.KnownTagIndex.Buttons == tag && cbBlock > 0) { uint cbButton; // Read the no. of buttons first cb = SerializationHelper.Decode(strm, out cbButton); if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; descr.Template.Add((KnownTagCache.KnownTagIndex)cbButton); while (cbBlock > 0 && cbButton > 0) { uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; cbButton--; descr.Template.Add((KnownTagCache.KnownTagIndex)dw); } } else if (KnownTagCache.KnownTagIndex.StrokePropertyList == tag && cbBlock > 0) { // Usually stroke property comes last in the template. Hence everything below this is // are Tags for strokes extended properties while (cbBlock > 0) { uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; descr.Template.Add((KnownTagCache.KnownTagIndex)dw); } } } if (0 != cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Reads and Decodes a stroke descriptor information from the stream. For details on how it is stored /// please refer the spec /// /// /// ///private uint DecodeStrokeDescriptorBlock(Stream strm, uint cbSize) { _strokeDescriptorTable.Clear(); if (0 == cbSize) return 0; StrokeDescriptor descr; uint cbRead = DecodeStrokeDescriptor(strm, cbSize, out descr); if (cbRead != cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); _strokeDescriptorTable.Add(descr); return cbRead; } /// /// Reads and Decodes a number of stroke descriptor information from the stream. For details on how they are stored /// please refer the spec /// /// /// ///private uint DecodeStrokeDescriptorTable(Stream strm, uint cbSize) { _strokeDescriptorTable.Clear(); if (0 == cbSize) return 0; uint cb; // Tracks the total no of bytes read from the stream uint cbTotal = cbSize; // Tracks how many more bytes can be read from the stream for the table. Limited by cbSize while (cbTotal > 0) { // First decode the size of the next block uint cbBlock; cb = SerializationHelper.Decode(strm, out cbBlock); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; if (cbBlock > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); StrokeDescriptor descr; cb = DecodeStrokeDescriptor(strm, cbBlock, out descr); if (cb != cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; // Add this stroke descriptor to the list of global stroke descriptors _strokeDescriptorTable.Add(descr); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Decodes metric table from the stream. For information on how they are stored in the stream, please refer to the spec. /// /// /// ///private uint DecodeMetricTable(Stream strm, uint cbSize) { _metricTable.Clear(); if (cbSize == 0) return 0; uint cb; uint cbTotal = cbSize; // This data is a list of Metric block. Each block starts with size of the block. After that it contains an // array of Metric Entries. Each metric enty comprises of size of the entry, tag for the property and the metric // properties. while (cbTotal > 0) { // First read the size of the metric block uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb + dw > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; MetricBlock newblock; cb = DecodeMetricBlock(strm, dw, out newblock); if (cb != dw) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; _metricTable.Add(newblock); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Decodes a Metric Block from the stream. For information on how they are stored in the stream, please refer to the spec. /// /// /// /// ///private uint DecodeMetricBlock(Stream strm, uint cbSize, out MetricBlock block) { // allocate the block block = new MetricBlock(); if (cbSize == 0) return 0; uint cb; uint cbTotal = cbSize; uint size; while (cbTotal > 0) { // First decode the tag for this entry uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; // Next read the size of the metric data cb = SerializationHelper.Decode(strm, out size); if (cb + size > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; // now create new metric entry MetricEntry entry = new MetricEntry(); entry.Tag = (KnownTagCache.KnownTagIndex)dw; byte[] data = new byte[size]; uint bytesRead = StrokeCollectionSerializer.ReliableRead(strm, data, size); cbTotal -= bytesRead; if ( bytesRead != size ) { // Make sure the bytes read are expected. If not, we should bail out. // An exception will be thrown. break; } entry.Data = data; block.AddMetricEntry(entry); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Reads and Decodes a Table of Transform Descriptors from the stream. For information on how they are stored /// in the stream, please refer to the spec. /// /// /// /// ///private uint DecodeTransformTable(Stream strm, uint cbSize, bool useDoubles) { // only clear the transform table if not using doubles // (e.g. first pass through transform table) if (!useDoubles) { _transformTable.Clear(); } if (0 == cbSize) return 0; uint cb; uint cbTotal = cbSize; int tableIndex = 0; while (cbTotal > 0) { KnownTagCache.KnownTagIndex tag; uint uiTag; cb = SerializationHelper.Decode(strm, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; TransformDescriptor xform; cb = DecodeTransformBlock(strm, tag, cbTotal, useDoubles, out xform); cbTotal -= cb; if (useDoubles) { _transformTable[tableIndex] = xform; } else { _transformTable.Add(xform); } tableIndex++; } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// ReliableRead /// /// /// /// ///internal static uint ReliableRead(Stream stream, byte[] buffer, uint requestedCount) { if (stream == null || buffer == null || requestedCount > buffer.Length) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid argument passed to ReliableRead")); } // let's read the whole block into our buffer uint totalBytesRead = 0; while (totalBytesRead < requestedCount) { int bytesRead = stream.Read(buffer, (int)totalBytesRead, (int)(requestedCount - totalBytesRead)); if (bytesRead == 0) { break; } totalBytesRead += (uint)bytesRead; } return totalBytesRead; } /// /// Reads and Decodes a Transfrom Descriptor Block from the stream. For information on how it is stored in the stream, /// please refer to the spec. /// /// /// /// /// /// ///private uint DecodeTransformBlock(Stream strm, KnownTagCache.KnownTagIndex tag, uint cbSize, bool useDoubles, out TransformDescriptor xform) { xform = new TransformDescriptor(); xform.Tag = tag; uint cbRead = 0; uint cbTotal = cbSize; if (0 == cbSize) return 0; // samgeo - Presharp issue // Presharp gives a warning when local IDisposable variables are not closed // in this case, we can't call Dispose since it will also close the underlying stream // which still needs to be read from #pragma warning disable 1634, 1691 #pragma warning disable 6518 BinaryReader bw = new BinaryReader(strm); if (KnownTagCache.KnownTagIndex.TransformRotate == tag) { uint angle; cbRead = SerializationHelper.Decode(strm, out angle); if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); xform.Transform[0] = (double)angle; xform.Size = 1; } else { if (tag == KnownTagCache.KnownTagIndex.TransformIsotropicScale) { xform.Size = 1; } else if (tag == KnownTagCache.KnownTagIndex.TransformAnisotropicScale || tag == KnownTagCache.KnownTagIndex.TransformTranslate) { xform.Size = 2; } else if (tag == KnownTagCache.KnownTagIndex.TransformScaleAndTranslate) { xform.Size = 4; } else { xform.Size = 6; } if (useDoubles) { cbRead = xform.Size * Native.SizeOfDouble; } else { cbRead = xform.Size * Native.SizeOfFloat; } if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); for (int i = 0; i < xform.Size; i++) { if (useDoubles) { xform.Transform[i] = bw.ReadDouble(); } else { xform.Transform[i] = (double)bw.ReadSingle(); } } } return cbRead; #pragma warning restore 6518 #pragma warning restore 1634, 1691 } /// /// Decodes Ink Space Rectangle information from the stream /// /// /// ///private uint DecodeInkSpaceRectangle(Stream strm, uint cbSize) { uint cb, cbRead = 0; uint cbTotal = cbSize; int data; //Left cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.X = data; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); //Top cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.Y = data; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); //Right cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.Width = data - _inkSpaceRectangle.Left; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); //Bottom cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.Height = data - _inkSpaceRectangle.Top; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbRead; } /// /// Creates a Matrix Information structure based on the transform descriptor /// /// ///private Matrix LoadTransform(TransformDescriptor tdrd) { double M00 = 0.0f, M01 = 0.0f, M10 = 0.0f, M11 = 0.0f, M20 = 0.0f, M21 = 0.0f; if (KnownTagCache.KnownTagIndex.TransformIsotropicScale == tdrd.Tag) { M00 = M11 = tdrd.Transform[0]; } else if (KnownTagCache.KnownTagIndex.TransformRotate == tdrd.Tag) { double dAngle = (tdrd.Transform[0] / 100) * (Math.PI / 180); M00 = M11 = Math.Cos(dAngle); M01 = Math.Sin(dAngle); if (M01 == 0.0f && M11 == 1.0f) { //special case for 0 degree rotate transforms //this is identity M10 = 0.0f; } else { M10 = -M11; } } else if (KnownTagCache.KnownTagIndex.TransformAnisotropicScale == tdrd.Tag) { M00 = tdrd.Transform[0]; M11 = tdrd.Transform[1]; } else if (KnownTagCache.KnownTagIndex.TransformTranslate == tdrd.Tag) { M20 = tdrd.Transform[0]; M21 = tdrd.Transform[1]; } else if (KnownTagCache.KnownTagIndex.TransformScaleAndTranslate == tdrd.Tag) { M00 = tdrd.Transform[0]; M11 = tdrd.Transform[1]; M20 = tdrd.Transform[2]; M21 = tdrd.Transform[3]; } else // TAG_TRANSFORM { M00 = tdrd.Transform[0]; M01 = tdrd.Transform[1]; M10 = tdrd.Transform[2]; M11 = tdrd.Transform[3]; M20 = tdrd.Transform[4]; M21 = tdrd.Transform[5]; } return new Matrix(M00, M01, M10, M11, M20, M21); } /// /// Sets the Property Metrics for a property based on Tag and metric descriptor block /// /// /// /// ///private StylusPointPropertyInfo GetStylusPointPropertyInfo(Guid guid, KnownTagCache.KnownTagIndex tag, MetricBlock block) { int dw = 0; bool fSetDefault = false; uint cbEntry; // StylusPointPropertyInfo values that we need to read in. int minimum = 0; int maximum = 0; StylusPointPropertyUnit unit = StylusPointPropertyUnit.None; float resolution = 1.0f; // To begin with initialize the property metrics with respective default valuses // first check if this property belongs to optional list for (dw = 0; dw < 11; dw++) { if (MetricEntry.MetricEntry_Optional[dw].Tag == tag) { minimum = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Minimum; maximum = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Maximum; resolution = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Resolution; unit = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Unit; fSetDefault = true; break; } } if (false == fSetDefault) { // We will come here if the property is not found in the Optional List // All other cases, we will have only default values minimum = Int32.MinValue; maximum = Int32.MaxValue; unit = StylusPointPropertyUnit.None; resolution = 1.0f; fSetDefault = true; } // Now see if there is a valid MetricBlock. If there is one, update the PROPERTY_METRICS with // values from this Block if (null != block) { MetricEntry entry = block.GetMetricEntryList(); while (null != entry) { if (entry.Tag == tag) { cbEntry = 0; int range; using (MemoryStream strm = new MemoryStream(entry.Data)) { // Decoded the Logical Min cbEntry += SerializationHelper.SignDecode(strm, out range); if (cbEntry >= entry.Size) { break; // return false; } minimum = range; // Logical Max cbEntry += SerializationHelper.SignDecode(strm, out range); if (cbEntry >= entry.Size) { break; // return false; } maximum = range; uint cb; // Units cbEntry += SerializationHelper.Decode(strm, out cb); unit = (StylusPointPropertyUnit)cb; if (cbEntry >= entry.Size) { break; // return false; } using (BinaryReader br = new BinaryReader(strm)) { resolution = br.ReadSingle(); cbEntry += Native.SizeOfFloat; } } break; } entry = entry.Next; } } // return a new StylusPointPropertyInfo return new StylusPointPropertyInfo( new StylusPointProperty(guid, StylusPointPropertyIds.IsKnownButton(guid)), minimum, maximum, unit, resolution); } /// /// Builds StylusPointDescription based on StrokeDescriptor and Metric Descriptor Block. Sometime Metric Descriptor block may contain /// metric information for properties which are not part of the stroke descriptor. They are simply ignored. /// /// /// /// ///private StylusPointDescription BuildStylusPointDescription(StrokeDescriptor strd, MetricBlock block, GuidList guidList) { int cTags = 0; int packetPropertyCount = 0; uint buttonCount = 0; Guid[] buttonguids = null; System.Collections.Generic.List tags = null; // if strd is null, it means there is only default descriptor with X & Y if (null != strd) { tags = new System.Collections.Generic.List (); while (cTags < strd.Template.Count) { KnownTagCache.KnownTagIndex tag = (KnownTagCache.KnownTagIndex)strd.Template[cTags]; if (KnownTagCache.KnownTagIndex.Buttons == tag) { cTags++; // The next item in the array is no of buttongs. buttonCount = (uint)strd.Template[cTags]; cTags++; // Currently we skip the the no of buttons as buttons is not implimented yet buttonguids = new Guid[buttonCount]; for (uint u = 0; u < buttonCount; u++) { Guid guid = guidList.FindGuid(strd.Template[cTags]); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Button guid tag embedded in ISF stream does not match guid table"),"strd"); } buttonguids[(int)u] = guid; cTags++; } } else if (KnownTagCache.KnownTagIndex.StrokePropertyList == tag) { break; // since no more Packet properties can be stored } else { if (KnownTagCache.KnownTagIndex.NoX == tag || KnownTagCache.KnownTagIndex.NoY == tag) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF with NoX or NoY specified"), "strd"); } tags.Add(strd.Template[cTags]); packetPropertyCount++; cTags++; } } } List stylusPointPropertyInfos = new List (); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.X, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.X), block)); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.Y, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.Y), block)); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.NormalPressure, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.NormalPressure), block)); int pressureIndex = -1; if (tags != null) { for (int i = 0; i < tags.Count; i++) { Guid guid = guidList.FindGuid(tags[i]); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Packet Description Property tag embedded in ISF stream does not match guid table"), "strd"); } if (pressureIndex == -1 && guid == StylusPointPropertyIds.NormalPressure) { pressureIndex = i + 2; //x,y have already been accounted for continue; //we've already added pressure (above) } stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(guid, tags[i], block)); } if (null != buttonguids) { // // add the buttons to the end of the description if they exist // for (int i = 0; i < buttonguids.Length; i++) { StylusPointProperty buttonProperty = new StylusPointProperty(buttonguids[i], true); StylusPointPropertyInfo buttonInfo = new StylusPointPropertyInfo(buttonProperty); stylusPointPropertyInfos.Add(buttonInfo); } } } return new StylusPointDescription(stylusPointPropertyInfos, pressureIndex); } #endregion #endregion // Decoding #region Encoding #region Public Methods #if OLD_ISF /// /// This functions Saves the Ink as Ink Serialized Format based on the Compression code /// ///A byte[] with the encoded ISF ////// Critical - Calls critical methods: /// StrokeCollectionSerializer.SaveStrokeIds /// ExtendedPropertySerializer.EncodeAsISF /// StrokeCollectionSerializer.StoreStrokeData /// /// /// TreatAsSafe - We're saving a StrokeCollection and we control and verify /// all of the data the StrokeCollection directly and indirectly contains /// /// This codepath calls into unmanaged code in Compressor.CompressPacketData /// and Compressor.CompressPropertyData. The underlying unmanaged code has been /// security reviewed and fuzzed /// /// [SecurityCritical, SecurityTreatAsSafe] #else ////// This functions Saves the Ink as Ink Serialized Format based on the Compression code /// ///A byte[] with the encoded ISF #endif internal void EncodeISF(Stream outputStream) { _strokeLookupTable = new System.Collections.Generic.Dictionary(_coreStrokes.Count); // Next go through all the strokes for (int i = 0; i < _coreStrokes.Count; i++) { _strokeLookupTable.Add(_coreStrokes[i], new StrokeLookupEntry()); } // Initialize all Arraylists _strokeDescriptorTable = new List (_coreStrokes.Count); _drawingAttributesTable = new List (); _metricTable = new List (); _transformTable = new List (); using (MemoryStream localStream = new MemoryStream(_coreStrokes.Count * 125)) //reasonable default { GuidList guidList = BuildGuidList(); uint cumulativeEncodedSize = 0; uint localEncodedSize = 0; byte xpData = (CurrentCompressionMode == CompressionMode.NoCompression) ? AlgoModule.NoCompression : AlgoModule.DefaultCompression; foreach (Stroke s in _coreStrokes) { _strokeLookupTable[s].CompressionData = xpData; // // we need to get this data up front so that we can // know if pressure was used (and thus if we need to add Pressure // to the ISF packet description // int[][] isfReadyData; bool shouldStorePressure; s.StylusPoints.ToISFReadyArrays(out isfReadyData, out shouldStorePressure); _strokeLookupTable[s].ISFReadyStrokeData = isfReadyData; // // this is our flag that ToISFReadyArrays sets if pressure was all default // _strokeLookupTable[s].StorePressure = shouldStorePressure; } // Store Ink space rectangle information if necessary and anything other than default if (_inkSpaceRectangle != new Rect()) { localEncodedSize = cumulativeEncodedSize; Rect inkSpaceRectangle = _inkSpaceRectangle; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.InkSpaceRectangle); int i = (int)inkSpaceRectangle.Left; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); i = (int)inkSpaceRectangle.Top; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); i = (int)inkSpaceRectangle.Right; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); i = (int)inkSpaceRectangle.Bottom; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); // validate that the expected inkspace rectangle block in ISF was the actual size encoded localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded InkSpaceRectangle: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // First prepare the compressor. Currently Compression is not supported. // Next write the persistence format information if anything other than ISF // Currently only ISF is implemented if (PersistenceFormat.InkSerializedFormat != CurrentPersistenceFormat) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.PersistenceFormat); cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)SerializationHelper.VarSize((uint)CurrentPersistenceFormat)); cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)CurrentPersistenceFormat); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded PersistenceFormat: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // Store any size information if necessary such as GIF image size // NTRAID#T2-00000-2004/03/15-[....]: WORK: Not Yet Implemented // Now store the Custom Guids localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += guidList.Save(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Custom Guid Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Now build the tables BuildTables(guidList); // first write the drawing attributes localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializeDrawingAttrsTable(localStream, guidList); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded DrawingAttributesTable: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Next write the stroke descriptor table localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializePacketDescrTable(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Packet Description: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Write the metric table localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializeMetricTable(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Metric Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Write the transform table localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializeTransformTable(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Transform Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Save global ink properties if (_coreStrokes.ExtendedProperties.Count > 0) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += ExtendedPropertySerializer.EncodeAsISF(_coreStrokes.ExtendedProperties, localStream, guidList, GetCompressionAlgorithm(), true); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Global Ink Attributes Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // Save stroke ids localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SaveStrokeIds(_coreStrokes, localStream, false); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Stroke Id List: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); StoreStrokeData(localStream, guidList, ref cumulativeEncodedSize, ref localEncodedSize); ISFDebugTrace("Embedded ISF Stream size=" + cumulativeEncodedSize); // Now that all data has been written we need to prepend the stream long preEncodingPosition = outputStream.Position; uint cbFinal = SerializationHelper.Encode(outputStream, (uint)0x00); cbFinal += SerializationHelper.Encode(outputStream, cumulativeEncodedSize); //we have to use localStream to encode ISF because we have to place a variable byte 'size of isf' at the //beginning of the stream outputStream.Write(localStream.GetBuffer(), 0, (int)cumulativeEncodedSize); cbFinal += cumulativeEncodedSize; ISFDebugTrace("Final ISF Stream size=" + cbFinal); if (cbFinal != outputStream.Position - preEncodingPosition) { throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } } } #if OLD_ISF /// /// Encodes all of the strokes in a strokecollection to ISF /// ////// Critical - Calls the critical method StrokeSerializer.EncodeStroke /// /// This directly called by StrokeCollectionSerializer.EncodeISF /// /// TreatAsSafe boundary is StrokeCollectionSerializer.EncodeISF /// /// [SecurityCritical] #else ////// Encodes all of the strokes in a strokecollection to ISF /// #endif private void StoreStrokeData(Stream localStream, GuidList guidList, ref uint cumulativeEncodedSize, ref uint localEncodedSize) { // Now we will save the stroke data uint currentDrawingAttributesTableIndex = 0; uint currentStrokeDescriptorTableIndex = 0; uint uCurrMetricDescriptorTableIndex = 0; uint currentTransformTableIndex = 0; int[] strokeIds = StrokeIdGenerator.GetStrokeIds(_coreStrokes); for (int i = 0; i < _coreStrokes.Count; i++) { Stroke s = _coreStrokes[i]; uint cbStroke = 0; ISFDebugTrace("Encoding Stroke Id#" + strokeIds[i]); // if the drawing attribute index is different from the current one, write it if (currentDrawingAttributesTableIndex != _strokeLookupTable[s].DrawingAttributesTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].DrawingAttributesTableIndex); currentDrawingAttributesTableIndex = _strokeLookupTable[s].DrawingAttributesTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded DrawingAttribute Table Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the stroke descriptor index is different from the current one, write it if (currentStrokeDescriptorTableIndex != _strokeLookupTable[s].StrokeDescriptorTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].StrokeDescriptorTableIndex); currentStrokeDescriptorTableIndex = _strokeLookupTable[s].StrokeDescriptorTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Stroke Descriptor Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the metric table index is different from the current one, write it if (uCurrMetricDescriptorTableIndex != _strokeLookupTable[s].MetricDescriptorTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.MetricTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].MetricDescriptorTableIndex); uCurrMetricDescriptorTableIndex = _strokeLookupTable[s].MetricDescriptorTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Metric Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the Transform index is different from the current one, write it if (currentTransformTableIndex != _strokeLookupTable[s].TransformTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.TransformTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].TransformTableIndex); currentTransformTableIndex = _strokeLookupTable[s].TransformTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Transform Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // now create a separate Memory Stream object which will be used for storing the saved stroke data temporarily using (MemoryStream tempstrm = new MemoryStream(s.StylusPoints.Count * 5)) //good approximation based on profiling isf files { localEncodedSize = cumulativeEncodedSize; #if OLD_ISF // Now save the stroke in the temp stream cbStroke = StrokeSerializer.EncodeStroke(s, tempstrm, null/*we never use CompressionMode.Max)*/, GetCompressionAlgorithm(), guidList, _strokeLookupTable[s]); #else cbStroke = StrokeSerializer.EncodeStroke(s, tempstrm, GetCompressionAlgorithm(), guidList, _strokeLookupTable[s]); #endif if (cbStroke != tempstrm.Length) { throw new InvalidOperationException(ISFDebugMessage("Encoded stroke size != reported size")); } // Now write the tag KnownTagCache.KnownTagIndex.Stroke cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.Stroke); ISFDebugTrace("Stroke size=" + tempstrm.Length); // Now write the size of the stroke cumulativeEncodedSize += SerializationHelper.Encode(localStream, cbStroke); // Finally write the stroke data localStream.Write(tempstrm.GetBuffer(), 0, (int)cbStroke); cumulativeEncodedSize += cbStroke; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoding Stroke Id#" + strokeIds[i] + " size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } } #if OLD_ISF ////// Saves the stroke Ids in the stream. /// /// /// /// save ids even if they are contiguous ////// /// Critical - Calls the critical method Compressor.CompressPacketData /// /// This directly called by StrokeCollectionSerializer.EncodeISF /// /// TreatAsSafe boundary is StrokeCollectionSerializer.EncodeISF /// /// [SecurityCritical] #else ////// Saves the stroke Ids in the stream. /// /// /// /// save ids even if they are contiguous #endif internal static uint SaveStrokeIds(StrokeCollection strokes, Stream strm, bool forceSave) { if (0 == strokes.Count) return 0; // Define an ArrayList to store the stroke ids int[] strkIds = StrokeIdGenerator.GetStrokeIds(strokes); // First enumerate all strokes to collect the ids and also check if the follow the default sequence. // If they do we don't save the stroke ids bool fDefIds = true; if (!forceSave) { // since the stroke allocation algorithm is i++, we check if any // values are not equal to the sequential and consecutive list for (int i = 0; i < strkIds.Length; i++) { if (strkIds[i] != (i + 1)) { // if non-sequential or non-consecutive, then persist the ids fDefIds = false; break; } } // no need to store them if all of them follow the default sequence if (fDefIds) return 0; } // The format is as follows //// Encode size of stroke count // First write the KnownTagCache.KnownTagIndex.StrokeIds uint cbWrote = SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.StrokeIds); ISFDebugTrace("Saved KnownTagCache.KnownTagIndex.StrokeIds size=" + cbWrote.ToString()); // First findout the no of bytes required to huffman compress these ids byte algorithm = AlgoModule.DefaultCompression; #if OLD_ISF byte[] data = Compressor.CompressPacketData(null, strkIds, ref algorithm); #else byte[] data = Compressor.CompressPacketData(strkIds, ref algorithm); #endif if (data != null) { // First write the encoded size of the buffer cbWrote += SerializationHelper.Encode(strm, (uint)(data.Length + SerializationHelper.VarSize((uint)strokes.Count))); // Write the count of ids cbWrote += SerializationHelper.Encode(strm, (uint)strokes.Count); strm.Write(data, 0, (int)data.Length); cbWrote += (uint)data.Length; } // If compression fails for some reason, write the uncompressed data else { byte bCompAlgo = AlgoModule.NoCompression; // Find out the size of the data + size of the id count uint cbStrokeId = (uint)(strokes.Count * Native.SizeOfInt + 1 + SerializationHelper.VarSize((uint)strokes.Count)); // 1 is for the compression header cbWrote += SerializationHelper.Encode(strm, cbStrokeId); cbWrote += SerializationHelper.Encode(strm, (uint)strokes.Count); strm.WriteByte(bCompAlgo); cbWrote++; // Now write all the ids in the stream // samgeo - Presharp issue // Presharp gives a warning when local IDisposable variables are not closed // in this case, we can't call Dispose since it will also close the underlying stream // which still needs to be written to #pragma warning disable 1634, 1691 #pragma warning disable 6518 BinaryWriter bw = new BinaryWriter(strm); for (int i = 0; i < strkIds.Length; i++) { bw.Write(strkIds[i]); cbWrote += Native.SizeOfInt; } #pragma warning restore 6518 #pragma warning restore 1634, 1691 } return cbWrote; } #endregion #region Private Methods /// /// Simple helper method to examine the first 7 members (if they exist) /// of the byte[] and see if they have the ascii characters 'base64:' in them. /// /// ///private bool IsBase64Data(Stream data) { Debug.Assert(data != null); long currentPosition = data.Position; try { byte[] isfBase64PrefixBytes = Base64HeaderBytes; if (data.Length < isfBase64PrefixBytes.Length) { return false; } for (int x = 0; x < isfBase64PrefixBytes.Length; x++) { if ((byte)data.ReadByte() != isfBase64PrefixBytes[x]) { return false; } } return true; } finally { //reset position data.Position = currentPosition; } } /// /// Builds the GuidList based on ExtendedPropeties and StrokeCollection /// ///private GuidList BuildGuidList() { GuidList guidList = new GuidList(); int i = 0; // First go through the list of ink properties ExtendedPropertyCollection attributes = _coreStrokes.ExtendedProperties; for (i = 0; i < attributes.Count; i++) { guidList.Add(attributes[i].Id); } // Next go through all the strokes for (int j = 0; j < _coreStrokes.Count; j++) { BuildStrokeGuidList(_coreStrokes[j], guidList); } return guidList; } /// /// Builds the list of Custom Guids that were used by this particular stroke, either in the packet layout /// or in the drawing attributes, or in the buttons or in Extended properties or in the point properties /// and updates the guidlist with that information /// /// /// private void BuildStrokeGuidList(Stroke stroke, GuidList guidList) { int i = 0; // First drawing attributes // Ignore the default Guids/attributes in the DrawingAttributes int count; Guid[] guids = ExtendedPropertySerializer.GetUnknownGuids(stroke.DrawingAttributes.ExtendedProperties, out count); for (i = 0; i < count; i++) { guidList.Add(guids[i]); } Guid[] descriptionGuids = stroke.StylusPoints.Description.GetStylusPointPropertyIds(); for (i = 0; i < descriptionGuids.Length; i++) { guidList.Add(descriptionGuids[i]); } if (stroke.ExtendedProperties.Count > 0) { // Add the ExtendedProperty guids in the list for (i = 0; i < stroke.ExtendedProperties.Count; i++) { guidList.Add(stroke.ExtendedProperties[i].Id); } } } private byte GetCompressionAlgorithm() { if (CompressionMode.Compressed == CurrentCompressionMode) { return AlgoModule.DefaultCompression; } return AlgoModule.NoCompression; } ////// This function serializes Stroke Descriptor Table in the stream. For information on how they are serialized, please refer to the spec. /// /// /// ///private uint SerializePacketDescrTable(Stream strm) { if (_strokeDescriptorTable.Count == 0) return 0; int count = 0; uint cbData = 0; // First add the appropriate header information if (_strokeDescriptorTable.Count == 1) { StrokeDescriptor tmp = _strokeDescriptorTable[0]; // If there is no tag, that means default template and only one entry in the list. Return from here if (tmp.Template.Count == 0) return 0; else { // Write it out directly // First the tag cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorBlock); // Now encode the descriptor itself cbData += EncodeStrokeDescriptor(strm, tmp); } } else { uint cbTotal = 0; // First calculate the total encoded size of the all the Templates for (count = 0; count < _strokeDescriptorTable.Count; count++) { cbTotal += SerializationHelper.VarSize((_strokeDescriptorTable[count]).Size) + (_strokeDescriptorTable[count]).Size; } // Now write the Tag cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorTable); cbData += SerializationHelper.Encode(strm, cbTotal); // Now write the encoded templates for (count = 0; count < _strokeDescriptorTable.Count; count++) { cbData += EncodeStrokeDescriptor(strm, _strokeDescriptorTable[count]); } } return cbData; } /// /// This function serializes Metric Descriptor Table in the stream. For information on how they are serialized, please refer to the spec. /// /// ///private uint SerializeMetricTable(Stream strm) { uint cSize = 0; MetricBlock block; if (0 == _metricTable.Count) return 0; for (int i = 0; i < _metricTable.Count; i++) cSize += _metricTable[i].Size; uint cbData = 0; // if total size of the blocks is 1, then there is nothing to write // the reason that the size of the blocks is 1 instead of 0 is because // MetricBlock.Size returns the size of the block plus the byte encoded // size value itself. If the MetricBlock size value is 0, then byte // encoded size value is 0, which has a byte size of 1. if (1 == cSize) { return 0; } else if (1 == _metricTable.Count) { cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.MetricBlock); } else { cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.MetricTable); cbData += SerializationHelper.Encode(strm, cSize); } for (int i = 0; i < _metricTable.Count; i++) { block = _metricTable[i]; cbData += block.Pack(strm); } return cbData; } /// /// Multibyte Encodes a Stroke Descroptor /// /// /// ///private uint EncodeStrokeDescriptor(Stream strm, StrokeDescriptor strd) { uint cbData = 0; // First encode the size of the descriptor cbData += SerializationHelper.Encode(strm, strd.Size); for (int count = 0; count < strd.Template.Count; count++) { // Now encode all members of the descriptor cbData += SerializationHelper.Encode(strm, (uint)strd.Template[count]); } return cbData; } /// /// This function serializes Transform Descriptor Table in the stream. For information on how they are serialized, please refer to the spec. /// /// ///private uint SerializeTransformTable(Stream strm) { // If there is only one entry in the TransformDescriptor table // and it is the default descriptor, skip serialization of transforms if (_transformTable.Count == 1 && _transformTable[0].Size == 0) { return 0; } uint floatTotal = 0; uint doubleTotal = 0; // First count the size of all transforms (handling both float && double versions) for (int i = 0; i < _transformTable.Count; i++) { TransformDescriptor xform = _transformTable[i]; uint cbLocal = SerializationHelper.VarSize((uint)xform.Tag); floatTotal += cbLocal; doubleTotal += cbLocal; if (KnownTagCache.KnownTagIndex.TransformRotate == xform.Tag) { cbLocal = SerializationHelper.VarSize((uint)(xform.Transform[0] + 0.5f)); floatTotal += cbLocal; doubleTotal += cbLocal; } else { cbLocal = xform.Size * Native.SizeOfFloat; floatTotal += cbLocal; doubleTotal += cbLocal * 2; } } uint cbTotal = 0; // If there is only one entry in the TransformDescriptor table if (_transformTable.Count == 1) { TransformDescriptor xform = _transformTable[0]; cbTotal = EncodeTransformDescriptor(strm, xform, false); } else { // Now first write the block descriptor and then write all transforms cbTotal += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.TransformTable); cbTotal += SerializationHelper.Encode(strm, floatTotal); for (int i = 0; i < _transformTable.Count; i++) { cbTotal += EncodeTransformDescriptor(strm, _transformTable[i], false); } } // now write the Extended Transform table (using doubles instead of floats) { // note that we do not distinguish between 1 and > 1 transforms for compression // Now first write the block descriptor and then write all transforms cbTotal += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.ExtendedTransformTable); cbTotal += SerializationHelper.Encode(strm, doubleTotal); for (int i = 0; i < _transformTable.Count; i++) { cbTotal += EncodeTransformDescriptor(strm, _transformTable[i], true); } } return cbTotal; } /// /// Multibyte Encode if necessary a Transform Descriptor into the stream /// /// /// /// ///private uint EncodeTransformDescriptor(Stream strm, TransformDescriptor xform, bool useDoubles) { uint cbData = 0; // First encode the tag cbData = SerializationHelper.Encode(strm, (uint)xform.Tag); // Encode specially if transform denotes rotation if (KnownTagCache.KnownTagIndex.TransformRotate == xform.Tag) { uint angle = (uint)(xform.Transform[0] + 0.5f); cbData += SerializationHelper.Encode(strm, angle); } else { // samgeo - Presharp issue // Presharp gives a warning when local IDisposable variables are not closed // in this case, we can't call Dispose since it will also close the underlying stream // which still needs to be written to #pragma warning disable 1634, 1691 #pragma warning disable 6518 BinaryWriter bw = new BinaryWriter(strm); for (int i = 0; i < xform.Size; i++) { // note that the binary writer changes serialization // lengths depending on the Write parameter cast if (useDoubles) { bw.Write(xform.Transform[i]); cbData += Native.SizeOfDouble; } else { bw.Write((float)xform.Transform[i]); cbData += Native.SizeOfFloat; } } #pragma warning restore 6518 #pragma warning restore 1634, 1691 } return cbData; } #if OLD_ISF /// /// This function serializes Drawing Attributes Table in the stream. For information on how they are serialized, please refer to the spec. /// /// /// ////// /// Critical - Calls the critical method /// DrawingAttributeSerializer.EncodeAsISF /// /// This directly called by StrokeCollectionSerializer.EncodeISF /// /// TreatAsSafe boundary is StrokeCollectionSerializer.EncodeISF /// /// [SecurityCritical] #else ////// This function serializes Drawing Attributes Table in the stream. For information on how they are serialized, please refer to the spec. /// /// /// #endif private uint SerializeDrawingAttrsTable(Stream stream, GuidList guidList) { uint totalSizeOfSerializedBytes = 0; uint sizeOfHeaderInBytes = 0; if (1 == _drawingAttributesTable.Count) { //we always serialize a single DA, even if it has default values so we will write width back to the stream DrawingAttributes drawingAttributes = _drawingAttributesTable[0]; // There is single drawing attribute. Save it along with the size totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesBlock); // Get the size of the saved bytes using (MemoryStream drawingAttributeStream = new MemoryStream(16)) //reasonable default based onn profiling { sizeOfHeaderInBytes = DrawingAttributeSerializer.EncodeAsISF(drawingAttributes, drawingAttributeStream, guidList, 0, true); // Write the size first totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizeOfHeaderInBytes); // write the data uint bytesWritten = Convert.ToUInt32(drawingAttributeStream.Position); totalSizeOfSerializedBytes += bytesWritten; Debug.Assert(sizeOfHeaderInBytes == bytesWritten); stream.Write( drawingAttributeStream.GetBuffer(), //returns a direct ref, no copied 0, Convert.ToInt32(bytesWritten)); drawingAttributeStream.Dispose(); } } else { // Temporarily declare an array to hold the size of the saved drawing attributes uint[] sizes = new uint[_drawingAttributesTable.Count]; MemoryStream[] drawingAttributeStreams = new MemoryStream[_drawingAttributesTable.Count]; // First calculate the size of each attribute for (int i = 0; i < _drawingAttributesTable.Count; i++) { DrawingAttributes drawingAttributes = _drawingAttributesTable[i]; drawingAttributeStreams[i] = new MemoryStream(16); //reasonable default based on profiling sizes[i] = DrawingAttributeSerializer.EncodeAsISF(drawingAttributes, drawingAttributeStreams[i], guidList, 0, true); sizeOfHeaderInBytes += SerializationHelper.VarSize(sizes[i]) + sizes[i]; } // Now write the KnownTagCache.KnownTagIndex.DrawingAttributesTable first, then sizeOfHeaderInBytes and then individual Drawing Attributes totalSizeOfSerializedBytes = SerializationHelper.Encode(stream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesTable); totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizeOfHeaderInBytes); for (int i = 0; i < _drawingAttributesTable.Count; i++) { DrawingAttributes drawingAttributes = _drawingAttributesTable[i]; // write the size of the block totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizes[i]); // write the saved data uint bytesWritten = Convert.ToUInt32(drawingAttributeStreams[i].Position); totalSizeOfSerializedBytes += bytesWritten; Debug.Assert(sizes[i] == bytesWritten); stream.Write( drawingAttributeStreams[i].GetBuffer(), //returns a direct ref, no copies 0, Convert.ToInt32(bytesWritten)); drawingAttributeStreams[i].Dispose(); } } return totalSizeOfSerializedBytes; } ////// This function builds list of all unique Tables, ie Stroke Descriptor Table, Metric Descriptor Table, Transform Descriptor Table /// and Drawing Attributes Table based on all the strokes. Each entry in the Table is unique with respect to the table. /// /// private void BuildTables(GuidList guidList) { _transformTable.Clear(); _strokeDescriptorTable.Clear(); _metricTable.Clear(); _drawingAttributesTable.Clear(); int count = 0; for (count = 0; count < _coreStrokes.Count; count++) { Stroke stroke = _coreStrokes[count]; // First get the updated descriptor from the stroke StrokeDescriptor strokeDescriptor; MetricBlock metricBlock; StrokeSerializer.BuildStrokeDescriptor(stroke, guidList, _strokeLookupTable[stroke], out strokeDescriptor, out metricBlock); bool fMatch = false; // Compare this with all the global stroke descriptor for a match for (int descriptorIndex = 0; descriptorIndex < _strokeDescriptorTable.Count; descriptorIndex++) { if (strokeDescriptor.IsEqual(_strokeDescriptorTable[descriptorIndex])) { fMatch = true; _strokeLookupTable[stroke].StrokeDescriptorTableIndex = (uint)descriptorIndex; break; } } if (false == fMatch) { _strokeDescriptorTable.Add(strokeDescriptor); _strokeLookupTable[stroke].StrokeDescriptorTableIndex = (uint)_strokeDescriptorTable.Count - 1; } // If there is at least one entry in the metric block, check if the current Block is equvalent to // any of the existing one. fMatch = false; for (int tmp = 0; tmp < _metricTable.Count; tmp++) { MetricBlock block = _metricTable[tmp]; SetType type = SetType.SubSet; if (block.CompareMetricBlock(metricBlock, ref type)) { // This entry exists in the list. If it is a subset of the element, do nothing. // Otherwise, replace the entry with this one if (type == SetType.SuperSet) { _metricTable[tmp] = metricBlock; } fMatch = true; _strokeLookupTable[stroke].MetricDescriptorTableIndex = (uint)tmp; break; } } if (false == fMatch) { _metricTable.Add(metricBlock); _strokeLookupTable[stroke].MetricDescriptorTableIndex = (uint)(_metricTable.Count - 1); } // Now build the Transform Table fMatch = false; // // always identity // TransformDescriptor xform = StrokeCollectionSerializer.IdentityTransformDescriptor; // First check to see if this matches with any existing Transform Blocks for (int i = 0; i < _transformTable.Count; i++) { if (true == xform.Compare(_transformTable[i])) { fMatch = true; _strokeLookupTable[stroke].TransformTableIndex = (uint)i; break; } } if (false == fMatch) { _transformTable.Add(xform); _strokeLookupTable[stroke].TransformTableIndex = (uint)(_transformTable.Count - 1); } // Now build the drawing attributes table fMatch = false; DrawingAttributes drattrs = _coreStrokes[count].DrawingAttributes; // First check to see if this matches with any existing transform blocks for (int i = 0; i < _drawingAttributesTable.Count; i++) { if (true == drattrs.Equals(_drawingAttributesTable[i])) { fMatch = true; _strokeLookupTable[stroke].DrawingAttributesTableIndex = (uint)i; break; } } if (false == fMatch) { _drawingAttributesTable.Add(drattrs); _strokeLookupTable[stroke].DrawingAttributesTableIndex = (uint)_drawingAttributesTable.Count - 1; } } } #endregion // Private Methods internal class StrokeLookupEntry { internal uint MetricDescriptorTableIndex = 0; internal uint StrokeDescriptorTableIndex = 0; internal uint TransformTableIndex = 0; internal uint DrawingAttributesTableIndex = 0; // Compression algorithm data internal byte CompressionData = 0; internal int[][] ISFReadyStrokeData = null; internal bool StorePressure = false; } #endregion // Encoding #region Debugging Methods [System.Diagnostics.Conditional("DEBUG_ISF")] static void ISFDebugTrace(string message) { System.Diagnostics.Debug.WriteLine(message); } #endregion // [System.Diagnostics.Conditional("DEBUG_ISF")] internal static string ISFDebugMessage(string debugMessage) { #if DEBUG return debugMessage; #else return SR.Get(SRID.IsfOperationFailed); #endif } #region Private Fields StrokeCollection _coreStrokes; private System.Collections.Generic.List_strokeDescriptorTable = null; private System.Collections.Generic.List _transformTable = null; private System.Collections.Generic.List _drawingAttributesTable = null; private System.Collections.Generic.List _metricTable = null; private Vector _himetricSize = new Vector(0.0f, 0.0f); // The ink space rectangle (e.g. bounding box for GIF) is stored // with the serialization info so that load/save roundtrip the // rectangle private Rect _inkSpaceRectangle = new Rect(); System.Collections.Generic.Dictionary _strokeLookupTable = null; #endregion } /// /// Simple static method for generating StrokeIds /// internal static class StrokeIdGenerator { ////// Generates backwards compatible StrokeID's for the strokes /// /// strokes ///internal static int[] GetStrokeIds(StrokeCollection strokes) { System.Diagnostics.Debug.Assert(strokes != null); int[] strokeIds = new int[strokes.Count]; for (int x = 0; x < strokeIds.Length; x++) { //stroke ID's are 1 based (1,2,3...) strokeIds[x] = x + 1; } return strokeIds; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //#define OLD_ISF //------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------- using MS.Utility; using System; using System.Diagnostics; using System.Security; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.IO; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Security.Permissions; using System.Runtime.Serialization; using System.Collections; using System.Collections.Generic; using System.Windows.Input; using System.Windows.Ink; using MS.Internal.IO.Packaging; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace MS.Internal.Ink.InkSerializedFormat { internal class StrokeCollectionSerializer { #region Constants (Static Fields) internal static readonly double AvalonToHimetricMultiplier = 2540F / 96.0F; internal static readonly double HimetricToAvalonMultiplier = 96.0F / 2540.0F; internal static readonly TransformDescriptor IdentityTransformDescriptor; static StrokeCollectionSerializer() { TransformDescriptor transformDescriptor = new TransformDescriptor(); transformDescriptor.Transform[0] = 1.0f; transformDescriptor.Tag = KnownTagCache.KnownTagIndex.TransformIsotropicScale; transformDescriptor.Size = 1; StrokeCollectionSerializer.IdentityTransformDescriptor = transformDescriptor; } #endregion #region Constructors // disable default constructor private StrokeCollectionSerializer() { } ////// Initialize the Ink serializer /// /// Pointer to the core stroke collection - avoids recreation of collections internal StrokeCollectionSerializer(StrokeCollection coreStrokes) { _coreStrokes = coreStrokes; } #endregion #region Public Fields internal PersistenceFormat CurrentPersistenceFormat = PersistenceFormat.InkSerializedFormat; internal CompressionMode CurrentCompressionMode = CompressionMode.Compressed; internal System.Collections.Generic.ListStrokeIds = null; #endregion #region Decoding #region Public Methods /// /// Loads a Ink object from a spcified byte array in the form of Ink Serialzied Format /// This method checks for the 'base64:' prefix in the byte[] because that is how V1 /// saved ISF /// /// internal void DecodeISF(Stream inkData) { try { // First examine the input data header bool isBase64; bool isGif; uint cbData; ExamineStreamHeader(inkData, out isBase64, out isGif, out cbData); if (isBase64) { // // this is a funky tablet v1 based byte[] that is base64 encoded... // each 4 bytes in this array corresponds to 3 bytes of ISF data. // EXCEPT the first 7 bytes which are saved with the value // 'base64:' and must not be base64 decoded. // and the last null terminator (if present) // // The following code does two things: // 1) Convert each byte to a char so it can be base64 decoded // 2) Strips out the first 7 resulting characters // int isfBase64PrefixLength = Base64HeaderBytes.Length; // the previous call to ExamineStreamHeader guarantees that inkData.Length > isfBase64PrefixLength System.Diagnostics.Debug.Assert(inkData.Length > isfBase64PrefixLength); inkData.Position = (long)isfBase64PrefixLength; ListcharData = new List ((int)inkData.Length); int intByte = inkData.ReadByte(); while (intByte != -1) { byte b = (byte)intByte; charData.Add((char)b); intByte = inkData.ReadByte(); } if (0 == (byte)(charData[charData.Count - 1])) { //strip the null terminator charData.RemoveAt(charData.Count - 1); } char[] chars = charData.ToArray(); byte[] isfData = Convert.FromBase64CharArray(chars, 0, chars.Length); MemoryStream ms = new MemoryStream(isfData); if (IsGIFData(ms)) { DecodeRawISF(ReadGifData(ms)); } else { DecodeRawISF(ms); } } else if (true == isGif) { DecodeRawISF(ReadGifData(inkData)); } else { DecodeRawISF(inkData); } } #if DEBUG catch (ArgumentException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (InvalidOperationException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (IndexOutOfRangeException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (NullReferenceException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (EndOfStreamException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } catch (OverflowException ex) { //only include an inner exception in debug builds throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), ex); } #else catch (ArgumentException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (InvalidOperationException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (IndexOutOfRangeException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (NullReferenceException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (EndOfStreamException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } catch (OverflowException) { throw new ArgumentException(SR.Get(SRID.IsfOperationFailed), "stream");//stream comes from StrokeCollection.ctor() } #endif } #endregion #region Private Methods /// /// Loads the strokeIds from the stream, we need to do this to decrement the count of bytes /// internal uint LoadStrokeIds(Stream isfStream, uint cbSize) { if (0 == cbSize) return 0; uint cb; uint cbTotal = cbSize; // First decode the no of ids uint count; cb = SerializationHelper.Decode(isfStream, out count); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "isfStream"); cbTotal -= cb; if (0 == count) return (cbSize - cbTotal); cb = cbTotal; byte[] inputdata = new byte[cb]; // read the stream uint bytesRead = StrokeCollectionSerializer.ReliableRead(isfStream, inputdata, cb); if (cb != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "isfStream"); } cbTotal -= cb; if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "isfStream"); return cbSize; } private bool IsGIFData(Stream inkdata) { Debug.Assert(inkdata != null); long currentPosition = inkdata.Position; try { return ((byte)inkdata.ReadByte() == 'G' && (byte)inkdata.ReadByte() == 'I' && (byte)inkdata.ReadByte() == 'F'); } finally { //reset position inkdata.Position = currentPosition; } } private Stream ReadGifData(Stream inkdata) { // Read the GIF header ... System.Drawing.Bitmap img = new System.Drawing.Bitmap(inkdata); // Read the comment as that is where the ISF is stored... // for reference the tag is PropertyTagExifUserComment [0x9286] or 37510 (int) System.Drawing.Imaging.PropertyItem piComment = img.GetPropertyItem(37510); return new MemoryStream(piComment.Value); } private void ExamineStreamHeader(Stream inkdata, out bool fBase64, out bool fGif, out uint cbData) { fGif = false; cbData = 0; fBase64 = false; if (inkdata.Length >= 7) { fBase64 = IsBase64Data(inkdata); } // Check for RAW gif if (!fBase64 && inkdata.Length >= 3) { fGif = IsGIFData(inkdata); } return; } private static readonly byte[] Base64HeaderBytes = new byte[]{(byte)'b', (byte)'a', (byte)'s', (byte)'e', (byte)'6', (byte)'4', (byte)':'}; #if OLD_ISF ////// Takes an ISF byte[] and populates the StrokeCollection /// attached to this StrokeCollectionSerializer. /// /// a byte[] of the raw isf to decode ////// Critical - Calls critical methods in Compressor /// TreatAsSafe - inkdata is a disconnected copy from the one passed in /// underlying unmanaged code has been security reviewed and fuzzed /// [SecurityCritical, SecurityTreatAsSafe] #else ////// Takes an ISF Stream and populates the StrokeCollection /// attached to this StrokeCollectionSerializer. /// /// a Stream the raw isf to decode #endif private void DecodeRawISF(Stream inputStream) { Debug.Assert(inputStream != null); KnownTagCache.KnownTagIndex isfTag; uint remainingBytesInStream; uint bytesDecodedInCurrentTag = 0; bool strokeDescriptorBlockDecoded = false; bool drawingAttributesBlockDecoded = false; bool metricBlockDecoded = false; bool transformDecoded = false; uint strokeDescriptorTableIndex = 0; uint oldStrokeDescriptorTableIndex = 0xFFFFFFFF; uint drawingAttributesTableIndex = 0; uint oldDrawingAttributesTableIndex = 0xFFFFFFFF; uint metricDescriptorTableIndex = 0; uint oldMetricDescriptorTableIndex = 0xFFFFFFFF; uint transformTableIndex = 0; uint oldTransformTableIndex = 0xFFFFFFFF; GuidList guidList = new GuidList(); int strokeIndex = 0; StylusPointDescription currentStylusPointDescription = null; Matrix currentTabletToInkTransform = Matrix.Identity; _strokeDescriptorTable = new System.Collections.Generic.List(); _drawingAttributesTable = new System.Collections.Generic.List (); _transformTable = new System.Collections.Generic.List (); _metricTable = new System.Collections.Generic.List (); // First make sure this ink is empty if (0 != _coreStrokes.Count || _coreStrokes.ExtendedProperties.Count != 0) { throw new InvalidOperationException(ISFDebugMessage("ISF decoder cannot operate on non-empty ink container")); } #if OLD_ISF // // store a compressor reference at this scope, if it is needed (if there is a compresson header) and // therefore instanced during this routine, we will dispose of it // in the finally block // Compressor compressor = null; try { #endif // First read the isfTag uint uiTag; uint localBytesDecoded = SerializationHelper.Decode(inputStream, out uiTag); if (0x00 != uiTag) throw new ArgumentException(SR.Get(SRID.InvalidStream)); // Now read the size of the stream localBytesDecoded = SerializationHelper.Decode(inputStream, out remainingBytesInStream); ISFDebugTrace("Decoded Stream Size in Bytes: " + remainingBytesInStream.ToString()); if (0 == remainingBytesInStream) return; while (0 < remainingBytesInStream) { bytesDecodedInCurrentTag = 0; // First read the isfTag localBytesDecoded = SerializationHelper.Decode(inputStream, out uiTag); isfTag = (KnownTagCache.KnownTagIndex)uiTag; if (remainingBytesInStream >= localBytesDecoded) remainingBytesInStream -= localBytesDecoded; else { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } ISFDebugTrace("Decoding Tag: " + ((KnownTagCache.KnownTagIndex)isfTag).ToString()); switch (isfTag) { case KnownTagCache.KnownTagIndex.GuidTable: case KnownTagCache.KnownTagIndex.DrawingAttributesTable: case KnownTagCache.KnownTagIndex.DrawingAttributesBlock: case KnownTagCache.KnownTagIndex.StrokeDescriptorTable: case KnownTagCache.KnownTagIndex.StrokeDescriptorBlock: case KnownTagCache.KnownTagIndex.MetricTable: case KnownTagCache.KnownTagIndex.MetricBlock: case KnownTagCache.KnownTagIndex.TransformTable: case KnownTagCache.KnownTagIndex.ExtendedTransformTable: case KnownTagCache.KnownTagIndex.Stroke: case KnownTagCache.KnownTagIndex.CompressionHeader: case KnownTagCache.KnownTagIndex.PersistenceFormat: case KnownTagCache.KnownTagIndex.HimetricSize: case KnownTagCache.KnownTagIndex.StrokeIds: { localBytesDecoded = SerializationHelper.Decode(inputStream, out bytesDecodedInCurrentTag); if (remainingBytesInStream < (localBytesDecoded + bytesDecodedInCurrentTag)) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } remainingBytesInStream -= localBytesDecoded; // Based on the isfTag figure out what information we're loading switch (isfTag) { case KnownTagCache.KnownTagIndex.GuidTable: { // Load guid Table localBytesDecoded = guidList.Load(inputStream, bytesDecodedInCurrentTag); break; } case KnownTagCache.KnownTagIndex.DrawingAttributesTable: { // Load drawing attributes table localBytesDecoded = LoadDrawAttrsTable(inputStream, guidList, bytesDecodedInCurrentTag); drawingAttributesBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.DrawingAttributesBlock: { //initialize to V1 defaults, we do it this way as opposed //to dr.DrawingFlags = 0 because this was a perf hot spot //and instancing the epc first mitigates it ExtendedPropertyCollection epc = new ExtendedPropertyCollection(); epc.Add(KnownIds.DrawingFlags, DrawingFlags.Polyline); DrawingAttributes dr = new DrawingAttributes(epc); localBytesDecoded = DrawingAttributeSerializer.DecodeAsISF(inputStream, guidList, bytesDecodedInCurrentTag, dr); _drawingAttributesTable.Add(dr); drawingAttributesBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorTable: { // Load stroke descriptor table localBytesDecoded = DecodeStrokeDescriptorTable(inputStream, bytesDecodedInCurrentTag); strokeDescriptorBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorBlock: { // Load a single stroke descriptor localBytesDecoded = DecodeStrokeDescriptorBlock(inputStream, bytesDecodedInCurrentTag); strokeDescriptorBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.MetricTable: { // Load Metric Table localBytesDecoded = DecodeMetricTable(inputStream, bytesDecodedInCurrentTag); metricBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.MetricBlock: { // Load a single Metric Block MetricBlock blk; localBytesDecoded = DecodeMetricBlock(inputStream, bytesDecodedInCurrentTag, out blk); _metricTable.Clear(); _metricTable.Add(blk); metricBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.TransformTable: { // Load Transform Table localBytesDecoded = DecodeTransformTable(inputStream, bytesDecodedInCurrentTag, false); transformDecoded = true; break; } case KnownTagCache.KnownTagIndex.ExtendedTransformTable: { // non-double transform table should have already been loaded if (!transformDecoded) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } // Load double-sized Transform Table localBytesDecoded = DecodeTransformTable(inputStream, bytesDecodedInCurrentTag, true); break; } case KnownTagCache.KnownTagIndex.PersistenceFormat: { uint fmt; localBytesDecoded = SerializationHelper.Decode(inputStream, out fmt); // Set the appropriate persistence information if (0 == fmt) { CurrentPersistenceFormat = PersistenceFormat.InkSerializedFormat; } else if (0x00000001 == fmt) { CurrentPersistenceFormat = PersistenceFormat.Gif; } break; } case KnownTagCache.KnownTagIndex.HimetricSize: { // Loads the Hi Metric Size for Fortified GIFs int sz; localBytesDecoded = SerializationHelper.SignDecode(inputStream, out sz); if (localBytesDecoded > remainingBytesInStream) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); _himetricSize.X = (double)sz; localBytesDecoded += SerializationHelper.SignDecode(inputStream, out sz); _himetricSize.Y = (double)sz; break; } case KnownTagCache.KnownTagIndex.CompressionHeader: { #if OLD_ISF byte[] data = new byte[bytesDecodedInCurrentTag]; // read the header from the stream uint bytesRead = StrokeCollectionSerializer.ReliableRead(inputStream, data, bytesDecodedInCurrentTag); if (bytesDecodedInCurrentTag != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "isfStream"); } uint size = bytesDecodedInCurrentTag; compressor = new Compressor(data, ref size); // in case the actual number of bytes read by the compressor // is less than the encoder had expected (e.g. compression // header was encoded as 10 bytes, but only 7 bytes were read) // then we don't want to adjust the stream position because // there are likely other following tags that are encoded // after the compression tag. This should never happen, // so just fail if the compressor is broken or the ISF is // corrupted. if (size != bytesDecodedInCurrentTag) { throw new InvalidOperationException(ISFDebugMessage("Compressor intialization reported inconsistent size")); } #else //just advance the inputstream position, we don't need //no compression header in the new isf decoding inputStream.Seek(bytesDecodedInCurrentTag, SeekOrigin.Current); #endif localBytesDecoded = bytesDecodedInCurrentTag; break; } case KnownTagCache.KnownTagIndex.StrokeIds: { localBytesDecoded = LoadStrokeIds(inputStream, bytesDecodedInCurrentTag); break; } case KnownTagCache.KnownTagIndex.Stroke: { ISFDebugTrace(" Decoding Stroke Id#(" + (strokeIndex + 1).ToString() + ")"); StrokeDescriptor strokeDescriptor = null; // Load the stroke descriptor based on the index from the list of unique // stroke descriptors if (strokeDescriptorBlockDecoded) { if (oldStrokeDescriptorTableIndex != strokeDescriptorTableIndex) { if (_strokeDescriptorTable.Count <= strokeDescriptorTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } strokeDescriptor = _strokeDescriptorTable[(int)strokeDescriptorTableIndex]; } // use new transform if the last transform is uninit'd or has changed if (oldTransformTableIndex != transformTableIndex) { // if transform was specified in the ISF stream if (transformDecoded) { if (_transformTable.Count <= transformTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); // Load the transform descriptor based on the index from the list of unique // transforn descriptors currentTabletToInkTransform = LoadTransform(_transformTable[(int)transformTableIndex]); } oldTransformTableIndex = transformTableIndex; // cache the transform by remembering the index // since ISF is stored in HIMETRIC, and we want to expose packet data // as Avalon units, we'll update the convert the transform before loading the stroke currentTabletToInkTransform.Scale(StrokeCollectionSerializer.HimetricToAvalonMultiplier, StrokeCollectionSerializer.HimetricToAvalonMultiplier); } MetricBlock metricBlock = null; // Load the metric block based on the index from the list of unique metric blocks if (metricBlockDecoded) { if (oldMetricDescriptorTableIndex != metricDescriptorTableIndex) { if (_metricTable.Count <= metricDescriptorTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } metricBlock = _metricTable[(int)metricDescriptorTableIndex]; } DrawingAttributes activeDrawingAttributes = null; // Load the drawing attributes based on the index from the list of unique drawing attributes if (drawingAttributesBlockDecoded) { if (oldDrawingAttributesTableIndex != drawingAttributesTableIndex) { if (_drawingAttributesTable.Count <= drawingAttributesTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); oldDrawingAttributesTableIndex = drawingAttributesTableIndex; } DrawingAttributes currDA = (DrawingAttributes)_drawingAttributesTable[(int)drawingAttributesTableIndex]; //we always clone so we don't get strokes that share DAs, which can lead //to all sorts of unpredictable behavior (ex: see Windows OS Bugs 1450047) activeDrawingAttributes = currDA.Clone(); } // if we didn't find an existing da to use, instance a new one if (activeDrawingAttributes == null) { activeDrawingAttributes = new DrawingAttributes(); } // Now create the StylusPacketDescription from the stroke descriptor and metric block if (oldMetricDescriptorTableIndex != metricDescriptorTableIndex || oldStrokeDescriptorTableIndex != strokeDescriptorTableIndex) { currentStylusPointDescription = BuildStylusPointDescription(strokeDescriptor, metricBlock, guidList); oldStrokeDescriptorTableIndex = strokeDescriptorTableIndex; oldMetricDescriptorTableIndex = metricDescriptorTableIndex; } // Load the stroke Stroke localStroke; #if OLD_ISF localBytesDecoded = StrokeSerializer.DecodeStroke(inputStream, bytesDecodedInCurrentTag, guidList, strokeDescriptor, currentStylusPointDescription, activeDrawingAttributes, currentTabletToInkTransform, compressor, out localStroke); #else localBytesDecoded = StrokeSerializer.DecodeStroke(inputStream, bytesDecodedInCurrentTag, guidList, strokeDescriptor, currentStylusPointDescription, activeDrawingAttributes, currentTabletToInkTransform, out localStroke); #endif if (localStroke != null) { _coreStrokes.AddWithoutEvent(localStroke); strokeIndex++; } break; } default: { throw new InvalidOperationException(ISFDebugMessage("Invalid ISF tag logic")); } } // if this isfTag's decoded size != expected size, then error out if (localBytesDecoded != bytesDecodedInCurrentTag) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } break; } case KnownTagCache.KnownTagIndex.Transform: case KnownTagCache.KnownTagIndex.TransformIsotropicScale: case KnownTagCache.KnownTagIndex.TransformAnisotropicScale: case KnownTagCache.KnownTagIndex.TransformRotate: case KnownTagCache.KnownTagIndex.TransformTranslate: case KnownTagCache.KnownTagIndex.TransformScaleAndTranslate: { // Load a single Transform Block TransformDescriptor xform; bytesDecodedInCurrentTag = DecodeTransformBlock(inputStream, isfTag, remainingBytesInStream, false, out xform); transformDecoded = true; _transformTable.Clear(); _transformTable.Add(xform); break; } case KnownTagCache.KnownTagIndex.TransformTableIndex: { // Load the Index into the Transform Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out transformTableIndex); break; } case KnownTagCache.KnownTagIndex.MetricTableIndex: { // Load the Index into the Metric Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out metricDescriptorTableIndex); break; } case KnownTagCache.KnownTagIndex.DrawingAttributesTableIndex: { // Load the Index into the Drawing Attributes Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out drawingAttributesTableIndex); break; } case KnownTagCache.KnownTagIndex.InkSpaceRectangle: { // Loads the Ink Space Rectangle information bytesDecodedInCurrentTag = DecodeInkSpaceRectangle(inputStream, remainingBytesInStream); break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorTableIndex: { // Load the Index into the Stroke Descriptor Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out strokeDescriptorTableIndex); break; } default: { if ((uint)isfTag >= KnownIdCache.CustomGuidBaseIndex || ((uint)isfTag >= KnownTagCache.KnownTagCount && ((uint)isfTag < (KnownTagCache.KnownTagCount + KnownIdCache.OriginalISFIdTable.Length)))) { ISFDebugTrace(" CUSTOM_GUID=" + guidList.FindGuid(isfTag).ToString()); // Loads any custom property data bytesDecodedInCurrentTag = remainingBytesInStream; Guid guid = guidList.FindGuid(isfTag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Global Custom Attribute tag embedded in ISF stream does not match guid table"), "inkdata"); } object data; // load the custom property data from the stream (and decode the type) localBytesDecoded = ExtendedPropertySerializer.DecodeAsISF(inputStream, bytesDecodedInCurrentTag, guidList, isfTag, ref guid, out data); if (localBytesDecoded > bytesDecodedInCurrentTag) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } // add the guid/data pair into the property collection (don't redecode the type) _coreStrokes.ExtendedProperties[guid] = data; } else { // Skip objects that this library doesn't know about // First read the size associated with this unknown isfTag localBytesDecoded = SerializationHelper.Decode(inputStream, out bytesDecodedInCurrentTag); if (remainingBytesInStream < (localBytesDecoded + bytesDecodedInCurrentTag)) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } else { inputStream.Seek(bytesDecodedInCurrentTag + localBytesDecoded, SeekOrigin.Current); } } bytesDecodedInCurrentTag = localBytesDecoded; break; } } ISFDebugTrace(" Size = " + bytesDecodedInCurrentTag.ToString()); if (bytesDecodedInCurrentTag > remainingBytesInStream) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } // update remaining ISF buffer length with decoded so far remainingBytesInStream -= bytesDecodedInCurrentTag; } #if OLD_ISF } finally { if (null != compressor) { compressor.Dispose(); compressor = null; } } #endif if (0 != remainingBytesInStream) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } #if OLD_ISF /// /// Loads a DrawingAttributes Table from the stream and adds individual drawing attributes to the drawattr /// list passed /// ////// /// Critical - Calls the DrawingAttributeSerializer.DecodeAsISF critical method /// [SecurityCritical] #else ////// Loads a DrawingAttributes Table from the stream and adds individual drawing attributes to the drawattr /// list passed /// #endif private uint LoadDrawAttrsTable(Stream strm, GuidList guidList, uint cbSize) { _drawingAttributesTable.Clear(); // First, allocate a temporary buffer and read the stream into it. // These will be compressed DRAW_ATTR structures. uint cbTotal = cbSize; // OK, now we count the number of DRAW_ATTRS compressed into this block uint cbDA = 0; while (cbTotal > 0) { // First read the size of the first drawing attributes block uint cb = SerializationHelper.Decode(strm, out cbDA); if (cbSize < cb) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; if (cbTotal < cbDA) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); // Create a new drawing attribute DrawingAttributes attributes = new DrawingAttributes(); // pull off our defaults onthe drawing attribute as we need to // respect what the ISF has. attributes.DrawingFlags = 0; cb = DrawingAttributeSerializer.DecodeAsISF(strm, guidList, cbDA, attributes); // Load the stream into this attribute if (cbSize < cbDA) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cbDA; // Add this attribute to the global list _drawingAttributesTable.Add(attributes); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } ////// Reads and Decodes a stroke descriptor information from the stream. For details on how it is stored /// please refer the spec /// /// /// /// ///private uint DecodeStrokeDescriptor(Stream strm, uint cbSize, out StrokeDescriptor descr) { descr = new StrokeDescriptor(); if (0 == cbSize) return 0; uint cb; uint cbBlock = cbSize; while (cbBlock > 0) { // first read the tag KnownTagCache.KnownTagIndex tag; uint uiTag; cb = SerializationHelper.Decode(strm, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; descr.Template.Add(tag); // If this is TAG_BUTTONS if (KnownTagCache.KnownTagIndex.Buttons == tag && cbBlock > 0) { uint cbButton; // Read the no. of buttons first cb = SerializationHelper.Decode(strm, out cbButton); if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; descr.Template.Add((KnownTagCache.KnownTagIndex)cbButton); while (cbBlock > 0 && cbButton > 0) { uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; cbButton--; descr.Template.Add((KnownTagCache.KnownTagIndex)dw); } } else if (KnownTagCache.KnownTagIndex.StrokePropertyList == tag && cbBlock > 0) { // Usually stroke property comes last in the template. Hence everything below this is // are Tags for strokes extended properties while (cbBlock > 0) { uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb > cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbBlock -= cb; descr.Template.Add((KnownTagCache.KnownTagIndex)dw); } } } if (0 != cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Reads and Decodes a stroke descriptor information from the stream. For details on how it is stored /// please refer the spec /// /// /// ///private uint DecodeStrokeDescriptorBlock(Stream strm, uint cbSize) { _strokeDescriptorTable.Clear(); if (0 == cbSize) return 0; StrokeDescriptor descr; uint cbRead = DecodeStrokeDescriptor(strm, cbSize, out descr); if (cbRead != cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); _strokeDescriptorTable.Add(descr); return cbRead; } /// /// Reads and Decodes a number of stroke descriptor information from the stream. For details on how they are stored /// please refer the spec /// /// /// ///private uint DecodeStrokeDescriptorTable(Stream strm, uint cbSize) { _strokeDescriptorTable.Clear(); if (0 == cbSize) return 0; uint cb; // Tracks the total no of bytes read from the stream uint cbTotal = cbSize; // Tracks how many more bytes can be read from the stream for the table. Limited by cbSize while (cbTotal > 0) { // First decode the size of the next block uint cbBlock; cb = SerializationHelper.Decode(strm, out cbBlock); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; if (cbBlock > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); StrokeDescriptor descr; cb = DecodeStrokeDescriptor(strm, cbBlock, out descr); if (cb != cbBlock) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; // Add this stroke descriptor to the list of global stroke descriptors _strokeDescriptorTable.Add(descr); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Decodes metric table from the stream. For information on how they are stored in the stream, please refer to the spec. /// /// /// ///private uint DecodeMetricTable(Stream strm, uint cbSize) { _metricTable.Clear(); if (cbSize == 0) return 0; uint cb; uint cbTotal = cbSize; // This data is a list of Metric block. Each block starts with size of the block. After that it contains an // array of Metric Entries. Each metric enty comprises of size of the entry, tag for the property and the metric // properties. while (cbTotal > 0) { // First read the size of the metric block uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb + dw > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; MetricBlock newblock; cb = DecodeMetricBlock(strm, dw, out newblock); if (cb != dw) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; _metricTable.Add(newblock); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Decodes a Metric Block from the stream. For information on how they are stored in the stream, please refer to the spec. /// /// /// /// ///private uint DecodeMetricBlock(Stream strm, uint cbSize, out MetricBlock block) { // allocate the block block = new MetricBlock(); if (cbSize == 0) return 0; uint cb; uint cbTotal = cbSize; uint size; while (cbTotal > 0) { // First decode the tag for this entry uint dw; cb = SerializationHelper.Decode(strm, out dw); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; // Next read the size of the metric data cb = SerializationHelper.Decode(strm, out size); if (cb + size > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; // now create new metric entry MetricEntry entry = new MetricEntry(); entry.Tag = (KnownTagCache.KnownTagIndex)dw; byte[] data = new byte[size]; uint bytesRead = StrokeCollectionSerializer.ReliableRead(strm, data, size); cbTotal -= bytesRead; if ( bytesRead != size ) { // Make sure the bytes read are expected. If not, we should bail out. // An exception will be thrown. break; } entry.Data = data; block.AddMetricEntry(entry); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// Reads and Decodes a Table of Transform Descriptors from the stream. For information on how they are stored /// in the stream, please refer to the spec. /// /// /// /// ///private uint DecodeTransformTable(Stream strm, uint cbSize, bool useDoubles) { // only clear the transform table if not using doubles // (e.g. first pass through transform table) if (!useDoubles) { _transformTable.Clear(); } if (0 == cbSize) return 0; uint cb; uint cbTotal = cbSize; int tableIndex = 0; while (cbTotal > 0) { KnownTagCache.KnownTagIndex tag; uint uiTag; cb = SerializationHelper.Decode(strm, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; TransformDescriptor xform; cb = DecodeTransformBlock(strm, tag, cbTotal, useDoubles, out xform); cbTotal -= cb; if (useDoubles) { _transformTable[tableIndex] = xform; } else { _transformTable.Add(xform); } tableIndex++; } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; } /// /// ReliableRead /// /// /// /// ///internal static uint ReliableRead(Stream stream, byte[] buffer, uint requestedCount) { if (stream == null || buffer == null || requestedCount > buffer.Length) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid argument passed to ReliableRead")); } // let's read the whole block into our buffer uint totalBytesRead = 0; while (totalBytesRead < requestedCount) { int bytesRead = stream.Read(buffer, (int)totalBytesRead, (int)(requestedCount - totalBytesRead)); if (bytesRead == 0) { break; } totalBytesRead += (uint)bytesRead; } return totalBytesRead; } /// /// Reads and Decodes a Transfrom Descriptor Block from the stream. For information on how it is stored in the stream, /// please refer to the spec. /// /// /// /// /// /// ///private uint DecodeTransformBlock(Stream strm, KnownTagCache.KnownTagIndex tag, uint cbSize, bool useDoubles, out TransformDescriptor xform) { xform = new TransformDescriptor(); xform.Tag = tag; uint cbRead = 0; uint cbTotal = cbSize; if (0 == cbSize) return 0; // samgeo - Presharp issue // Presharp gives a warning when local IDisposable variables are not closed // in this case, we can't call Dispose since it will also close the underlying stream // which still needs to be read from #pragma warning disable 1634, 1691 #pragma warning disable 6518 BinaryReader bw = new BinaryReader(strm); if (KnownTagCache.KnownTagIndex.TransformRotate == tag) { uint angle; cbRead = SerializationHelper.Decode(strm, out angle); if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); xform.Transform[0] = (double)angle; xform.Size = 1; } else { if (tag == KnownTagCache.KnownTagIndex.TransformIsotropicScale) { xform.Size = 1; } else if (tag == KnownTagCache.KnownTagIndex.TransformAnisotropicScale || tag == KnownTagCache.KnownTagIndex.TransformTranslate) { xform.Size = 2; } else if (tag == KnownTagCache.KnownTagIndex.TransformScaleAndTranslate) { xform.Size = 4; } else { xform.Size = 6; } if (useDoubles) { cbRead = xform.Size * Native.SizeOfDouble; } else { cbRead = xform.Size * Native.SizeOfFloat; } if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); for (int i = 0; i < xform.Size; i++) { if (useDoubles) { xform.Transform[i] = bw.ReadDouble(); } else { xform.Transform[i] = (double)bw.ReadSingle(); } } } return cbRead; #pragma warning restore 6518 #pragma warning restore 1634, 1691 } /// /// Decodes Ink Space Rectangle information from the stream /// /// /// ///private uint DecodeInkSpaceRectangle(Stream strm, uint cbSize) { uint cb, cbRead = 0; uint cbTotal = cbSize; int data; //Left cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.X = data; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); //Top cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.Y = data; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); //Right cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.Width = data - _inkSpaceRectangle.Left; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); //Bottom cb = SerializationHelper.SignDecode(strm, out data); if (cb > cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; cbRead += cb; _inkSpaceRectangle.Height = data - _inkSpaceRectangle.Top; if (cbRead > cbSize) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbRead; } /// /// Creates a Matrix Information structure based on the transform descriptor /// /// ///private Matrix LoadTransform(TransformDescriptor tdrd) { double M00 = 0.0f, M01 = 0.0f, M10 = 0.0f, M11 = 0.0f, M20 = 0.0f, M21 = 0.0f; if (KnownTagCache.KnownTagIndex.TransformIsotropicScale == tdrd.Tag) { M00 = M11 = tdrd.Transform[0]; } else if (KnownTagCache.KnownTagIndex.TransformRotate == tdrd.Tag) { double dAngle = (tdrd.Transform[0] / 100) * (Math.PI / 180); M00 = M11 = Math.Cos(dAngle); M01 = Math.Sin(dAngle); if (M01 == 0.0f && M11 == 1.0f) { //special case for 0 degree rotate transforms //this is identity M10 = 0.0f; } else { M10 = -M11; } } else if (KnownTagCache.KnownTagIndex.TransformAnisotropicScale == tdrd.Tag) { M00 = tdrd.Transform[0]; M11 = tdrd.Transform[1]; } else if (KnownTagCache.KnownTagIndex.TransformTranslate == tdrd.Tag) { M20 = tdrd.Transform[0]; M21 = tdrd.Transform[1]; } else if (KnownTagCache.KnownTagIndex.TransformScaleAndTranslate == tdrd.Tag) { M00 = tdrd.Transform[0]; M11 = tdrd.Transform[1]; M20 = tdrd.Transform[2]; M21 = tdrd.Transform[3]; } else // TAG_TRANSFORM { M00 = tdrd.Transform[0]; M01 = tdrd.Transform[1]; M10 = tdrd.Transform[2]; M11 = tdrd.Transform[3]; M20 = tdrd.Transform[4]; M21 = tdrd.Transform[5]; } return new Matrix(M00, M01, M10, M11, M20, M21); } /// /// Sets the Property Metrics for a property based on Tag and metric descriptor block /// /// /// /// ///private StylusPointPropertyInfo GetStylusPointPropertyInfo(Guid guid, KnownTagCache.KnownTagIndex tag, MetricBlock block) { int dw = 0; bool fSetDefault = false; uint cbEntry; // StylusPointPropertyInfo values that we need to read in. int minimum = 0; int maximum = 0; StylusPointPropertyUnit unit = StylusPointPropertyUnit.None; float resolution = 1.0f; // To begin with initialize the property metrics with respective default valuses // first check if this property belongs to optional list for (dw = 0; dw < 11; dw++) { if (MetricEntry.MetricEntry_Optional[dw].Tag == tag) { minimum = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Minimum; maximum = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Maximum; resolution = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Resolution; unit = MetricEntry.MetricEntry_Optional[dw].PropertyMetrics.Unit; fSetDefault = true; break; } } if (false == fSetDefault) { // We will come here if the property is not found in the Optional List // All other cases, we will have only default values minimum = Int32.MinValue; maximum = Int32.MaxValue; unit = StylusPointPropertyUnit.None; resolution = 1.0f; fSetDefault = true; } // Now see if there is a valid MetricBlock. If there is one, update the PROPERTY_METRICS with // values from this Block if (null != block) { MetricEntry entry = block.GetMetricEntryList(); while (null != entry) { if (entry.Tag == tag) { cbEntry = 0; int range; using (MemoryStream strm = new MemoryStream(entry.Data)) { // Decoded the Logical Min cbEntry += SerializationHelper.SignDecode(strm, out range); if (cbEntry >= entry.Size) { break; // return false; } minimum = range; // Logical Max cbEntry += SerializationHelper.SignDecode(strm, out range); if (cbEntry >= entry.Size) { break; // return false; } maximum = range; uint cb; // Units cbEntry += SerializationHelper.Decode(strm, out cb); unit = (StylusPointPropertyUnit)cb; if (cbEntry >= entry.Size) { break; // return false; } using (BinaryReader br = new BinaryReader(strm)) { resolution = br.ReadSingle(); cbEntry += Native.SizeOfFloat; } } break; } entry = entry.Next; } } // return a new StylusPointPropertyInfo return new StylusPointPropertyInfo( new StylusPointProperty(guid, StylusPointPropertyIds.IsKnownButton(guid)), minimum, maximum, unit, resolution); } /// /// Builds StylusPointDescription based on StrokeDescriptor and Metric Descriptor Block. Sometime Metric Descriptor block may contain /// metric information for properties which are not part of the stroke descriptor. They are simply ignored. /// /// /// /// ///private StylusPointDescription BuildStylusPointDescription(StrokeDescriptor strd, MetricBlock block, GuidList guidList) { int cTags = 0; int packetPropertyCount = 0; uint buttonCount = 0; Guid[] buttonguids = null; System.Collections.Generic.List tags = null; // if strd is null, it means there is only default descriptor with X & Y if (null != strd) { tags = new System.Collections.Generic.List (); while (cTags < strd.Template.Count) { KnownTagCache.KnownTagIndex tag = (KnownTagCache.KnownTagIndex)strd.Template[cTags]; if (KnownTagCache.KnownTagIndex.Buttons == tag) { cTags++; // The next item in the array is no of buttongs. buttonCount = (uint)strd.Template[cTags]; cTags++; // Currently we skip the the no of buttons as buttons is not implimented yet buttonguids = new Guid[buttonCount]; for (uint u = 0; u < buttonCount; u++) { Guid guid = guidList.FindGuid(strd.Template[cTags]); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Button guid tag embedded in ISF stream does not match guid table"),"strd"); } buttonguids[(int)u] = guid; cTags++; } } else if (KnownTagCache.KnownTagIndex.StrokePropertyList == tag) { break; // since no more Packet properties can be stored } else { if (KnownTagCache.KnownTagIndex.NoX == tag || KnownTagCache.KnownTagIndex.NoY == tag) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF with NoX or NoY specified"), "strd"); } tags.Add(strd.Template[cTags]); packetPropertyCount++; cTags++; } } } List stylusPointPropertyInfos = new List (); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.X, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.X), block)); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.Y, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.Y), block)); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.NormalPressure, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.NormalPressure), block)); int pressureIndex = -1; if (tags != null) { for (int i = 0; i < tags.Count; i++) { Guid guid = guidList.FindGuid(tags[i]); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Packet Description Property tag embedded in ISF stream does not match guid table"), "strd"); } if (pressureIndex == -1 && guid == StylusPointPropertyIds.NormalPressure) { pressureIndex = i + 2; //x,y have already been accounted for continue; //we've already added pressure (above) } stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(guid, tags[i], block)); } if (null != buttonguids) { // // add the buttons to the end of the description if they exist // for (int i = 0; i < buttonguids.Length; i++) { StylusPointProperty buttonProperty = new StylusPointProperty(buttonguids[i], true); StylusPointPropertyInfo buttonInfo = new StylusPointPropertyInfo(buttonProperty); stylusPointPropertyInfos.Add(buttonInfo); } } } return new StylusPointDescription(stylusPointPropertyInfos, pressureIndex); } #endregion #endregion // Decoding #region Encoding #region Public Methods #if OLD_ISF /// /// This functions Saves the Ink as Ink Serialized Format based on the Compression code /// ///A byte[] with the encoded ISF ////// Critical - Calls critical methods: /// StrokeCollectionSerializer.SaveStrokeIds /// ExtendedPropertySerializer.EncodeAsISF /// StrokeCollectionSerializer.StoreStrokeData /// /// /// TreatAsSafe - We're saving a StrokeCollection and we control and verify /// all of the data the StrokeCollection directly and indirectly contains /// /// This codepath calls into unmanaged code in Compressor.CompressPacketData /// and Compressor.CompressPropertyData. The underlying unmanaged code has been /// security reviewed and fuzzed /// /// [SecurityCritical, SecurityTreatAsSafe] #else ////// This functions Saves the Ink as Ink Serialized Format based on the Compression code /// ///A byte[] with the encoded ISF #endif internal void EncodeISF(Stream outputStream) { _strokeLookupTable = new System.Collections.Generic.Dictionary(_coreStrokes.Count); // Next go through all the strokes for (int i = 0; i < _coreStrokes.Count; i++) { _strokeLookupTable.Add(_coreStrokes[i], new StrokeLookupEntry()); } // Initialize all Arraylists _strokeDescriptorTable = new List (_coreStrokes.Count); _drawingAttributesTable = new List (); _metricTable = new List (); _transformTable = new List (); using (MemoryStream localStream = new MemoryStream(_coreStrokes.Count * 125)) //reasonable default { GuidList guidList = BuildGuidList(); uint cumulativeEncodedSize = 0; uint localEncodedSize = 0; byte xpData = (CurrentCompressionMode == CompressionMode.NoCompression) ? AlgoModule.NoCompression : AlgoModule.DefaultCompression; foreach (Stroke s in _coreStrokes) { _strokeLookupTable[s].CompressionData = xpData; // // we need to get this data up front so that we can // know if pressure was used (and thus if we need to add Pressure // to the ISF packet description // int[][] isfReadyData; bool shouldStorePressure; s.StylusPoints.ToISFReadyArrays(out isfReadyData, out shouldStorePressure); _strokeLookupTable[s].ISFReadyStrokeData = isfReadyData; // // this is our flag that ToISFReadyArrays sets if pressure was all default // _strokeLookupTable[s].StorePressure = shouldStorePressure; } // Store Ink space rectangle information if necessary and anything other than default if (_inkSpaceRectangle != new Rect()) { localEncodedSize = cumulativeEncodedSize; Rect inkSpaceRectangle = _inkSpaceRectangle; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.InkSpaceRectangle); int i = (int)inkSpaceRectangle.Left; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); i = (int)inkSpaceRectangle.Top; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); i = (int)inkSpaceRectangle.Right; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); i = (int)inkSpaceRectangle.Bottom; cumulativeEncodedSize += SerializationHelper.SignEncode(localStream, i); // validate that the expected inkspace rectangle block in ISF was the actual size encoded localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded InkSpaceRectangle: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // First prepare the compressor. Currently Compression is not supported. // Next write the persistence format information if anything other than ISF // Currently only ISF is implemented if (PersistenceFormat.InkSerializedFormat != CurrentPersistenceFormat) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.PersistenceFormat); cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)SerializationHelper.VarSize((uint)CurrentPersistenceFormat)); cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)CurrentPersistenceFormat); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded PersistenceFormat: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // Store any size information if necessary such as GIF image size // NTRAID#T2-00000-2004/03/15-[....]: WORK: Not Yet Implemented // Now store the Custom Guids localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += guidList.Save(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Custom Guid Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Now build the tables BuildTables(guidList); // first write the drawing attributes localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializeDrawingAttrsTable(localStream, guidList); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded DrawingAttributesTable: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Next write the stroke descriptor table localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializePacketDescrTable(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Packet Description: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Write the metric table localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializeMetricTable(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Metric Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Write the transform table localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializeTransformTable(localStream); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Transform Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); // Save global ink properties if (_coreStrokes.ExtendedProperties.Count > 0) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += ExtendedPropertySerializer.EncodeAsISF(_coreStrokes.ExtendedProperties, localStream, guidList, GetCompressionAlgorithm(), true); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Global Ink Attributes Table: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // Save stroke ids localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SaveStrokeIds(_coreStrokes, localStream, false); localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoded Stroke Id List: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); StoreStrokeData(localStream, guidList, ref cumulativeEncodedSize, ref localEncodedSize); ISFDebugTrace("Embedded ISF Stream size=" + cumulativeEncodedSize); // Now that all data has been written we need to prepend the stream long preEncodingPosition = outputStream.Position; uint cbFinal = SerializationHelper.Encode(outputStream, (uint)0x00); cbFinal += SerializationHelper.Encode(outputStream, cumulativeEncodedSize); //we have to use localStream to encode ISF because we have to place a variable byte 'size of isf' at the //beginning of the stream outputStream.Write(localStream.GetBuffer(), 0, (int)cumulativeEncodedSize); cbFinal += cumulativeEncodedSize; ISFDebugTrace("Final ISF Stream size=" + cbFinal); if (cbFinal != outputStream.Position - preEncodingPosition) { throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } } } #if OLD_ISF /// /// Encodes all of the strokes in a strokecollection to ISF /// ////// Critical - Calls the critical method StrokeSerializer.EncodeStroke /// /// This directly called by StrokeCollectionSerializer.EncodeISF /// /// TreatAsSafe boundary is StrokeCollectionSerializer.EncodeISF /// /// [SecurityCritical] #else ////// Encodes all of the strokes in a strokecollection to ISF /// #endif private void StoreStrokeData(Stream localStream, GuidList guidList, ref uint cumulativeEncodedSize, ref uint localEncodedSize) { // Now we will save the stroke data uint currentDrawingAttributesTableIndex = 0; uint currentStrokeDescriptorTableIndex = 0; uint uCurrMetricDescriptorTableIndex = 0; uint currentTransformTableIndex = 0; int[] strokeIds = StrokeIdGenerator.GetStrokeIds(_coreStrokes); for (int i = 0; i < _coreStrokes.Count; i++) { Stroke s = _coreStrokes[i]; uint cbStroke = 0; ISFDebugTrace("Encoding Stroke Id#" + strokeIds[i]); // if the drawing attribute index is different from the current one, write it if (currentDrawingAttributesTableIndex != _strokeLookupTable[s].DrawingAttributesTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].DrawingAttributesTableIndex); currentDrawingAttributesTableIndex = _strokeLookupTable[s].DrawingAttributesTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded DrawingAttribute Table Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the stroke descriptor index is different from the current one, write it if (currentStrokeDescriptorTableIndex != _strokeLookupTable[s].StrokeDescriptorTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].StrokeDescriptorTableIndex); currentStrokeDescriptorTableIndex = _strokeLookupTable[s].StrokeDescriptorTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Stroke Descriptor Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the metric table index is different from the current one, write it if (uCurrMetricDescriptorTableIndex != _strokeLookupTable[s].MetricDescriptorTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.MetricTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].MetricDescriptorTableIndex); uCurrMetricDescriptorTableIndex = _strokeLookupTable[s].MetricDescriptorTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Metric Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the Transform index is different from the current one, write it if (currentTransformTableIndex != _strokeLookupTable[s].TransformTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.TransformTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].TransformTableIndex); currentTransformTableIndex = _strokeLookupTable[s].TransformTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Transform Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // now create a separate Memory Stream object which will be used for storing the saved stroke data temporarily using (MemoryStream tempstrm = new MemoryStream(s.StylusPoints.Count * 5)) //good approximation based on profiling isf files { localEncodedSize = cumulativeEncodedSize; #if OLD_ISF // Now save the stroke in the temp stream cbStroke = StrokeSerializer.EncodeStroke(s, tempstrm, null/*we never use CompressionMode.Max)*/, GetCompressionAlgorithm(), guidList, _strokeLookupTable[s]); #else cbStroke = StrokeSerializer.EncodeStroke(s, tempstrm, GetCompressionAlgorithm(), guidList, _strokeLookupTable[s]); #endif if (cbStroke != tempstrm.Length) { throw new InvalidOperationException(ISFDebugMessage("Encoded stroke size != reported size")); } // Now write the tag KnownTagCache.KnownTagIndex.Stroke cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.Stroke); ISFDebugTrace("Stroke size=" + tempstrm.Length); // Now write the size of the stroke cumulativeEncodedSize += SerializationHelper.Encode(localStream, cbStroke); // Finally write the stroke data localStream.Write(tempstrm.GetBuffer(), 0, (int)cbStroke); cumulativeEncodedSize += cbStroke; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoding Stroke Id#" + strokeIds[i] + " size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } } #if OLD_ISF ////// Saves the stroke Ids in the stream. /// /// /// /// save ids even if they are contiguous ////// /// Critical - Calls the critical method Compressor.CompressPacketData /// /// This directly called by StrokeCollectionSerializer.EncodeISF /// /// TreatAsSafe boundary is StrokeCollectionSerializer.EncodeISF /// /// [SecurityCritical] #else ////// Saves the stroke Ids in the stream. /// /// /// /// save ids even if they are contiguous #endif internal static uint SaveStrokeIds(StrokeCollection strokes, Stream strm, bool forceSave) { if (0 == strokes.Count) return 0; // Define an ArrayList to store the stroke ids int[] strkIds = StrokeIdGenerator.GetStrokeIds(strokes); // First enumerate all strokes to collect the ids and also check if the follow the default sequence. // If they do we don't save the stroke ids bool fDefIds = true; if (!forceSave) { // since the stroke allocation algorithm is i++, we check if any // values are not equal to the sequential and consecutive list for (int i = 0; i < strkIds.Length; i++) { if (strkIds[i] != (i + 1)) { // if non-sequential or non-consecutive, then persist the ids fDefIds = false; break; } } // no need to store them if all of them follow the default sequence if (fDefIds) return 0; } // The format is as follows //// Encode size of stroke count // First write the KnownTagCache.KnownTagIndex.StrokeIds uint cbWrote = SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.StrokeIds); ISFDebugTrace("Saved KnownTagCache.KnownTagIndex.StrokeIds size=" + cbWrote.ToString()); // First findout the no of bytes required to huffman compress these ids byte algorithm = AlgoModule.DefaultCompression; #if OLD_ISF byte[] data = Compressor.CompressPacketData(null, strkIds, ref algorithm); #else byte[] data = Compressor.CompressPacketData(strkIds, ref algorithm); #endif if (data != null) { // First write the encoded size of the buffer cbWrote += SerializationHelper.Encode(strm, (uint)(data.Length + SerializationHelper.VarSize((uint)strokes.Count))); // Write the count of ids cbWrote += SerializationHelper.Encode(strm, (uint)strokes.Count); strm.Write(data, 0, (int)data.Length); cbWrote += (uint)data.Length; } // If compression fails for some reason, write the uncompressed data else { byte bCompAlgo = AlgoModule.NoCompression; // Find out the size of the data + size of the id count uint cbStrokeId = (uint)(strokes.Count * Native.SizeOfInt + 1 + SerializationHelper.VarSize((uint)strokes.Count)); // 1 is for the compression header cbWrote += SerializationHelper.Encode(strm, cbStrokeId); cbWrote += SerializationHelper.Encode(strm, (uint)strokes.Count); strm.WriteByte(bCompAlgo); cbWrote++; // Now write all the ids in the stream // samgeo - Presharp issue // Presharp gives a warning when local IDisposable variables are not closed // in this case, we can't call Dispose since it will also close the underlying stream // which still needs to be written to #pragma warning disable 1634, 1691 #pragma warning disable 6518 BinaryWriter bw = new BinaryWriter(strm); for (int i = 0; i < strkIds.Length; i++) { bw.Write(strkIds[i]); cbWrote += Native.SizeOfInt; } #pragma warning restore 6518 #pragma warning restore 1634, 1691 } return cbWrote; } #endregion #region Private Methods /// /// Simple helper method to examine the first 7 members (if they exist) /// of the byte[] and see if they have the ascii characters 'base64:' in them. /// /// ///private bool IsBase64Data(Stream data) { Debug.Assert(data != null); long currentPosition = data.Position; try { byte[] isfBase64PrefixBytes = Base64HeaderBytes; if (data.Length < isfBase64PrefixBytes.Length) { return false; } for (int x = 0; x < isfBase64PrefixBytes.Length; x++) { if ((byte)data.ReadByte() != isfBase64PrefixBytes[x]) { return false; } } return true; } finally { //reset position data.Position = currentPosition; } } /// /// Builds the GuidList based on ExtendedPropeties and StrokeCollection /// ///private GuidList BuildGuidList() { GuidList guidList = new GuidList(); int i = 0; // First go through the list of ink properties ExtendedPropertyCollection attributes = _coreStrokes.ExtendedProperties; for (i = 0; i < attributes.Count; i++) { guidList.Add(attributes[i].Id); } // Next go through all the strokes for (int j = 0; j < _coreStrokes.Count; j++) { BuildStrokeGuidList(_coreStrokes[j], guidList); } return guidList; } /// /// Builds the list of Custom Guids that were used by this particular stroke, either in the packet layout /// or in the drawing attributes, or in the buttons or in Extended properties or in the point properties /// and updates the guidlist with that information /// /// /// private void BuildStrokeGuidList(Stroke stroke, GuidList guidList) { int i = 0; // First drawing attributes // Ignore the default Guids/attributes in the DrawingAttributes int count; Guid[] guids = ExtendedPropertySerializer.GetUnknownGuids(stroke.DrawingAttributes.ExtendedProperties, out count); for (i = 0; i < count; i++) { guidList.Add(guids[i]); } Guid[] descriptionGuids = stroke.StylusPoints.Description.GetStylusPointPropertyIds(); for (i = 0; i < descriptionGuids.Length; i++) { guidList.Add(descriptionGuids[i]); } if (stroke.ExtendedProperties.Count > 0) { // Add the ExtendedProperty guids in the list for (i = 0; i < stroke.ExtendedProperties.Count; i++) { guidList.Add(stroke.ExtendedProperties[i].Id); } } } private byte GetCompressionAlgorithm() { if (CompressionMode.Compressed == CurrentCompressionMode) { return AlgoModule.DefaultCompression; } return AlgoModule.NoCompression; } ////// This function serializes Stroke Descriptor Table in the stream. For information on how they are serialized, please refer to the spec. /// /// /// ///private uint SerializePacketDescrTable(Stream strm) { if (_strokeDescriptorTable.Count == 0) return 0; int count = 0; uint cbData = 0; // First add the appropriate header information if (_strokeDescriptorTable.Count == 1) { StrokeDescriptor tmp = _strokeDescriptorTable[0]; // If there is no tag, that means default template and only one entry in the list. Return from here if (tmp.Template.Count == 0) return 0; else { // Write it out directly // First the tag cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorBlock); // Now encode the descriptor itself cbData += EncodeStrokeDescriptor(strm, tmp); } } else { uint cbTotal = 0; // First calculate the total encoded size of the all the Templates for (count = 0; count < _strokeDescriptorTable.Count; count++) { cbTotal += SerializationHelper.VarSize((_strokeDescriptorTable[count]).Size) + (_strokeDescriptorTable[count]).Size; } // Now write the Tag cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorTable); cbData += SerializationHelper.Encode(strm, cbTotal); // Now write the encoded templates for (count = 0; count < _strokeDescriptorTable.Count; count++) { cbData += EncodeStrokeDescriptor(strm, _strokeDescriptorTable[count]); } } return cbData; } /// /// This function serializes Metric Descriptor Table in the stream. For information on how they are serialized, please refer to the spec. /// /// ///private uint SerializeMetricTable(Stream strm) { uint cSize = 0; MetricBlock block; if (0 == _metricTable.Count) return 0; for (int i = 0; i < _metricTable.Count; i++) cSize += _metricTable[i].Size; uint cbData = 0; // if total size of the blocks is 1, then there is nothing to write // the reason that the size of the blocks is 1 instead of 0 is because // MetricBlock.Size returns the size of the block plus the byte encoded // size value itself. If the MetricBlock size value is 0, then byte // encoded size value is 0, which has a byte size of 1. if (1 == cSize) { return 0; } else if (1 == _metricTable.Count) { cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.MetricBlock); } else { cbData += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.MetricTable); cbData += SerializationHelper.Encode(strm, cSize); } for (int i = 0; i < _metricTable.Count; i++) { block = _metricTable[i]; cbData += block.Pack(strm); } return cbData; } /// /// Multibyte Encodes a Stroke Descroptor /// /// /// ///private uint EncodeStrokeDescriptor(Stream strm, StrokeDescriptor strd) { uint cbData = 0; // First encode the size of the descriptor cbData += SerializationHelper.Encode(strm, strd.Size); for (int count = 0; count < strd.Template.Count; count++) { // Now encode all members of the descriptor cbData += SerializationHelper.Encode(strm, (uint)strd.Template[count]); } return cbData; } /// /// This function serializes Transform Descriptor Table in the stream. For information on how they are serialized, please refer to the spec. /// /// ///private uint SerializeTransformTable(Stream strm) { // If there is only one entry in the TransformDescriptor table // and it is the default descriptor, skip serialization of transforms if (_transformTable.Count == 1 && _transformTable[0].Size == 0) { return 0; } uint floatTotal = 0; uint doubleTotal = 0; // First count the size of all transforms (handling both float && double versions) for (int i = 0; i < _transformTable.Count; i++) { TransformDescriptor xform = _transformTable[i]; uint cbLocal = SerializationHelper.VarSize((uint)xform.Tag); floatTotal += cbLocal; doubleTotal += cbLocal; if (KnownTagCache.KnownTagIndex.TransformRotate == xform.Tag) { cbLocal = SerializationHelper.VarSize((uint)(xform.Transform[0] + 0.5f)); floatTotal += cbLocal; doubleTotal += cbLocal; } else { cbLocal = xform.Size * Native.SizeOfFloat; floatTotal += cbLocal; doubleTotal += cbLocal * 2; } } uint cbTotal = 0; // If there is only one entry in the TransformDescriptor table if (_transformTable.Count == 1) { TransformDescriptor xform = _transformTable[0]; cbTotal = EncodeTransformDescriptor(strm, xform, false); } else { // Now first write the block descriptor and then write all transforms cbTotal += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.TransformTable); cbTotal += SerializationHelper.Encode(strm, floatTotal); for (int i = 0; i < _transformTable.Count; i++) { cbTotal += EncodeTransformDescriptor(strm, _transformTable[i], false); } } // now write the Extended Transform table (using doubles instead of floats) { // note that we do not distinguish between 1 and > 1 transforms for compression // Now first write the block descriptor and then write all transforms cbTotal += SerializationHelper.Encode(strm, (uint)KnownTagCache.KnownTagIndex.ExtendedTransformTable); cbTotal += SerializationHelper.Encode(strm, doubleTotal); for (int i = 0; i < _transformTable.Count; i++) { cbTotal += EncodeTransformDescriptor(strm, _transformTable[i], true); } } return cbTotal; } /// /// Multibyte Encode if necessary a Transform Descriptor into the stream /// /// /// /// ///private uint EncodeTransformDescriptor(Stream strm, TransformDescriptor xform, bool useDoubles) { uint cbData = 0; // First encode the tag cbData = SerializationHelper.Encode(strm, (uint)xform.Tag); // Encode specially if transform denotes rotation if (KnownTagCache.KnownTagIndex.TransformRotate == xform.Tag) { uint angle = (uint)(xform.Transform[0] + 0.5f); cbData += SerializationHelper.Encode(strm, angle); } else { // samgeo - Presharp issue // Presharp gives a warning when local IDisposable variables are not closed // in this case, we can't call Dispose since it will also close the underlying stream // which still needs to be written to #pragma warning disable 1634, 1691 #pragma warning disable 6518 BinaryWriter bw = new BinaryWriter(strm); for (int i = 0; i < xform.Size; i++) { // note that the binary writer changes serialization // lengths depending on the Write parameter cast if (useDoubles) { bw.Write(xform.Transform[i]); cbData += Native.SizeOfDouble; } else { bw.Write((float)xform.Transform[i]); cbData += Native.SizeOfFloat; } } #pragma warning restore 6518 #pragma warning restore 1634, 1691 } return cbData; } #if OLD_ISF /// /// This function serializes Drawing Attributes Table in the stream. For information on how they are serialized, please refer to the spec. /// /// /// ////// /// Critical - Calls the critical method /// DrawingAttributeSerializer.EncodeAsISF /// /// This directly called by StrokeCollectionSerializer.EncodeISF /// /// TreatAsSafe boundary is StrokeCollectionSerializer.EncodeISF /// /// [SecurityCritical] #else ////// This function serializes Drawing Attributes Table in the stream. For information on how they are serialized, please refer to the spec. /// /// /// #endif private uint SerializeDrawingAttrsTable(Stream stream, GuidList guidList) { uint totalSizeOfSerializedBytes = 0; uint sizeOfHeaderInBytes = 0; if (1 == _drawingAttributesTable.Count) { //we always serialize a single DA, even if it has default values so we will write width back to the stream DrawingAttributes drawingAttributes = _drawingAttributesTable[0]; // There is single drawing attribute. Save it along with the size totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesBlock); // Get the size of the saved bytes using (MemoryStream drawingAttributeStream = new MemoryStream(16)) //reasonable default based onn profiling { sizeOfHeaderInBytes = DrawingAttributeSerializer.EncodeAsISF(drawingAttributes, drawingAttributeStream, guidList, 0, true); // Write the size first totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizeOfHeaderInBytes); // write the data uint bytesWritten = Convert.ToUInt32(drawingAttributeStream.Position); totalSizeOfSerializedBytes += bytesWritten; Debug.Assert(sizeOfHeaderInBytes == bytesWritten); stream.Write( drawingAttributeStream.GetBuffer(), //returns a direct ref, no copied 0, Convert.ToInt32(bytesWritten)); drawingAttributeStream.Dispose(); } } else { // Temporarily declare an array to hold the size of the saved drawing attributes uint[] sizes = new uint[_drawingAttributesTable.Count]; MemoryStream[] drawingAttributeStreams = new MemoryStream[_drawingAttributesTable.Count]; // First calculate the size of each attribute for (int i = 0; i < _drawingAttributesTable.Count; i++) { DrawingAttributes drawingAttributes = _drawingAttributesTable[i]; drawingAttributeStreams[i] = new MemoryStream(16); //reasonable default based on profiling sizes[i] = DrawingAttributeSerializer.EncodeAsISF(drawingAttributes, drawingAttributeStreams[i], guidList, 0, true); sizeOfHeaderInBytes += SerializationHelper.VarSize(sizes[i]) + sizes[i]; } // Now write the KnownTagCache.KnownTagIndex.DrawingAttributesTable first, then sizeOfHeaderInBytes and then individual Drawing Attributes totalSizeOfSerializedBytes = SerializationHelper.Encode(stream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesTable); totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizeOfHeaderInBytes); for (int i = 0; i < _drawingAttributesTable.Count; i++) { DrawingAttributes drawingAttributes = _drawingAttributesTable[i]; // write the size of the block totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizes[i]); // write the saved data uint bytesWritten = Convert.ToUInt32(drawingAttributeStreams[i].Position); totalSizeOfSerializedBytes += bytesWritten; Debug.Assert(sizes[i] == bytesWritten); stream.Write( drawingAttributeStreams[i].GetBuffer(), //returns a direct ref, no copies 0, Convert.ToInt32(bytesWritten)); drawingAttributeStreams[i].Dispose(); } } return totalSizeOfSerializedBytes; } ////// This function builds list of all unique Tables, ie Stroke Descriptor Table, Metric Descriptor Table, Transform Descriptor Table /// and Drawing Attributes Table based on all the strokes. Each entry in the Table is unique with respect to the table. /// /// private void BuildTables(GuidList guidList) { _transformTable.Clear(); _strokeDescriptorTable.Clear(); _metricTable.Clear(); _drawingAttributesTable.Clear(); int count = 0; for (count = 0; count < _coreStrokes.Count; count++) { Stroke stroke = _coreStrokes[count]; // First get the updated descriptor from the stroke StrokeDescriptor strokeDescriptor; MetricBlock metricBlock; StrokeSerializer.BuildStrokeDescriptor(stroke, guidList, _strokeLookupTable[stroke], out strokeDescriptor, out metricBlock); bool fMatch = false; // Compare this with all the global stroke descriptor for a match for (int descriptorIndex = 0; descriptorIndex < _strokeDescriptorTable.Count; descriptorIndex++) { if (strokeDescriptor.IsEqual(_strokeDescriptorTable[descriptorIndex])) { fMatch = true; _strokeLookupTable[stroke].StrokeDescriptorTableIndex = (uint)descriptorIndex; break; } } if (false == fMatch) { _strokeDescriptorTable.Add(strokeDescriptor); _strokeLookupTable[stroke].StrokeDescriptorTableIndex = (uint)_strokeDescriptorTable.Count - 1; } // If there is at least one entry in the metric block, check if the current Block is equvalent to // any of the existing one. fMatch = false; for (int tmp = 0; tmp < _metricTable.Count; tmp++) { MetricBlock block = _metricTable[tmp]; SetType type = SetType.SubSet; if (block.CompareMetricBlock(metricBlock, ref type)) { // This entry exists in the list. If it is a subset of the element, do nothing. // Otherwise, replace the entry with this one if (type == SetType.SuperSet) { _metricTable[tmp] = metricBlock; } fMatch = true; _strokeLookupTable[stroke].MetricDescriptorTableIndex = (uint)tmp; break; } } if (false == fMatch) { _metricTable.Add(metricBlock); _strokeLookupTable[stroke].MetricDescriptorTableIndex = (uint)(_metricTable.Count - 1); } // Now build the Transform Table fMatch = false; // // always identity // TransformDescriptor xform = StrokeCollectionSerializer.IdentityTransformDescriptor; // First check to see if this matches with any existing Transform Blocks for (int i = 0; i < _transformTable.Count; i++) { if (true == xform.Compare(_transformTable[i])) { fMatch = true; _strokeLookupTable[stroke].TransformTableIndex = (uint)i; break; } } if (false == fMatch) { _transformTable.Add(xform); _strokeLookupTable[stroke].TransformTableIndex = (uint)(_transformTable.Count - 1); } // Now build the drawing attributes table fMatch = false; DrawingAttributes drattrs = _coreStrokes[count].DrawingAttributes; // First check to see if this matches with any existing transform blocks for (int i = 0; i < _drawingAttributesTable.Count; i++) { if (true == drattrs.Equals(_drawingAttributesTable[i])) { fMatch = true; _strokeLookupTable[stroke].DrawingAttributesTableIndex = (uint)i; break; } } if (false == fMatch) { _drawingAttributesTable.Add(drattrs); _strokeLookupTable[stroke].DrawingAttributesTableIndex = (uint)_drawingAttributesTable.Count - 1; } } } #endregion // Private Methods internal class StrokeLookupEntry { internal uint MetricDescriptorTableIndex = 0; internal uint StrokeDescriptorTableIndex = 0; internal uint TransformTableIndex = 0; internal uint DrawingAttributesTableIndex = 0; // Compression algorithm data internal byte CompressionData = 0; internal int[][] ISFReadyStrokeData = null; internal bool StorePressure = false; } #endregion // Encoding #region Debugging Methods [System.Diagnostics.Conditional("DEBUG_ISF")] static void ISFDebugTrace(string message) { System.Diagnostics.Debug.WriteLine(message); } #endregion // [System.Diagnostics.Conditional("DEBUG_ISF")] internal static string ISFDebugMessage(string debugMessage) { #if DEBUG return debugMessage; #else return SR.Get(SRID.IsfOperationFailed); #endif } #region Private Fields StrokeCollection _coreStrokes; private System.Collections.Generic.List_strokeDescriptorTable = null; private System.Collections.Generic.List _transformTable = null; private System.Collections.Generic.List _drawingAttributesTable = null; private System.Collections.Generic.List _metricTable = null; private Vector _himetricSize = new Vector(0.0f, 0.0f); // The ink space rectangle (e.g. bounding box for GIF) is stored // with the serialization info so that load/save roundtrip the // rectangle private Rect _inkSpaceRectangle = new Rect(); System.Collections.Generic.Dictionary _strokeLookupTable = null; #endregion } /// /// Simple static method for generating StrokeIds /// internal static class StrokeIdGenerator { ////// Generates backwards compatible StrokeID's for the strokes /// /// strokes ///internal static int[] GetStrokeIds(StrokeCollection strokes) { System.Diagnostics.Debug.Assert(strokes != null); int[] strokeIds = new int[strokes.Count]; for (int x = 0; x < strokeIds.Length; x++) { //stroke ID's are 1 based (1,2,3...) strokeIds[x] = x + 1; } return strokeIds; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- AnchoredBlock.cs
- StreamingContext.cs
- RequestCacheValidator.cs
- WebRequestModuleElementCollection.cs
- _NegoStream.cs
- Annotation.cs
- XmlElementAttributes.cs
- validationstate.cs
- TdsParserStateObject.cs
- shaper.cs
- DetailsViewDeletedEventArgs.cs
- SimpleType.cs
- TextSelectionHighlightLayer.cs
- ProfileSection.cs
- SqlWorkflowPersistenceService.cs
- IdentityReference.cs
- SingleStorage.cs
- TrimSurroundingWhitespaceAttribute.cs
- MemberHolder.cs
- InternalSafeNativeMethods.cs
- CodeNamespace.cs
- ProxyHelper.cs
- WindowsAuthenticationModule.cs
- VerticalAlignConverter.cs
- XmlSerializationReader.cs
- EntitySetDataBindingList.cs
- SplineKeyFrames.cs
- Stroke2.cs
- SafeEventLogWriteHandle.cs
- EnumValidator.cs
- TraceUtility.cs
- ResXDataNode.cs
- EnvelopedSignatureTransform.cs
- NotCondition.cs
- AssignDesigner.xaml.cs
- ServiceOperationParameter.cs
- Win32Exception.cs
- NopReturnReader.cs
- ObjectDataSourceSelectingEventArgs.cs
- SuppressedPackageProperties.cs
- NotificationContext.cs
- BitmapEffect.cs
- IndentedTextWriter.cs
- WebOperationContext.cs
- EntityDataSourceDesignerHelper.cs
- GridSplitter.cs
- VariableAction.cs
- JumpItem.cs
- FragmentQuery.cs
- LocalValueEnumerator.cs
- CrossContextChannel.cs
- VirtualDirectoryMappingCollection.cs
- Image.cs
- ToolStrip.cs
- WorkflowApplicationUnloadedException.cs
- SafeLocalAllocation.cs
- HuffModule.cs
- FragmentQueryKB.cs
- BrowserDefinition.cs
- BamlVersionHeader.cs
- HtmlGenericControl.cs
- GridItemPatternIdentifiers.cs
- Vector3DAnimationBase.cs
- TextTreeExtractElementUndoUnit.cs
- OciEnlistContext.cs
- DragDrop.cs
- UInt64Storage.cs
- XmlSerializationReader.cs
- TraceHandler.cs
- PreloadedPackages.cs
- FixedSOMLineRanges.cs
- Win32MouseDevice.cs
- DynamicDocumentPaginator.cs
- TransformProviderWrapper.cs
- VisualStyleElement.cs
- _ProxyChain.cs
- DesignTimeDataBinding.cs
- IIS7WorkerRequest.cs
- ProcessHost.cs
- FtpRequestCacheValidator.cs
- RegexMatch.cs
- XmlUtf8RawTextWriter.cs
- ConfigurationLocation.cs
- DocumentViewerHelper.cs
- ObjectDataSourceStatusEventArgs.cs
- CheckBoxFlatAdapter.cs
- RectAnimationClockResource.cs
- cryptoapiTransform.cs
- ColorTransform.cs
- ContentHostHelper.cs
- BCLDebug.cs
- DataViewListener.cs
- QuaternionAnimationUsingKeyFrames.cs
- SemanticBasicElement.cs
- AssemblyAttributes.cs
- BatchServiceHost.cs
- SiteMapProvider.cs
- TiffBitmapEncoder.cs
- ViewEvent.cs
- TraceHandler.cs