/******************************************************************************* * 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 { /// /// Provides access to the properties bag of the package /// 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 /// /// Provides access to all the office document properties. /// /// /// 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 /// /// Provides access to the XML document that holds all the code /// document properties. /// public XmlDocument CorePropertiesXml { get { if (_xmlPropertiesCore == null) { string xml = string.Format("", 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"; /// /// Gets/sets the title property of the document (core property) /// public string Title { get { return _coreHelper.GetXmlNodeString(TitlePath); } set { _coreHelper.SetXmlNodeString(TitlePath, value); } } const string SubjectPath = "dc:subject"; /// /// Gets/sets the subject property of the document (core property) /// public string Subject { get { return _coreHelper.GetXmlNodeString(SubjectPath); } set { _coreHelper.SetXmlNodeString(SubjectPath, value); } } const string AuthorPath = "dc:creator"; /// /// Gets/sets the author property of the document (core property) /// public string Author { get { return _coreHelper.GetXmlNodeString(AuthorPath); } set { _coreHelper.SetXmlNodeString(AuthorPath, value); } } const string CommentsPath = "dc:description"; /// /// Gets/sets the comments property of the document (core property) /// public string Comments { get { return _coreHelper.GetXmlNodeString(CommentsPath); } set { _coreHelper.SetXmlNodeString(CommentsPath, value); } } const string KeywordsPath = "cp:keywords"; /// /// Gets/sets the keywords property of the document (core property) /// public string Keywords { get { return _coreHelper.GetXmlNodeString(KeywordsPath); } set { _coreHelper.SetXmlNodeString(KeywordsPath, value); } } const string LastModifiedByPath = "cp:lastModifiedBy"; /// /// Gets/sets the lastModifiedBy property of the document (core property) /// public string LastModifiedBy { get { return _coreHelper.GetXmlNodeString(LastModifiedByPath); } set { _coreHelper.SetXmlNodeString(LastModifiedByPath, value); } } const string LastPrintedPath = "cp:lastPrinted"; /// /// Gets/sets the lastPrinted property of the document (core property) /// public string LastPrinted { get { return _coreHelper.GetXmlNodeString(LastPrintedPath); } set { _coreHelper.SetXmlNodeString(LastPrintedPath, value); } } const string CreatedPath = "dcterms:created"; /// /// Gets/sets the created property of the document (core property) /// 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); } } const string CategoryPath = "cp:category"; /// /// Gets/sets the category property of the document (core property) /// public string Category { get { return _coreHelper.GetXmlNodeString(CategoryPath); } set { _coreHelper.SetXmlNodeString(CategoryPath, value); } } const string ContentStatusPath = "cp:contentStatus"; /// /// Gets/sets the status property of the document (core property) /// public string Status { get { return _coreHelper.GetXmlNodeString(ContentStatusPath); } set { _coreHelper.SetXmlNodeString(ContentStatusPath, value); } } #endregion #region Extended Properties #region ExtendedPropertiesXml /// /// Provides access to the XML document that holds the extended properties of the document (app.xml) /// public XmlDocument ExtendedPropertiesXml { get { if (_xmlPropertiesExtended == null) { _xmlPropertiesExtended = GetXmlDocument(string.Format("", 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"; /// /// Gets the Application property of the document (extended property) /// public string Application { get { return _extendedHelper.GetXmlNodeString(ApplicationPath); } } const string HyperlinkBasePath = "xp:Properties/xp:HyperlinkBase"; /// /// Gets/sets the HyperlinkBase property of the document (extended property) /// 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"; /// /// Gets the AppVersion property of the document (extended property) /// public string AppVersion { get { return _extendedHelper.GetXmlNodeString(AppVersionPath); } } const string CompanyPath = "xp:Properties/xp:Company"; /// /// Gets/sets the Company property of the document (extended property) /// public string Company { get { return _extendedHelper.GetXmlNodeString(CompanyPath); } set { _extendedHelper.SetXmlNodeString(CompanyPath, value); } } const string ManagerPath = "xp:Properties/xp:Manager"; /// /// Gets/sets the Manager property of the document (extended property) /// public string Manager { get { return _extendedHelper.GetXmlNodeString(ManagerPath); } set { _extendedHelper.SetXmlNodeString(ManagerPath, value); } } const string ModifiedPath = "dcterms:modified"; /// /// Gets/sets the modified property of the document (core property) /// 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); } } #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 /// /// Provides access to the XML document which holds the document's custom properties /// public XmlDocument CustomPropertiesXml { get { if (_xmlPropertiesCustom == null) { _xmlPropertiesCustom = GetXmlDocument(string.Format("", 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 /// /// Gets the value of a custom property /// /// The name of the property /// The current value of the property 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; } } /// /// Allows you to set the value of a current custom property or create your own custom property. /// /// The name of the property /// The value of the property 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 /// /// Saves the document properties back to the package. /// 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 } }