| /******************************************************************************* |
| * 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 |
| * Jan Källman Total rewrite 2010-03-01 |
| * Jan Källman License changed GPL-->LGPL 2011-12-27 |
| * Raziq York Added Created & Modified 2014-08-20 |
| *******************************************************************************/ |
| using System; |
| using System.Xml; |
| using System.IO; |
| using System.Globalization; |
| using OfficeOpenXml.Utils; |
| |
| namespace OfficeOpenXml |
| { |
| /// <summary> |
| /// Provides access to the properties bag of the package |
| /// </summary> |
| public sealed class OfficeProperties : XmlHelper |
| { |
| #region Private Properties |
| private XmlDocument _xmlPropertiesCore; |
| private XmlDocument _xmlPropertiesExtended; |
| private XmlDocument _xmlPropertiesCustom; |
| |
| private Uri _uriPropertiesCore = new Uri("/docProps/core.xml", UriKind.Relative); |
| private Uri _uriPropertiesExtended = new Uri("/docProps/app.xml", UriKind.Relative); |
| private Uri _uriPropertiesCustom = new Uri("/docProps/custom.xml", UriKind.Relative); |
| |
| XmlHelper _coreHelper; |
| XmlHelper _extendedHelper; |
| XmlHelper _customHelper; |
| private ExcelPackage _package; |
| #endregion |
| |
| #region ExcelProperties Constructor |
| /// <summary> |
| /// Provides access to all the office document properties. |
| /// </summary> |
| /// <param name="package"></param> |
| /// <param name="ns"></param> |
| internal OfficeProperties(ExcelPackage package, XmlNamespaceManager ns) : |
| base(ns) |
| { |
| _package = package; |
| |
| _coreHelper = XmlHelperFactory.Create(ns, CorePropertiesXml.SelectSingleNode("cp:coreProperties", NameSpaceManager)); |
| _extendedHelper = XmlHelperFactory.Create(ns, ExtendedPropertiesXml); |
| _customHelper = XmlHelperFactory.Create(ns, CustomPropertiesXml); |
| |
| } |
| #endregion |
| #region CorePropertiesXml |
| /// <summary> |
| /// Provides access to the XML document that holds all the code |
| /// document properties. |
| /// </summary> |
| public XmlDocument CorePropertiesXml |
| { |
| get |
| { |
| if (_xmlPropertiesCore == null) |
| { |
| string xml = string.Format("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><cp:coreProperties xmlns:cp=\"{0}\" xmlns:dc=\"{1}\" xmlns:dcterms=\"{2}\" xmlns:dcmitype=\"{3}\" xmlns:xsi=\"{4}\"></cp:coreProperties>", |
| ExcelPackage.schemaCore, |
| ExcelPackage.schemaDc, |
| ExcelPackage.schemaDcTerms, |
| ExcelPackage.schemaDcmiType, |
| ExcelPackage.schemaXsi); |
| |
| _xmlPropertiesCore = GetXmlDocument(xml, _uriPropertiesCore, @"application/vnd.openxmlformats-package.core-properties+xml", @"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"); |
| } |
| return (_xmlPropertiesCore); |
| } |
| } |
| |
| private XmlDocument GetXmlDocument(string startXml, Uri uri, string contentType, string relationship) |
| { |
| XmlDocument xmlDoc; |
| if (_package.Package.PartExists(uri)) |
| xmlDoc = _package.GetXmlFromUri(uri); |
| else |
| { |
| xmlDoc = new XmlDocument(); |
| xmlDoc.LoadXml(startXml); |
| |
| // Create a the part and add to the package |
| Packaging.ZipPackagePart part = _package.Package.CreatePart(uri, contentType); |
| |
| // Save it to the package |
| StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write)); |
| xmlDoc.Save(stream); |
| //stream.Close(); |
| _package.Package.Flush(); |
| |
| // create the relationship between the workbook and the new shared strings part |
| _package.Package.CreateRelationship(UriHelper.GetRelativeUri(new Uri("/xl", UriKind.Relative), uri), Packaging.TargetMode.Internal, relationship); |
| _package.Package.Flush(); |
| } |
| return xmlDoc; |
| } |
| #endregion |
| #region Core Properties |
| const string TitlePath = "dc:title"; |
| /// <summary> |
| /// Gets/sets the title property of the document (core property) |
| /// </summary> |
| public string Title |
| { |
| get { return _coreHelper.GetXmlNodeString(TitlePath); } |
| set { _coreHelper.SetXmlNodeString(TitlePath, value); } |
| } |
| |
| const string SubjectPath = "dc:subject"; |
| /// <summary> |
| /// Gets/sets the subject property of the document (core property) |
| /// </summary> |
| public string Subject |
| { |
| get { return _coreHelper.GetXmlNodeString(SubjectPath); } |
| set { _coreHelper.SetXmlNodeString(SubjectPath, value); } |
| } |
| |
| const string AuthorPath = "dc:creator"; |
| /// <summary> |
| /// Gets/sets the author property of the document (core property) |
| /// </summary> |
| public string Author |
| { |
| get { return _coreHelper.GetXmlNodeString(AuthorPath); } |
| set { _coreHelper.SetXmlNodeString(AuthorPath, value); } |
| } |
| |
| const string CommentsPath = "dc:description"; |
| /// <summary> |
| /// Gets/sets the comments property of the document (core property) |
| /// </summary> |
| public string Comments |
| { |
| get { return _coreHelper.GetXmlNodeString(CommentsPath); } |
| set { _coreHelper.SetXmlNodeString(CommentsPath, value); } |
| } |
| |
| const string KeywordsPath = "cp:keywords"; |
| /// <summary> |
| /// Gets/sets the keywords property of the document (core property) |
| /// </summary> |
| public string Keywords |
| { |
| get { return _coreHelper.GetXmlNodeString(KeywordsPath); } |
| set { _coreHelper.SetXmlNodeString(KeywordsPath, value); } |
| } |
| |
| const string LastModifiedByPath = "cp:lastModifiedBy"; |
| /// <summary> |
| /// Gets/sets the lastModifiedBy property of the document (core property) |
| /// </summary> |
| public string LastModifiedBy |
| { |
| get { return _coreHelper.GetXmlNodeString(LastModifiedByPath); } |
| set |
| { |
| _coreHelper.SetXmlNodeString(LastModifiedByPath, value); |
| } |
| } |
| |
| const string LastPrintedPath = "cp:lastPrinted"; |
| /// <summary> |
| /// Gets/sets the lastPrinted property of the document (core property) |
| /// </summary> |
| public string LastPrinted |
| { |
| get { return _coreHelper.GetXmlNodeString(LastPrintedPath); } |
| set { _coreHelper.SetXmlNodeString(LastPrintedPath, value); } |
| } |
| |
| const string CreatedPath = "dcterms:created"; |
| |
| /// <summary> |
| /// Gets/sets the created property of the document (core property) |
| /// </summary> |
| public DateTime Created |
| { |
| get |
| { |
| DateTime date; |
| return DateTime.TryParse(_coreHelper.GetXmlNodeString(CreatedPath), out date) ? date : DateTime.MinValue; |
| } |
| set |
| { |
| var dateString = value.ToUniversalTime().ToString("s", CultureInfo.InvariantCulture) + "Z"; |
| _coreHelper.SetXmlNodeString(CreatedPath, dateString); |
| _coreHelper.SetXmlNodeString(CreatedPath + "/@xsi:type", "dcterms:W3CDTF"); |
| } |
| } |
| |
| const string CategoryPath = "cp:category"; |
| /// <summary> |
| /// Gets/sets the category property of the document (core property) |
| /// </summary> |
| public string Category |
| { |
| get { return _coreHelper.GetXmlNodeString(CategoryPath); } |
| set { _coreHelper.SetXmlNodeString(CategoryPath, value); } |
| } |
| |
| const string ContentStatusPath = "cp:contentStatus"; |
| /// <summary> |
| /// Gets/sets the status property of the document (core property) |
| /// </summary> |
| public string Status |
| { |
| get { return _coreHelper.GetXmlNodeString(ContentStatusPath); } |
| set { _coreHelper.SetXmlNodeString(ContentStatusPath, value); } |
| } |
| #endregion |
| |
| #region Extended Properties |
| #region ExtendedPropertiesXml |
| /// <summary> |
| /// Provides access to the XML document that holds the extended properties of the document (app.xml) |
| /// </summary> |
| public XmlDocument ExtendedPropertiesXml |
| { |
| get |
| { |
| if (_xmlPropertiesExtended == null) |
| { |
| _xmlPropertiesExtended = GetXmlDocument(string.Format("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><Properties xmlns:vt=\"{0}\" xmlns=\"{1}\"></Properties>", |
| ExcelPackage.schemaVt, |
| ExcelPackage.schemaExtended), |
| _uriPropertiesExtended, |
| @"application/vnd.openxmlformats-officedocument.extended-properties+xml", |
| @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"); |
| } |
| return (_xmlPropertiesExtended); |
| } |
| } |
| #endregion |
| |
| const string ApplicationPath = "xp:Properties/xp:Application"; |
| /// <summary> |
| /// Gets/Set the Application property of the document (extended property) |
| /// </summary> |
| public string Application |
| { |
| get { return _extendedHelper.GetXmlNodeString(ApplicationPath); } |
| set { _extendedHelper.SetXmlNodeString(ApplicationPath, value); } |
| } |
| |
| const string HyperlinkBasePath = "xp:Properties/xp:HyperlinkBase"; |
| /// <summary> |
| /// Gets/sets the HyperlinkBase property of the document (extended property) |
| /// </summary> |
| public Uri HyperlinkBase |
| { |
| get { return new Uri(_extendedHelper.GetXmlNodeString(HyperlinkBasePath), UriKind.Absolute); } |
| set { _extendedHelper.SetXmlNodeString(HyperlinkBasePath, value.AbsoluteUri); } |
| } |
| |
| const string AppVersionPath = "xp:Properties/xp:AppVersion"; |
| /// <summary> |
| /// Gets/Set the AppVersion property of the document (extended property) |
| /// </summary> |
| public string AppVersion |
| { |
| get { return _extendedHelper.GetXmlNodeString(AppVersionPath); } |
| set { _extendedHelper.SetXmlNodeString(AppVersionPath, value); } |
| } |
| const string CompanyPath = "xp:Properties/xp:Company"; |
| |
| /// <summary> |
| /// Gets/sets the Company property of the document (extended property) |
| /// </summary> |
| public string Company |
| { |
| get { return _extendedHelper.GetXmlNodeString(CompanyPath); } |
| set { _extendedHelper.SetXmlNodeString(CompanyPath, value); } |
| } |
| |
| const string ManagerPath = "xp:Properties/xp:Manager"; |
| /// <summary> |
| /// Gets/sets the Manager property of the document (extended property) |
| /// </summary> |
| public string Manager |
| { |
| get { return _extendedHelper.GetXmlNodeString(ManagerPath); } |
| set { _extendedHelper.SetXmlNodeString(ManagerPath, value); } |
| } |
| |
| const string ModifiedPath = "dcterms:modified"; |
| /// <summary> |
| /// Gets/sets the modified property of the document (core property) |
| /// </summary> |
| public DateTime Modified |
| { |
| get |
| { |
| DateTime date; |
| return DateTime.TryParse(_coreHelper.GetXmlNodeString(ModifiedPath), out date) ? date : DateTime.MinValue; |
| } |
| set |
| { |
| var dateString = value.ToUniversalTime().ToString("s", CultureInfo.InvariantCulture) + "Z"; |
| _coreHelper.SetXmlNodeString(ModifiedPath, dateString); |
| _coreHelper.SetXmlNodeString(ModifiedPath + "/@xsi:type", "dcterms:W3CDTF"); |
| } |
| } |
| |
| #region Get and Set Extended Properties |
| private string GetExtendedPropertyValue(string propertyName) |
| { |
| string retValue = null; |
| string searchString = string.Format("xp:Properties/xp:{0}", propertyName); |
| XmlNode node = ExtendedPropertiesXml.SelectSingleNode(searchString, NameSpaceManager); |
| if (node != null) |
| { |
| retValue = node.InnerText; |
| } |
| return retValue; |
| } |
| #endregion |
| #endregion |
| |
| #region Custom Properties |
| |
| #region CustomPropertiesXml |
| /// <summary> |
| /// Provides access to the XML document which holds the document's custom properties |
| /// </summary> |
| public XmlDocument CustomPropertiesXml |
| { |
| get |
| { |
| if (_xmlPropertiesCustom == null) |
| { |
| _xmlPropertiesCustom = GetXmlDocument(string.Format("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><Properties xmlns:vt=\"{0}\" xmlns=\"{1}\"></Properties>", |
| ExcelPackage.schemaVt, |
| ExcelPackage.schemaCustom), |
| _uriPropertiesCustom, |
| @"application/vnd.openxmlformats-officedocument.custom-properties+xml", |
| @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"); |
| } |
| return (_xmlPropertiesCustom); |
| } |
| } |
| #endregion |
| |
| #region Get and Set Custom Properties |
| /// <summary> |
| /// Gets the value of a custom property |
| /// </summary> |
| /// <param name="propertyName">The name of the property</param> |
| /// <returns>The current value of the property</returns> |
| public object GetCustomPropertyValue(string propertyName) |
| { |
| string searchString = string.Format("ctp:Properties/ctp:property[@name='{0}']", propertyName); |
| XmlElement node = CustomPropertiesXml.SelectSingleNode(searchString, NameSpaceManager) as XmlElement; |
| if (node != null) |
| { |
| string value = node.LastChild.InnerText; |
| switch (node.LastChild.LocalName) |
| { |
| case "filetime": |
| DateTime dt; |
| if (DateTime.TryParse(value, out dt)) |
| { |
| return dt; |
| } |
| else |
| { |
| return null; |
| } |
| case "i4": |
| int i; |
| if (int.TryParse(value, out i)) |
| { |
| return i; |
| } |
| else |
| { |
| return null; |
| } |
| case "r8": |
| double d; |
| if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) |
| { |
| return d; |
| } |
| else |
| { |
| return null; |
| } |
| case "bool": |
| if (value == "true") |
| { |
| return true; |
| } |
| else if (value == "false") |
| { |
| return false; |
| } |
| else |
| { |
| return null; |
| } |
| default: |
| return value; |
| } |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| /// <summary> |
| /// Allows you to set the value of a current custom property or create your own custom property. |
| /// </summary> |
| /// <param name="propertyName">The name of the property</param> |
| /// <param name="value">The value of the property</param> |
| public void SetCustomPropertyValue(string propertyName, object value) |
| { |
| XmlNode allProps = CustomPropertiesXml.SelectSingleNode(@"ctp:Properties", NameSpaceManager); |
| |
| var prop = string.Format("ctp:Properties/ctp:property[@name='{0}']", propertyName); |
| XmlElement node = CustomPropertiesXml.SelectSingleNode(prop, NameSpaceManager) as XmlElement; |
| if (node == null) |
| { |
| int pid; |
| var MaxNode = CustomPropertiesXml.SelectSingleNode("ctp:Properties/ctp:property[not(@pid <= preceding-sibling::ctp:property/@pid) and not(@pid <= following-sibling::ctp:property/@pid)]", NameSpaceManager); |
| if (MaxNode == null) |
| { |
| pid = 2; |
| } |
| else |
| { |
| if (!int.TryParse(MaxNode.Attributes["pid"].Value, out pid)) |
| { |
| pid = 2; |
| } |
| pid++; |
| } |
| node = CustomPropertiesXml.CreateElement("property", ExcelPackage.schemaCustom); |
| node.SetAttribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"); |
| node.SetAttribute("pid", pid.ToString()); // custom property pid |
| node.SetAttribute("name", propertyName); |
| |
| allProps.AppendChild(node); |
| } |
| else |
| { |
| while (node.ChildNodes.Count > 0) node.RemoveChild(node.ChildNodes[0]); |
| } |
| XmlElement valueElem; |
| if (value is bool) |
| { |
| valueElem = CustomPropertiesXml.CreateElement("vt", "bool", ExcelPackage.schemaVt); |
| valueElem.InnerText = value.ToString().ToLower(CultureInfo.InvariantCulture); |
| } |
| else if (value is DateTime) |
| { |
| valueElem = CustomPropertiesXml.CreateElement("vt", "filetime", ExcelPackage.schemaVt); |
| valueElem.InnerText = ((DateTime)value).AddHours(-1).ToString("yyyy-MM-ddTHH:mm:ssZ"); |
| } |
| else if (value is short || value is int) |
| { |
| valueElem = CustomPropertiesXml.CreateElement("vt", "i4", ExcelPackage.schemaVt); |
| valueElem.InnerText = value.ToString(); |
| } |
| else if (value is double || value is decimal || value is float || value is long) |
| { |
| valueElem = CustomPropertiesXml.CreateElement("vt", "r8", ExcelPackage.schemaVt); |
| if (value is double) |
| { |
| valueElem.InnerText = ((double)value).ToString(CultureInfo.InvariantCulture); |
| } |
| else if (value is float) |
| { |
| valueElem.InnerText = ((float)value).ToString(CultureInfo.InvariantCulture); |
| } |
| else if (value is decimal) |
| { |
| valueElem.InnerText = ((decimal)value).ToString(CultureInfo.InvariantCulture); |
| } |
| else |
| { |
| valueElem.InnerText = value.ToString(); |
| } |
| } |
| else |
| { |
| valueElem = CustomPropertiesXml.CreateElement("vt", "lpwstr", ExcelPackage.schemaVt); |
| valueElem.InnerText = value.ToString(); |
| } |
| node.AppendChild(valueElem); |
| } |
| #endregion |
| #endregion |
| |
| #region Save |
| /// <summary> |
| /// Saves the document properties back to the package. |
| /// </summary> |
| internal void Save() |
| { |
| if (_xmlPropertiesCore != null) |
| { |
| _package.SavePart(_uriPropertiesCore, _xmlPropertiesCore); |
| } |
| if (_xmlPropertiesExtended != null) |
| { |
| _package.SavePart(_uriPropertiesExtended, _xmlPropertiesExtended); |
| } |
| if (_xmlPropertiesCustom != null) |
| { |
| _package.SavePart(_uriPropertiesCustom, _xmlPropertiesCustom); |
| } |
| |
| } |
| #endregion |
| |
| } |
| } |