| /******************************************************************************* | 
 |  * You may amend and distribute as you like, but don't remove this header! | 
 |  * | 
 |  * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. | 
 |  * See http://www.codeplex.com/EPPlus for details. | 
 |  * | 
 |  * Copyright (C) 2011  Jan Källman | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   | 
 |  * See the GNU Lesser General Public License for more details. | 
 |  * | 
 |  * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php | 
 |  * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html | 
 |  * | 
 |  * All code and executables are provided "as is" with no warranty either express or implied.  | 
 |  * The author accepts no liability for any damage or loss of business that this product may cause. | 
 |  * | 
 |  * Code change notes: | 
 |  *  | 
 |  * Author							Change						Date | 
 |  ******************************************************************************* | 
 |  * Jan Källman		Added		25-Oct-2012 | 
 |  *******************************************************************************/ | 
 | using System; | 
 | using System.Collections.Generic; | 
 | using System.Globalization; | 
 | using System.Linq; | 
 | using System.Text; | 
 | using System.IO; | 
 | using System.Xml; | 
 | using OfficeOpenXml.Utils; | 
 | using OfficeOpenXml.Packaging.Ionic.Zip; | 
 | using Ionic.Zip; | 
 | namespace OfficeOpenXml.Packaging | 
 | { | 
 |     /// <summary> | 
 |     /// Specifies whether the target is inside or outside the System.IO.Packaging.Package. | 
 |     /// </summary> | 
 |     public enum TargetMode | 
 |     { | 
 |         /// <summary> | 
 |         /// The relationship references a part that is inside the package. | 
 |         /// </summary> | 
 |         Internal = 0, | 
 |         /// <summary> | 
 |         /// The relationship references a resource that is external to the package. | 
 |         /// </summary> | 
 |         External = 1, | 
 |     } | 
 |     /// <summary> | 
 |     /// Represent an OOXML Zip package. | 
 |     /// </summary> | 
 |     public class ZipPackage : ZipPackageRelationshipBase | 
 |     { | 
 |         internal class ContentType | 
 |         { | 
 |             internal string Name; | 
 |             internal bool IsExtension; | 
 |             internal string Match; | 
 |             public ContentType(string name, bool isExtension, string match) | 
 |             { | 
 |                 Name = name; | 
 |                 IsExtension = isExtension; | 
 |                 Match = match; | 
 |             } | 
 |         } | 
 |         Dictionary<string, ZipPackagePart> Parts = new Dictionary<string, ZipPackagePart>(StringComparer.InvariantCultureIgnoreCase); | 
 |         internal Dictionary<string, ContentType> _contentTypes = new Dictionary<string, ContentType>(StringComparer.InvariantCultureIgnoreCase); | 
 |         internal ZipPackage() | 
 |         { | 
 |             AddNew(); | 
 |         } | 
 |  | 
 |         private void AddNew() | 
 |         { | 
 |             _contentTypes.Add("xml", new ContentType(ExcelPackage.schemaXmlExtension, true, "xml")); | 
 |             _contentTypes.Add("rels", new ContentType(ExcelPackage.schemaRelsExtension, true, "rels")); | 
 |         } | 
 |  | 
 |         internal ZipPackage(Stream stream) | 
 |         { | 
 |             bool hasContentTypeXml = false; | 
 |             if (stream == null || stream.Length == 0) | 
 |             { | 
 |                 AddNew(); | 
 |             } | 
 |             else | 
 |             { | 
 |                 var rels = new Dictionary<string, string>(); | 
 |                 stream.Seek(0, SeekOrigin.Begin);                 | 
 |                 using (ZipInputStream zip = new ZipInputStream(stream)) | 
 |                 { | 
 |                     var e = zip.GetNextEntry(); | 
 |                     while (e != null) | 
 |                     { | 
 |                         if (e.UncompressedSize > 0) | 
 |                         { | 
 |                             var b = new byte[e.UncompressedSize]; | 
 |                             var size = zip.Read(b, 0, (int)e.UncompressedSize); | 
 |                             if (e.FileName.Equals("[content_types].xml", StringComparison.InvariantCultureIgnoreCase)) | 
 |                             { | 
 |                                 AddContentTypes(Encoding.UTF8.GetString(b)); | 
 |                                 hasContentTypeXml = true; | 
 |                             } | 
 |                             else if (e.FileName.Equals("_rels/.rels", StringComparison.InvariantCultureIgnoreCase))  | 
 |                             { | 
 |                                 ReadRelation(Encoding.UTF8.GetString(b), ""); | 
 |                             } | 
 |                             else | 
 |                             { | 
 |                                 if (e.FileName.EndsWith(".rels", StringComparison.InvariantCultureIgnoreCase)) | 
 |                                 { | 
 |                                     rels.Add(GetUriKey(e.FileName), Encoding.UTF8.GetString(b)); | 
 |                                 } | 
 |                                 else | 
 |                                 { | 
 |                                     var part = new ZipPackagePart(this, e); | 
 |                                     part.Stream = new MemoryStream(); | 
 |                                     part.Stream.Write(b, 0, b.Length); | 
 |                                     Parts.Add(GetUriKey(e.FileName), part); | 
 |                                 } | 
 |                             } | 
 |                         } | 
 |                         else | 
 |                         { | 
 |                         } | 
 |                         e = zip.GetNextEntry(); | 
 |                     } | 
 |  | 
 |                     foreach (var p in Parts) | 
 |                     { | 
 |                         string name = Path.GetFileName(p.Key); | 
 |                         string extension = Path.GetExtension(p.Key); | 
 |                         string relFile = string.Format("{0}_rels/{1}.rels", p.Key.Substring(0, p.Key.Length - name.Length), name); | 
 |                         if (rels.ContainsKey(relFile)) | 
 |                         { | 
 |                             p.Value.ReadRelation(rels[relFile], p.Value.Uri.OriginalString); | 
 |                         } | 
 |                         if (_contentTypes.ContainsKey(p.Key)) | 
 |                         { | 
 |                             p.Value.ContentType = _contentTypes[p.Key].Name; | 
 |                         } | 
 |                         else if (extension.Length > 1 && _contentTypes.ContainsKey(extension.Substring(1))) | 
 |                         { | 
 |                             p.Value.ContentType = _contentTypes[extension.Substring(1)].Name; | 
 |                         } | 
 |                     } | 
 |                     if (!hasContentTypeXml) | 
 |                     { | 
 |                         throw (new InvalidDataException("The file is not an valid Package file. If the file is encrypted, please supply the password in the constructor.")); | 
 |                     } | 
 |                     if (!hasContentTypeXml) | 
 |                     { | 
 |                         throw (new InvalidDataException("The file is not an valid Package file. If the file is encrypted, please supply the password in the constructor.")); | 
 |                     } | 
 |                     zip.Close(); | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         private void AddContentTypes(string xml) | 
 |         { | 
 |             var doc = new XmlDocument(); | 
 |             XmlHelper.LoadXmlSafe(doc, xml, Encoding.UTF8); | 
 |  | 
 |             foreach (XmlElement c in doc.DocumentElement.ChildNodes) | 
 |             { | 
 |                 ContentType ct; | 
 |                 if (string.IsNullOrEmpty(c.GetAttribute("Extension"))) | 
 |                 { | 
 |                     ct = new ContentType(c.GetAttribute("ContentType"), false, c.GetAttribute("PartName")); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     ct = new ContentType(c.GetAttribute("ContentType"), true, c.GetAttribute("Extension")); | 
 |                 } | 
 |                 _contentTypes.Add(GetUriKey(ct.Match), ct); | 
 |             } | 
 |         } | 
 |  | 
 |         #region Methods | 
 |         internal ZipPackagePart CreatePart(Uri partUri, string contentType) | 
 |         { | 
 |             return CreatePart(partUri, contentType, CompressionLevel.Default); | 
 |         } | 
 |         internal ZipPackagePart CreatePart(Uri partUri, string contentType, CompressionLevel compressionLevel) | 
 |         { | 
 |             if (PartExists(partUri)) | 
 |             { | 
 |                 throw (new InvalidOperationException("Part already exist")); | 
 |             } | 
 |  | 
 |             var part = new ZipPackagePart(this, partUri, contentType, compressionLevel); | 
 |             _contentTypes.Add(GetUriKey(part.Uri.OriginalString), new ContentType(contentType, false, part.Uri.OriginalString)); | 
 |             Parts.Add(GetUriKey(part.Uri.OriginalString), part); | 
 |             return part; | 
 |         } | 
 |         internal ZipPackagePart GetPart(Uri partUri) | 
 |         { | 
 |             if (PartExists(partUri)) | 
 |             { | 
 |                 return Parts.Single(x => x.Key.Equals(GetUriKey(partUri.OriginalString),StringComparison.InvariantCultureIgnoreCase)).Value; | 
 |             } | 
 |             else | 
 |             { | 
 |                 throw (new InvalidOperationException("Part does not exist.")); | 
 |             } | 
 |         } | 
 |  | 
 |         internal string GetUriKey(string uri) | 
 |         { | 
 |             string ret = uri; | 
 |             if (ret[0] != '/') | 
 |             { | 
 |                 ret = "/" + ret; | 
 |             } | 
 |             return ret; | 
 |         } | 
 |         internal bool PartExists(Uri partUri) | 
 |         { | 
 |             var uriKey = GetUriKey(partUri.OriginalString.ToLower(CultureInfo.InvariantCulture)); | 
 |             return Parts.Keys.Any(x => x.Equals(uriKey, StringComparison.InvariantCultureIgnoreCase)); | 
 |         } | 
 |         #endregion | 
 |  | 
 |         internal void DeletePart(Uri Uri) | 
 |         { | 
 |             var delList=new List<object[]>();  | 
 |             foreach (var p in Parts.Values) | 
 |             { | 
 |                 foreach (var r in p.GetRelationships()) | 
 |                 { | 
 |                     if (UriHelper.ResolvePartUri(p.Uri, r.TargetUri).OriginalString.Equals(Uri.OriginalString, StringComparison.InvariantCultureIgnoreCase)) | 
 |                     {                         | 
 |                         delList.Add(new object[]{r.Id, p}); | 
 |                     } | 
 |                 } | 
 |             } | 
 |             foreach (var o in delList) | 
 |             { | 
 |                 ((ZipPackagePart)o[1]).DeleteRelationship(o[0].ToString()); | 
 |             } | 
 |             var rels = GetPart(Uri).GetRelationships(); | 
 |             while (rels.Count > 0) | 
 |             { | 
 |                 rels.Remove(rels.First().Id); | 
 |             } | 
 |             rels=null; | 
 |             _contentTypes.Remove(GetUriKey(Uri.OriginalString)); | 
 |             //remove all relations | 
 |             Parts.Remove(GetUriKey(Uri.OriginalString)); | 
 |              | 
 |         } | 
 |         internal void Save(Stream stream) | 
 |         { | 
 |             var enc = Encoding.UTF8; | 
 |             ZipOutputStream os = new ZipOutputStream(stream, true); | 
 |             os.CompressionLevel = (OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel)_compression;             | 
 |             /**** ContentType****/ | 
 |             var entry = os.PutNextEntry("[Content_Types].xml"); | 
 |             byte[] b = enc.GetBytes(GetContentTypeXml()); | 
 |             os.Write(b, 0, b.Length); | 
 |             /**** Top Rels ****/ | 
 |             _rels.WriteZip(os, "_rels\\.rels"); | 
 |             ZipPackagePart ssPart=null; | 
 |             foreach(var part in Parts.Values) | 
 |             { | 
 |                 if (part.ContentType != ExcelPackage.contentTypeSharedString) | 
 |                 { | 
 |                     part.WriteZip(os); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     ssPart = part; | 
 |                 } | 
 |             } | 
 |             //Shared strings must be saved after all worksheets. The ss dictionary is populated when that workheets are saved (to get the best performance). | 
 |             if (ssPart != null) | 
 |             { | 
 |                 ssPart.WriteZip(os); | 
 |             } | 
 |             os.Flush(); | 
 |             os.Close(); | 
 |             os.Dispose();   | 
 |              | 
 |             //return ms; | 
 |         } | 
 |  | 
 |         private string GetContentTypeXml() | 
 |         { | 
 |             StringBuilder xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); | 
 |             foreach (ContentType ct in _contentTypes.Values) | 
 |             { | 
 |                 if (ct.IsExtension) | 
 |                 { | 
 |                     xml.AppendFormat("<Default ContentType=\"{0}\" Extension=\"{1}\"/>", ct.Name, ct.Match); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     xml.AppendFormat("<Override ContentType=\"{0}\" PartName=\"{1}\" />", ct.Name, GetUriKey(ct.Match)); | 
 |                 } | 
 |             } | 
 |             xml.Append("</Types>"); | 
 |             return xml.ToString(); | 
 |         } | 
 |         internal void Flush() | 
 |         { | 
 |  | 
 |         } | 
 |         internal void Close() | 
 |         { | 
 |              | 
 |         } | 
 |         CompressionLevel _compression = CompressionLevel.Default; | 
 |         public CompressionLevel Compression  | 
 |         {  | 
 |             get | 
 |             { | 
 |                 return _compression; | 
 |             } | 
 |             set | 
 |             { | 
 |                 foreach (var part in Parts.Values) | 
 |                 { | 
 |                     if (part.CompressionLevel == _compression) | 
 |                     { | 
 |                         part.CompressionLevel = value; | 
 |                     } | 
 |                 } | 
 |                 _compression = value; | 
 |             } | 
 |         } | 
 |     } | 
 | } |