| /******************************************************************************* | |
| * 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 Initial Release 2009-10-01 | |
| * Starnuto Di Topo & Jan Källman Added stream constructors | |
| * and Load method Save as | |
| * stream 2010-03-14 | |
| * Jan Källman License changed GPL-->LGPL 2011-12-27 | |
| *******************************************************************************/ | |
| using System; | |
| using System.Xml; | |
| using System.IO; | |
| using System.Collections.Generic; | |
| using System.Security.Cryptography; | |
| using OfficeOpenXml.Drawing; | |
| using OfficeOpenXml.Utils; | |
| using OfficeOpenXml.Packaging.Ionic.Zlib; | |
| using OfficeOpenXml.FormulaParsing; | |
| using OfficeOpenXml.Encryption; | |
| namespace OfficeOpenXml | |
| { | |
| /// <summary> | |
| /// Maps to DotNetZips CompressionLevel enum | |
| /// </summary> | |
| public enum CompressionLevel | |
| { | |
| Level0 = 0, | |
| None = 0, | |
| Level1 = 1, | |
| BestSpeed = 1, | |
| Level2 = 2, | |
| Level3 = 3, | |
| Level4 = 4, | |
| Level5 = 5, | |
| Level6 = 6, | |
| Default = 6, | |
| Level7 = 7, | |
| Level8 = 8, | |
| BestCompression = 9, | |
| Level9 = 9, | |
| } | |
| /// <summary> | |
| /// Represents an Excel 2007/2010 XLSX file package. | |
| /// This is the top-level object to access all parts of the document. | |
| /// <code> | |
| /// FileInfo newFile = new FileInfo(outputDir.FullName + @"\sample1.xlsx"); | |
| /// if (newFile.Exists) | |
| /// { | |
| /// newFile.Delete(); // ensures we create a new workbook | |
| /// newFile = new FileInfo(outputDir.FullName + @"\sample1.xlsx"); | |
| /// } | |
| /// using (ExcelPackage package = new ExcelPackage(newFile)) | |
| /// { | |
| /// // add a new worksheet to the empty workbook | |
| /// ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Inventory"); | |
| /// //Add the headers | |
| /// worksheet.Cells[1, 1].Value = "ID"; | |
| /// worksheet.Cells[1, 2].Value = "Product"; | |
| /// worksheet.Cells[1, 3].Value = "Quantity"; | |
| /// worksheet.Cells[1, 4].Value = "Price"; | |
| /// worksheet.Cells[1, 5].Value = "Value"; | |
| /// | |
| /// //Add some items... | |
| /// worksheet.Cells["A2"].Value = "12001"; | |
| /// worksheet.Cells["B2"].Value = "Nails"; | |
| /// worksheet.Cells["C2"].Value = 37; | |
| /// worksheet.Cells["D2"].Value = 3.99; | |
| /// | |
| /// worksheet.Cells["A3"].Value = "12002"; | |
| /// worksheet.Cells["B3"].Value = "Hammer"; | |
| /// worksheet.Cells["C3"].Value = 5; | |
| /// worksheet.Cells["D3"].Value = 12.10; | |
| /// | |
| /// worksheet.Cells["A4"].Value = "12003"; | |
| /// worksheet.Cells["B4"].Value = "Saw"; | |
| /// worksheet.Cells["C4"].Value = 12; | |
| /// worksheet.Cells["D4"].Value = 15.37; | |
| /// | |
| /// //Add a formula for the value-column | |
| /// worksheet.Cells["E2:E4"].Formula = "C2*D2"; | |
| /// | |
| /// //Ok now format the values; | |
| /// using (var range = worksheet.Cells[1, 1, 1, 5]) | |
| /// { | |
| /// range.Style.Font.Bold = true; | |
| /// range.Style.Fill.PatternType = ExcelFillStyle.Solid; | |
| /// range.Style.Fill.BackgroundColor.SetColor(Color.DarkBlue); | |
| /// range.Style.Font.Color.SetColor(Color.White); | |
| /// } | |
| /// | |
| /// worksheet.Cells["A5:E5"].Style.Border.Top.Style = ExcelBorderStyle.Thin; | |
| /// worksheet.Cells["A5:E5"].Style.Font.Bold = true; | |
| /// | |
| /// worksheet.Cells[5, 3, 5, 5].Formula = string.Format("SUBTOTAL(9,{0})", new ExcelAddress(2,3,4,3).Address); | |
| /// worksheet.Cells["C2:C5"].Style.Numberformat.Format = "#,##0"; | |
| /// worksheet.Cells["D2:E5"].Style.Numberformat.Format = "#,##0.00"; | |
| /// | |
| /// //Create an autofilter for the range | |
| /// worksheet.Cells["A1:E4"].AutoFilter = true; | |
| /// | |
| /// worksheet.Cells["A1:E5"].AutoFitColumns(0); | |
| /// | |
| /// // lets set the header text | |
| /// worksheet.HeaderFooter.oddHeader.CenteredText = "&24&U&\"Arial,Regular Bold\" Inventory"; | |
| /// // add the page number to the footer plus the total number of pages | |
| /// worksheet.HeaderFooter.oddFooter.RightAlignedText = | |
| /// string.Format("Page {0} of {1}", ExcelHeaderFooter.PageNumber, ExcelHeaderFooter.NumberOfPages); | |
| /// // add the sheet name to the footer | |
| /// worksheet.HeaderFooter.oddFooter.CenteredText = ExcelHeaderFooter.SheetName; | |
| /// // add the file path to the footer | |
| /// worksheet.HeaderFooter.oddFooter.LeftAlignedText = ExcelHeaderFooter.FilePath + ExcelHeaderFooter.FileName; | |
| /// | |
| /// worksheet.PrinterSettings.RepeatRows = worksheet.Cells["1:2"]; | |
| /// worksheet.PrinterSettings.RepeatColumns = worksheet.Cells["A:G"]; | |
| /// | |
| /// // Change the sheet view to show it in page layout mode | |
| /// worksheet.View.PageLayoutView = true; | |
| /// | |
| /// // set some document properties | |
| /// package.Workbook.Properties.Title = "Invertory"; | |
| /// package.Workbook.Properties.Author = "Jan Källman"; | |
| /// package.Workbook.Properties.Comments = "This sample demonstrates how to create an Excel 2007 workbook using EPPlus"; | |
| /// | |
| /// // set some extended property values | |
| /// package.Workbook.Properties.Company = "AdventureWorks Inc."; | |
| /// | |
| /// // set some custom property values | |
| /// package.Workbook.Properties.SetCustomPropertyValue("Checked by", "Jan Källman"); | |
| /// package.Workbook.Properties.SetCustomPropertyValue("AssemblyName", "EPPlus"); | |
| /// | |
| /// // save our new workbook and we are done! | |
| /// package.Save(); | |
| /// | |
| /// } | |
| /// | |
| /// return newFile.FullName; | |
| /// </code> | |
| /// More samples can be found at <a href="http://epplus.codeplex.com/">http://epplus.codeplex.com/</a> | |
| /// </summary> | |
| public sealed class ExcelPackage : IDisposable | |
| { | |
| internal const bool preserveWhitespace=false; | |
| Stream _stream = null; | |
| private bool _isExternalStream=false; | |
| internal class ImageInfo | |
| { | |
| internal string Hash { get; set; } | |
| internal Uri Uri{get;set;} | |
| internal int RefCount { get; set; } | |
| internal Packaging.ZipPackagePart Part { get; set; } | |
| } | |
| internal Dictionary<string, ImageInfo> _images = new Dictionary<string, ImageInfo>(); | |
| #region Properties | |
| /// <summary> | |
| /// Extention Schema types | |
| /// </summary> | |
| internal const string schemaXmlExtension = "application/xml"; | |
| internal const string schemaRelsExtension = "application/vnd.openxmlformats-package.relationships+xml"; | |
| /// <summary> | |
| /// Main Xml schema name | |
| /// </summary> | |
| internal const string schemaMain = @"http://schemas.openxmlformats.org/spreadsheetml/2006/main"; | |
| /// <summary> | |
| /// Relationship schema name | |
| /// </summary> | |
| internal const string schemaRelationships = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships"; | |
| internal const string schemaDrawings = @"http://schemas.openxmlformats.org/drawingml/2006/main"; | |
| internal const string schemaSheetDrawings = @"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"; | |
| internal const string schemaMarkupCompatibility = @"http://schemas.openxmlformats.org/markup-compatibility/2006"; | |
| internal const string schemaMicrosoftVml = @"urn:schemas-microsoft-com:vml"; | |
| internal const string schemaMicrosoftOffice = "urn:schemas-microsoft-com:office:office"; | |
| internal const string schemaMicrosoftExcel = "urn:schemas-microsoft-com:office:excel"; | |
| internal const string schemaChart = @"http://schemas.openxmlformats.org/drawingml/2006/chart"; | |
| internal const string schemaHyperlink = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; | |
| internal const string schemaComment = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"; | |
| internal const string schemaImage = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"; | |
| //Office properties | |
| internal const string schemaCore = @"http://schemas.openxmlformats.org/package/2006/metadata/core-properties"; | |
| internal const string schemaExtended = @"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"; | |
| internal const string schemaCustom = @"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"; | |
| internal const string schemaDc = @"http://purl.org/dc/elements/1.1/"; | |
| internal const string schemaDcTerms = @"http://purl.org/dc/terms/"; | |
| internal const string schemaDcmiType = @"http://purl.org/dc/dcmitype/"; | |
| internal const string schemaXsi = @"http://www.w3.org/2001/XMLSchema-instance"; | |
| internal const string schemaVt = @"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"; | |
| //Pivottables | |
| internal const string schemaPivotTable = @"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"; | |
| internal const string schemaPivotCacheDefinition = @"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"; | |
| internal const string schemaPivotCacheRecords = @"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"; | |
| //VBA | |
| internal const string schemaVBA = @"application/vnd.ms-office.vbaProject"; | |
| internal const string schemaVBASignature = @"application/vnd.ms-office.vbaProjectSignature"; | |
| internal const string contentTypeWorkbookDefault = @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; | |
| internal const string contentTypeWorkbookMacroEnabled = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"; | |
| internal const string contentTypeSharedString = @"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"; | |
| //Package reference | |
| private Packaging.ZipPackage _package; | |
| internal ExcelWorkbook _workbook; | |
| /// <summary> | |
| /// Maximum number of columns in a worksheet (16384). | |
| /// </summary> | |
| public const int MaxColumns = 16384; | |
| /// <summary> | |
| /// Maximum number of rows in a worksheet (1048576). | |
| /// </summary> | |
| public const int MaxRows = 1048576; | |
| #endregion | |
| #region ExcelPackage Constructors | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage. Output is accessed through the Stream property. | |
| /// </summary> | |
| public ExcelPackage() | |
| { | |
| Init(); | |
| ConstructNewFile(null); | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a existing file or creates a new file. | |
| /// </summary> | |
| /// <param name="newFile">If newFile exists, it is opened. Otherwise it is created from scratch.</param> | |
| public ExcelPackage(FileInfo newFile) | |
| { | |
| Init(); | |
| File = newFile; | |
| ConstructNewFile(null); | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a existing file or creates a new file. | |
| /// </summary> | |
| /// <param name="newFile">If newFile exists, it is opened. Otherwise it is created from scratch.</param> | |
| /// <param name="password">Password for an encrypted package</param> | |
| public ExcelPackage(FileInfo newFile, string password) | |
| { | |
| Init(); | |
| File = newFile; | |
| ConstructNewFile(password); | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a existing template. | |
| /// If newFile exists, it will be overwritten when the Save method is called | |
| /// </summary> | |
| /// <param name="newFile">The name of the Excel file to be created</param> | |
| /// <param name="template">The name of the Excel template to use as the basis of the new Excel file</param> | |
| public ExcelPackage(FileInfo newFile, FileInfo template) | |
| { | |
| Init(); | |
| File = newFile; | |
| CreateFromTemplate(template, null); | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a existing template. | |
| /// If newFile exists, it will be overwritten when the Save method is called | |
| /// </summary> | |
| /// <param name="newFile">The name of the Excel file to be created</param> | |
| /// <param name="template">The name of the Excel template to use as the basis of the new Excel file</param> | |
| /// <param name="password">Password to decrypted the template</param> | |
| public ExcelPackage(FileInfo newFile, FileInfo template, string password) | |
| { | |
| Init(); | |
| File = newFile; | |
| CreateFromTemplate(template, password); | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a existing template. | |
| /// </summary> | |
| /// <param name="template">The name of the Excel template to use as the basis of the new Excel file</param> | |
| /// <param name="useStream">if true use a stream. If false create a file in the temp dir with a random name</param> | |
| public ExcelPackage(FileInfo template, bool useStream) | |
| { | |
| Init(); | |
| CreateFromTemplate(template, null); | |
| if (useStream == false) | |
| { | |
| File = new FileInfo(Path.GetTempPath() + Guid.NewGuid().ToString() + ".xlsx"); | |
| } | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a existing template. | |
| /// </summary> | |
| /// <param name="template">The name of the Excel template to use as the basis of the new Excel file</param> | |
| /// <param name="useStream">if true use a stream. If false create a file in the temp dir with a random name</param> | |
| /// <param name="password">Password to decrypted the template</param> | |
| public ExcelPackage(FileInfo template, bool useStream, string password) | |
| { | |
| Init(); | |
| CreateFromTemplate(template, password); | |
| if (useStream == false) | |
| { | |
| File = new FileInfo(Path.GetTempPath() + Guid.NewGuid().ToString() + ".xlsx"); | |
| } | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a stream | |
| /// </summary> | |
| /// <param name="newStream">The stream object can be empty or contain a package. The stream must be Read/Write</param> | |
| public ExcelPackage(Stream newStream) | |
| { | |
| Init(); | |
| if (newStream.Length == 0) | |
| { | |
| _stream = newStream; | |
| _isExternalStream = true; | |
| ConstructNewFile(null); | |
| } | |
| else | |
| { | |
| Load(newStream); | |
| } | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a stream | |
| /// </summary> | |
| /// <param name="newStream">The stream object can be empty or contain a package. The stream must be Read/Write</param> | |
| /// <param name="Password">The password to decrypt the document</param> | |
| public ExcelPackage(Stream newStream, string Password) | |
| { | |
| if (!(newStream.CanRead && newStream.CanWrite)) | |
| { | |
| throw new Exception("The stream must be read/write"); | |
| } | |
| Init(); | |
| if (newStream.Length > 0) | |
| { | |
| Load(newStream,Password); | |
| } | |
| else | |
| { | |
| _stream = newStream; | |
| _isExternalStream = true; | |
| //_package = Package.Open(_stream, FileMode.Create, FileAccess.ReadWrite); TODO:Remove | |
| _package = new Packaging.ZipPackage(_stream); | |
| CreateBlankWb(); | |
| } | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a stream | |
| /// </summary> | |
| /// <param name="newStream">The output stream. Must be an empty read/write stream.</param> | |
| /// <param name="templateStream">This stream is copied to the output stream at load</param> | |
| public ExcelPackage(Stream newStream, Stream templateStream) | |
| { | |
| if (newStream.Length > 0) | |
| { | |
| throw(new Exception("The output stream must be empty. Length > 0")); | |
| } | |
| else if (!(newStream.CanRead && newStream.CanWrite)) | |
| { | |
| throw new Exception("The stream must be read/write"); | |
| } | |
| Init(); | |
| Load(templateStream, newStream, null); | |
| } | |
| /// <summary> | |
| /// Create a new instance of the ExcelPackage class based on a stream | |
| /// </summary> | |
| /// <param name="newStream">The output stream. Must be an empty read/write stream.</param> | |
| /// <param name="templateStream">This stream is copied to the output stream at load</param> | |
| /// <param name="Password">Password to decrypted the template</param> | |
| public ExcelPackage(Stream newStream, Stream templateStream, string Password) | |
| { | |
| if (newStream.Length > 0) | |
| { | |
| throw (new Exception("The output stream must be empty. Length > 0")); | |
| } | |
| else if (!(newStream.CanRead && newStream.CanWrite)) | |
| { | |
| throw new Exception("The stream must be read/write"); | |
| } | |
| Init(); | |
| Load(templateStream, newStream, Password); | |
| } | |
| #endregion | |
| internal ImageInfo AddImage(byte[] image) | |
| { | |
| return AddImage(image, null, ""); | |
| } | |
| internal ImageInfo AddImage(byte[] image, Uri uri, string contentType) | |
| { | |
| var hashProvider = new SHA1CryptoServiceProvider(); | |
| var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-",""); | |
| lock (_images) | |
| { | |
| if (_images.ContainsKey(hash)) | |
| { | |
| _images[hash].RefCount++; | |
| } | |
| else | |
| { | |
| Packaging.ZipPackagePart imagePart; | |
| if (uri == null) | |
| { | |
| uri = GetNewUri(Package, "/xl/media/image{0}.jpg"); | |
| imagePart = Package.CreatePart(uri, "image/jpeg", CompressionLevel.None); | |
| } | |
| else | |
| { | |
| imagePart = Package.CreatePart(uri, contentType, CompressionLevel.None); | |
| } | |
| var stream = imagePart.GetStream(FileMode.Create, FileAccess.Write); | |
| stream.Write(image, 0, image.GetLength(0)); | |
| _images.Add(hash, new ImageInfo() { Uri = uri, RefCount = 1, Hash = hash, Part = imagePart }); | |
| } | |
| } | |
| return _images[hash]; | |
| } | |
| internal ImageInfo LoadImage(byte[] image, Uri uri, Packaging.ZipPackagePart imagePart) | |
| { | |
| var hashProvider = new SHA1CryptoServiceProvider(); | |
| var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-", ""); | |
| if (_images.ContainsKey(hash)) | |
| { | |
| _images[hash].RefCount++; | |
| } | |
| else | |
| { | |
| _images.Add(hash, new ImageInfo() { Uri = uri, RefCount = 1, Hash = hash, Part = imagePart }); | |
| } | |
| return _images[hash]; | |
| } | |
| internal void RemoveImage(string hash) | |
| { | |
| lock (_images) | |
| { | |
| if (_images.ContainsKey(hash)) | |
| { | |
| var ii = _images[hash]; | |
| ii.RefCount--; | |
| if (ii.RefCount == 0) | |
| { | |
| Package.DeletePart(ii.Uri); | |
| _images.Remove(hash); | |
| } | |
| } | |
| } | |
| } | |
| internal ImageInfo GetImageInfo(byte[] image) | |
| { | |
| var hashProvider = new SHA1CryptoServiceProvider(); | |
| var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-",""); | |
| if (_images.ContainsKey(hash)) | |
| { | |
| return _images[hash]; | |
| } | |
| else | |
| { | |
| return null; | |
| } | |
| } | |
| private Uri GetNewUri(Packaging.ZipPackage package, string sUri) | |
| { | |
| int id = 1; | |
| Uri uri; | |
| do | |
| { | |
| uri = new Uri(string.Format(sUri, id++), UriKind.Relative); | |
| } | |
| while (package.PartExists(uri)); | |
| return uri; | |
| } | |
| /// <summary> | |
| /// Init values here | |
| /// </summary> | |
| private void Init() | |
| { | |
| DoAdjustDrawings = true; | |
| } | |
| /// <summary> | |
| /// Create a new file from a template | |
| /// </summary> | |
| /// <param name="template">An existing xlsx file to use as a template</param> | |
| /// <param name="password">The password to decrypt the package.</param> | |
| /// <returns></returns> | |
| private void CreateFromTemplate(FileInfo template, string password) | |
| { | |
| if (template != null) template.Refresh(); | |
| if (template.Exists) | |
| { | |
| if(_stream==null) _stream=new MemoryStream(); | |
| var ms = new MemoryStream(); | |
| if (password != null) | |
| { | |
| #if !MONO | |
| Encryption.IsEncrypted = true; | |
| Encryption.Password = password; | |
| var encrHandler = new EncryptedPackageHandler(); | |
| ms = encrHandler.DecryptPackage(template, Encryption); | |
| encrHandler = null; | |
| #endif | |
| #if MONO | |
| throw (new NotImplementedException("No support for Encrypted packages in Mono")); | |
| #endif | |
| //throw (new NotImplementedException("No support for Encrypted packages in this version")); | |
| } | |
| else | |
| { | |
| byte[] b = System.IO.File.ReadAllBytes(template.FullName); | |
| ms.Write(b, 0, b.Length); | |
| } | |
| try | |
| { | |
| //_package = Package.Open(_stream, FileMode.Open, FileAccess.ReadWrite); | |
| _package = new Packaging.ZipPackage(ms); | |
| } | |
| catch (Exception ex) | |
| { | |
| #if !MONO | |
| if (password == null && CompoundDocument.IsStorageFile(template.FullName)==0) | |
| { | |
| throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); | |
| } | |
| else | |
| { | |
| throw; | |
| } | |
| #endif | |
| #if MONO | |
| throw; | |
| #endif | |
| } | |
| } | |
| else | |
| throw new Exception("Passed invalid TemplatePath to Excel Template"); | |
| //return newFile; | |
| } | |
| private void ConstructNewFile(string password) | |
| { | |
| var ms = new MemoryStream(); | |
| if (_stream == null) _stream = new MemoryStream(); | |
| if (File != null) File.Refresh(); | |
| if (File != null && File.Exists) | |
| { | |
| if (password != null) | |
| { | |
| #if !MONO | |
| var encrHandler = new EncryptedPackageHandler(); | |
| Encryption.IsEncrypted = true; | |
| Encryption.Password = password; | |
| ms = encrHandler.DecryptPackage(File, Encryption); | |
| encrHandler = null; | |
| #endif | |
| #if MONO | |
| throw new NotImplementedException("No support for Encrypted packages in Mono"); | |
| #endif | |
| } | |
| else | |
| { | |
| byte[] b = System.IO.File.ReadAllBytes(File.FullName); | |
| ms.Write(b, 0, b.Length); | |
| } | |
| try | |
| { | |
| //_package = Package.Open(_stream, FileMode.Open, FileAccess.ReadWrite); | |
| _package = new Packaging.ZipPackage(ms); | |
| } | |
| catch (Exception ex) | |
| { | |
| #if !MONO | |
| if (password == null && CompoundDocument.IsStorageFile(File.FullName)==0) | |
| { | |
| throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); | |
| } | |
| else | |
| { | |
| throw; | |
| } | |
| #endif | |
| #if MONO | |
| throw; | |
| #endif | |
| } | |
| } | |
| else | |
| { | |
| //_package = Package.Open(_stream, FileMode.Create, FileAccess.ReadWrite); | |
| _package = new Packaging.ZipPackage(ms); | |
| CreateBlankWb(); | |
| } | |
| } | |
| private void CreateBlankWb() | |
| { | |
| XmlDocument workbook = Workbook.WorkbookXml; // this will create the workbook xml in the package | |
| // create the relationship to the main part | |
| _package.CreateRelationship(UriHelper.GetRelativeUri(new Uri("/xl", UriKind.Relative), Workbook.WorkbookUri), Packaging.TargetMode.Internal, schemaRelationships + "/officeDocument"); | |
| } | |
| /// <summary> | |
| /// Returns a reference to the package | |
| /// </summary> | |
| public Packaging.ZipPackage Package { get { return (_package); } } | |
| ExcelEncryption _encryption=null; | |
| /// <summary> | |
| /// Information how and if the package is encrypted | |
| /// </summary> | |
| public ExcelEncryption Encryption | |
| { | |
| get | |
| { | |
| if (_encryption == null) | |
| { | |
| _encryption = new ExcelEncryption(); | |
| } | |
| return _encryption; | |
| } | |
| } | |
| /// <summary> | |
| /// Returns a reference to the workbook component within the package. | |
| /// All worksheets and cells can be accessed through the workbook. | |
| /// </summary> | |
| public ExcelWorkbook Workbook | |
| { | |
| get | |
| { | |
| if (_workbook == null) | |
| { | |
| var nsm = CreateDefaultNSM(); | |
| _workbook = new ExcelWorkbook(this, nsm); | |
| _workbook.GetExternalReferences(); | |
| _workbook.GetDefinedNames(); | |
| } | |
| return (_workbook); | |
| } | |
| } | |
| /// <summary> | |
| /// Automaticlly adjust drawing size when column width/row height are adjusted, depending on the drawings editBy property. | |
| /// Default True | |
| /// </summary> | |
| public bool DoAdjustDrawings | |
| { | |
| get; | |
| set; | |
| } | |
| private XmlNamespaceManager CreateDefaultNSM() | |
| { | |
| // Create a NamespaceManager to handle the default namespace, | |
| // and create a prefix for the default namespace: | |
| NameTable nt = new NameTable(); | |
| var ns = new XmlNamespaceManager(nt); | |
| ns.AddNamespace(string.Empty, ExcelPackage.schemaMain); | |
| ns.AddNamespace("d", ExcelPackage.schemaMain); | |
| ns.AddNamespace("r", ExcelPackage.schemaRelationships); | |
| ns.AddNamespace("c", ExcelPackage.schemaChart); | |
| ns.AddNamespace("vt", schemaVt); | |
| // extended properties (app.xml) | |
| ns.AddNamespace("xp", schemaExtended); | |
| // custom properties | |
| ns.AddNamespace("ctp", schemaCustom); | |
| // core properties | |
| ns.AddNamespace("cp", schemaCore); | |
| // core property namespaces | |
| ns.AddNamespace("dc", schemaDc); | |
| ns.AddNamespace("dcterms", schemaDcTerms); | |
| ns.AddNamespace("dcmitype", schemaDcmiType); | |
| ns.AddNamespace("xsi", schemaXsi); | |
| return ns; | |
| } | |
| #region SavePart | |
| /// <summary> | |
| /// Saves the XmlDocument into the package at the specified Uri. | |
| /// </summary> | |
| /// <param name="uri">The Uri of the component</param> | |
| /// <param name="xmlDoc">The XmlDocument to save</param> | |
| internal void SavePart(Uri uri, XmlDocument xmlDoc) | |
| { | |
| Packaging.ZipPackagePart part = _package.GetPart(uri); | |
| xmlDoc.Save(part.GetStream(FileMode.Create, FileAccess.Write)); | |
| } | |
| /// <summary> | |
| /// Saves the XmlDocument into the package at the specified Uri. | |
| /// </summary> | |
| /// <param name="uri">The Uri of the component</param> | |
| /// <param name="xmlDoc">The XmlDocument to save</param> | |
| internal void SaveWorkbook(Uri uri, XmlDocument xmlDoc) | |
| { | |
| Packaging.ZipPackagePart part = _package.GetPart(uri); | |
| if(Workbook.VbaProject==null) | |
| { | |
| if (part.ContentType != contentTypeWorkbookDefault) | |
| { | |
| part = _package.CreatePart(uri, contentTypeWorkbookDefault, Compression); | |
| } | |
| } | |
| else | |
| { | |
| if (part.ContentType != contentTypeWorkbookMacroEnabled) | |
| { | |
| var rels = part.GetRelationships(); | |
| _package.DeletePart(uri); | |
| part = Package.CreatePart(uri, contentTypeWorkbookMacroEnabled); | |
| foreach (var rel in rels) | |
| { | |
| Package.DeleteRelationship(rel.Id); | |
| part.CreateRelationship(rel.TargetUri, rel.TargetMode, rel.RelationshipType); | |
| } | |
| } | |
| } | |
| xmlDoc.Save(part.GetStream(FileMode.Create, FileAccess.Write)); | |
| } | |
| #endregion | |
| #region Dispose | |
| /// <summary> | |
| /// Closes the package. | |
| /// </summary> | |
| public void Dispose() | |
| { | |
| if(_package != null) | |
| { | |
| if (_isExternalStream==false && Stream != null && (Stream.CanRead || Stream.CanWrite)) | |
| { | |
| Stream.Close(); | |
| } | |
| _package.Close(); | |
| if(_isExternalStream==false) ((IDisposable)_stream).Dispose(); | |
| if(_workbook != null) | |
| { | |
| _workbook.Dispose(); | |
| } | |
| _package = null; | |
| _images = null; | |
| _file = null; | |
| _workbook = null; | |
| _stream = null; | |
| _workbook = null; | |
| } | |
| } | |
| #endregion | |
| #region Save // ExcelPackage save | |
| /// <summary> | |
| /// Saves all the components back into the package. | |
| /// This method recursively calls the Save method on all sub-components. | |
| /// We close the package after the save is done. | |
| /// </summary> | |
| public void Save() | |
| { | |
| try | |
| { | |
| Workbook.Save(); | |
| if (File == null) | |
| { | |
| if(Encryption.IsEncrypted) | |
| { | |
| #if !MONO | |
| var ms = new MemoryStream(); | |
| _package.Save(ms); | |
| byte[] file = ms.ToArray(); | |
| EncryptedPackageHandler eph = new EncryptedPackageHandler(); | |
| var msEnc = eph.EncryptPackage(file, Encryption); | |
| CopyStream(msEnc, ref _stream); | |
| #endif | |
| #if MONO | |
| throw new NotSupportedException("Encryption is not supported under Mono."); | |
| #endif | |
| } | |
| else | |
| { | |
| _package.Save(_stream); | |
| } | |
| _stream.Flush(); | |
| _package.Close(); | |
| } | |
| else | |
| { | |
| if (System.IO.File.Exists(File.FullName)) | |
| { | |
| try | |
| { | |
| System.IO.File.Delete(File.FullName); | |
| } | |
| catch (Exception ex) | |
| { | |
| throw (new Exception(string.Format("Error overwriting file {0}", File.FullName), ex)); | |
| } | |
| } | |
| _package.Save(_stream); | |
| _package.Close(); | |
| if (Stream is MemoryStream) | |
| { | |
| var fi = new FileStream(File.FullName, FileMode.Create); | |
| //EncryptPackage | |
| if (Encryption.IsEncrypted) | |
| { | |
| #if !MONO | |
| byte[] file = ((MemoryStream)Stream).ToArray(); | |
| EncryptedPackageHandler eph = new EncryptedPackageHandler(); | |
| var ms = eph.EncryptPackage(file, Encryption); | |
| fi.Write(ms.GetBuffer(), 0, (int)ms.Length); | |
| #endif | |
| #if MONO | |
| throw new NotSupportedException("Encryption is not supported under Mono."); | |
| #endif | |
| } | |
| else | |
| { | |
| fi.Write(((MemoryStream)Stream).GetBuffer(), 0, (int)Stream.Length); | |
| } | |
| fi.Close(); | |
| } | |
| else | |
| { | |
| System.IO.File.WriteAllBytes(File.FullName, GetAsByteArray(false)); | |
| } | |
| } | |
| } | |
| catch (Exception ex) | |
| { | |
| if (File == null) | |
| { | |
| throw; | |
| } | |
| else | |
| { | |
| throw (new InvalidOperationException(string.Format("Error saving file {0}", File.FullName), ex)); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Saves all the components back into the package. | |
| /// This method recursively calls the Save method on all sub-components. | |
| /// The package is closed after it ha | |
| /// d to encrypt the workbook with. | |
| /// </summary> | |
| /// <param name="password">This parameter overrides the Workbook.Encryption.Password.</param> | |
| public void Save(string password) | |
| { | |
| Encryption.Password = password; | |
| Save(); | |
| } | |
| /// <summary> | |
| /// Saves the workbook to a new file | |
| /// The package is closed after it has been saved | |
| /// </summary> | |
| /// <param name="file">The file location</param> | |
| public void SaveAs(FileInfo file) | |
| { | |
| File = file; | |
| Save(); | |
| } | |
| /// <summary> | |
| /// Saves the workbook to a new file | |
| /// The package is closed after it has been saved | |
| /// </summary> | |
| /// <param name="file">The file</param> | |
| /// <param name="password">The password to encrypt the workbook with. | |
| /// This parameter overrides the Encryption.Password.</param> | |
| public void SaveAs(FileInfo file, string password) | |
| { | |
| File = file; | |
| Encryption.Password = password; | |
| Save(); | |
| } | |
| /// <summary> | |
| /// Copies the Package to the Outstream | |
| /// The package is closed after it has been saved | |
| /// </summary> | |
| /// <param name="OutputStream">The stream to copy the package to</param> | |
| public void SaveAs(Stream OutputStream) | |
| { | |
| File = null; | |
| Save(); | |
| if (OutputStream != _stream) | |
| { | |
| if (Encryption.IsEncrypted) | |
| { | |
| #if !MONO | |
| //Encrypt Workbook | |
| Byte[] file = new byte[Stream.Length]; | |
| long pos = Stream.Position; | |
| Stream.Seek(0, SeekOrigin.Begin); | |
| Stream.Read(file, 0, (int) Stream.Length); | |
| EncryptedPackageHandler eph = new EncryptedPackageHandler(); | |
| var ms = eph.EncryptPackage(file, Encryption); | |
| CopyStream(ms, ref OutputStream); | |
| #endif | |
| #if MONO | |
| throw new NotSupportedException("Encryption is not supported under Mono."); | |
| #endif | |
| } | |
| else | |
| { | |
| CopyStream(_stream, ref OutputStream); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Copies the Package to the Outstream | |
| /// The package is closed after it has been saved | |
| /// </summary> | |
| /// <param name="OutputStream">The stream to copy the package to</param> | |
| /// <param name="password">The password to encrypt the workbook with. | |
| /// This parameter overrides the Encryption.Password.</param> | |
| public void SaveAs(Stream OutputStream, string password) | |
| { | |
| Encryption.Password = password; | |
| SaveAs(OutputStream); | |
| } | |
| FileInfo _file = null; | |
| /// <summary> | |
| /// The output file. Null if no file is used | |
| /// </summary> | |
| public FileInfo File | |
| { | |
| get | |
| { | |
| return _file; | |
| } | |
| set | |
| { | |
| _file = value; | |
| } | |
| } | |
| /// <summary> | |
| /// The output stream. This stream is the not the encrypted package. | |
| /// To get the encrypted package use the SaveAs(stream) method. | |
| /// </summary> | |
| public Stream Stream | |
| { | |
| get | |
| { | |
| return _stream; | |
| } | |
| } | |
| #endregion | |
| /// <summary> | |
| /// Compression option for the package | |
| /// </summary> | |
| public CompressionLevel Compression | |
| { | |
| get | |
| { | |
| return Package.Compression; | |
| } | |
| set | |
| { | |
| Package.Compression = value; | |
| } | |
| } | |
| #region GetXmlFromUri | |
| /// <summary> | |
| /// Get the XmlDocument from an URI | |
| /// </summary> | |
| /// <param name="uri">The Uri to the part</param> | |
| /// <returns>The XmlDocument</returns> | |
| internal XmlDocument GetXmlFromUri(Uri uri) | |
| { | |
| XmlDocument xml = new XmlDocument(); | |
| Packaging.ZipPackagePart part = _package.GetPart(uri); | |
| XmlHelper.LoadXmlSafe(xml, part.GetStream()); | |
| return (xml); | |
| } | |
| #endregion | |
| /// <summary> | |
| /// Saves and returns the Excel files as a bytearray. | |
| /// Note that the package is closed upon save | |
| /// </summary> | |
| /// <example> | |
| /// Example how to return a document from a Webserver... | |
| /// <code> | |
| /// ExcelPackage package=new ExcelPackage(); | |
| /// /**** ... Create the document ****/ | |
| /// Byte[] bin = package.GetAsByteArray(); | |
| /// Response.ContentType = "Application/vnd.ms-Excel"; | |
| /// Response.AddHeader("content-disposition", "attachment; filename=TheFile.xlsx"); | |
| /// Response.BinaryWrite(bin); | |
| /// </code> | |
| /// </example> | |
| /// <returns></returns> | |
| public byte[] GetAsByteArray() | |
| { | |
| return GetAsByteArray(true); | |
| } | |
| /// <summary> | |
| /// Saves and returns the Excel files as a bytearray | |
| /// Note that the package is closed upon save | |
| /// </summary> | |
| /// <example> | |
| /// Example how to return a document from a Webserver... | |
| /// <code> | |
| /// ExcelPackage package=new ExcelPackage(); | |
| /// /**** ... Create the document ****/ | |
| /// Byte[] bin = package.GetAsByteArray(); | |
| /// Response.ContentType = "Application/vnd.ms-Excel"; | |
| /// Response.AddHeader("content-disposition", "attachment; filename=TheFile.xlsx"); | |
| /// Response.BinaryWrite(bin); | |
| /// </code> | |
| /// </example> | |
| /// <param name="password">The password to encrypt the workbook with. | |
| /// This parameter overrides the Encryption.Password.</param> | |
| /// <returns></returns> | |
| public byte[] GetAsByteArray(string password) | |
| { | |
| if (password != null) | |
| { | |
| Encryption.Password = password; | |
| } | |
| return GetAsByteArray(true); | |
| } | |
| internal byte[] GetAsByteArray(bool save) | |
| { | |
| if (save) | |
| { | |
| Workbook.Save(); | |
| _package.Close(); | |
| _package.Save(_stream); | |
| } | |
| Byte[] byRet = new byte[Stream.Length]; | |
| long pos = Stream.Position; | |
| Stream.Seek(0, SeekOrigin.Begin); | |
| Stream.Read(byRet, 0, (int)Stream.Length); | |
| //Encrypt Workbook? | |
| if (Encryption.IsEncrypted) | |
| { | |
| #if !MONO | |
| EncryptedPackageHandler eph=new EncryptedPackageHandler(); | |
| var ms = eph.EncryptPackage(byRet, Encryption); | |
| byRet = ms.ToArray(); | |
| #endif | |
| } | |
| Stream.Seek(pos, SeekOrigin.Begin); | |
| Stream.Close(); | |
| return byRet; | |
| } | |
| /// <summary> | |
| /// Loads the specified package data from a stream. | |
| /// </summary> | |
| /// <param name="input">The input.</param> | |
| public void Load(Stream input) | |
| { | |
| Load(input, new MemoryStream(), null); | |
| } | |
| /// <summary> | |
| /// Loads the specified package data from a stream. | |
| /// </summary> | |
| /// <param name="input">The input.</param> | |
| /// <param name="Password">The password to decrypt the document</param> | |
| public void Load(Stream input, string Password) | |
| { | |
| Load(input, new MemoryStream(), Password); | |
| } | |
| /// <summary> | |
| /// | |
| /// </summary> | |
| /// <param name="input"></param> | |
| /// <param name="output"></param> | |
| /// <param name="Password"></param> | |
| private void Load(Stream input, Stream output, string Password) | |
| { | |
| //Release some resources: | |
| if (this._package != null) | |
| { | |
| this._package.Close(); | |
| this._package = null; | |
| } | |
| if (this._stream != null) | |
| { | |
| this._stream.Close(); | |
| this._stream.Dispose(); | |
| this._stream = null; | |
| } | |
| _isExternalStream = true; | |
| if (input.Length == 0) // Template is blank, Construct new | |
| { | |
| _stream = output; | |
| ConstructNewFile(Password); | |
| } | |
| else | |
| { | |
| Stream ms; | |
| this._stream = output; | |
| if (Password != null) | |
| { | |
| #if !MONO | |
| Stream encrStream = new MemoryStream(); | |
| CopyStream(input, ref encrStream); | |
| EncryptedPackageHandler eph = new EncryptedPackageHandler(); | |
| Encryption.Password = Password; | |
| ms = eph.DecryptPackage((MemoryStream)encrStream, Encryption); | |
| #endif | |
| #if MONO | |
| throw new NotSupportedException("Encryption is not supported under Mono."); | |
| #endif | |
| } | |
| else | |
| { | |
| ms = new MemoryStream(); | |
| CopyStream(input, ref ms); | |
| } | |
| try | |
| { | |
| //this._package = Package.Open(this._stream, FileMode.Open, FileAccess.ReadWrite); | |
| _package = new Packaging.ZipPackage(ms); | |
| } | |
| catch (Exception ex) | |
| { | |
| #if !MONO | |
| EncryptedPackageHandler eph = new EncryptedPackageHandler(); | |
| if (Password == null && CompoundDocument.IsStorageILockBytes(CompoundDocument.GetLockbyte((MemoryStream)_stream)) == 0) | |
| { | |
| throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); | |
| } | |
| else | |
| { | |
| throw; | |
| } | |
| #endif | |
| #if MONO | |
| throw; | |
| #endif | |
| } | |
| } | |
| //Clear the workbook so that it gets reinitialized next time | |
| this._workbook = null; | |
| } | |
| static object _lock=new object(); | |
| /// <summary> | |
| /// Copies the input stream to the output stream. | |
| /// </summary> | |
| /// <param name="inputStream">The input stream.</param> | |
| /// <param name="outputStream">The output stream.</param> | |
| internal static void CopyStream(Stream inputStream, ref Stream outputStream) | |
| { | |
| if (!inputStream.CanRead) | |
| { | |
| throw (new Exception("Can not read from inputstream")); | |
| } | |
| if (!outputStream.CanWrite) | |
| { | |
| throw (new Exception("Can not write to outputstream")); | |
| } | |
| if (inputStream.CanSeek) | |
| { | |
| inputStream.Seek(0, SeekOrigin.Begin); | |
| } | |
| const int bufferLength = 8096; | |
| var buffer = new Byte[bufferLength]; | |
| lock (_lock) | |
| { | |
| int bytesRead = inputStream.Read(buffer, 0, bufferLength); | |
| // write the required bytes | |
| while (bytesRead > 0) | |
| { | |
| outputStream.Write(buffer, 0, bytesRead); | |
| bytesRead = inputStream.Read(buffer, 0, bufferLength); | |
| } | |
| outputStream.Flush(); | |
| } | |
| } | |
| } | |
| } |