/*******************************************************************************
* 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
}
}