YNICTE/packages/OfficeOpenXml.Core.ExcelPac.../src/OfficeOpenXml.Core.ExcelPac.../OfficeProperties.cs

547 lines
19 KiB
C#

/*
* You may amend and distribute as you like, but don't remove this header!
*
* ExcelPackage provides server-side generation of Excel 2007 spreadsheets.
* See http://www.codeplex.com/ExcelPackage for details.
*
* Copyright 2007 © Dr John Tunnicliffe
* mailto:dr.john.tunnicliffe@btinternet.com
* All rights reserved.
*
* ExcelPackage is an Open Source project provided under the
* GNU General Public License (GPL) as published by the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* The GNU General Public License can be viewed at http://www.opensource.org/licenses/gpl-license.php
* If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
*
* The code for this project may be used and redistributed by any means PROVIDING it is
* not sold for profit without the author's written consent, and providing that this notice
* and the author's name and all copyright notices remain intact.
*
* 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
* ******************************************************************************
* John Tunnicliffe Initial Release 01-Jan-2007
* ******************************************************************************
*/
using System;
using System.IO;
using System.IO.Packaging;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
namespace OfficeOpenXml.Core.ExcelPackage
{
/// <summary>
/// Provides access to the properties bag of any office document (i.e. Word, Excel etc.)
/// </summary>
public class OfficeProperties
{
#region Private Properties
private const string schemaCore = @"http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
private const string schemeExtended = @"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
private const string schemaCustom = @"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
private const string schemaDc = @"http://purl.org/dc/elements/1.1/";
private const string schemaDcTerms = @"http://purl.org/dc/terms/";
private const string schemaDcmiType = @"http://purl.org/dc/dcmitype/";
private const string schemaXsi = @"http://www.w3.org/2001/XMLSchema-instance";
private const string schemaVt = @"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes";
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);
private XDocument _xmlPropertiesCore;
private XDocument _xmlPropertiesExtended;
private XDocument _xmlPropertiesCustom;
private ExcelPackage _xlPackage;
private XmlNamespaceManager _nsManager;
#endregion
#region ExcelProperties Constructor
/// <summary>
/// Provides access to all the office document properties.
/// </summary>
/// <param name="xlPackage"></param>
public OfficeProperties(ExcelPackage xlPackage)
{
_xlPackage = xlPackage;
// Create a NamespaceManager to handle the default namespace,
// and create a prefix for the default namespace:
NameTable nt = new NameTable();
_nsManager = new XmlNamespaceManager(nt);
// default namespace
_nsManager.AddNamespace("d", ExcelPackage.schemaMain.NamespaceName);
_nsManager.AddNamespace("vt", schemaVt);
// extended properties (app.xml)
_nsManager.AddNamespace("xp", schemeExtended);
// custom properties
_nsManager.AddNamespace("ctp", schemaCustom);
// core properties
_nsManager.AddNamespace("cp", schemaCore);
// core property namespaces
_nsManager.AddNamespace("dc", schemaDc);
_nsManager.AddNamespace("dcterms", schemaDcTerms);
_nsManager.AddNamespace("dcmitype", schemaDcmiType);
_nsManager.AddNamespace("xsi", schemaXsi);
}
#endregion
#region Protected Internal Properties
/// <summary>
/// The URI to the core properties component (core.xml)
/// </summary>
protected internal Uri CorePropertiesUri { get { return (_uriPropertiesCore); } }
/// <summary>
/// The URI to the extended properties component (app.xml)
/// </summary>
protected internal Uri ExtendedPropertiesUri { get { return (_uriPropertiesExtended); } }
/// <summary>
/// The URI to the custom properties component (custom.xml)
/// </summary>
protected internal Uri CustomPropertiesUri { get { return (_uriPropertiesCustom); } }
#endregion
#region Core Properties
#region CorePropertiesXml
/// <summary>
/// Provides access to the XML document that holds all the code
/// document properties.
/// </summary>
public XDocument CorePropertiesXml
{
get
{
if (_xmlPropertiesCore == null)
{
if (_xlPackage.Package.PartExists(CorePropertiesUri))
_xmlPropertiesCore = _xlPackage.GetXmlFromUri(CorePropertiesUri);
else
{
// create a new document properties part and add to the package
PackagePart partCore = _xlPackage.Package.CreatePart(CorePropertiesUri, @"application/vnd.openxmlformats-package.core-properties+xml", CompressionOption.Normal);
// create the document properties XML (with no entries in it)
_xmlPropertiesCore = new XDocument(
new XElement("cp:coreProperties")
.AddSchemaAttribute(schemaCore, "cp")
.AddSchemaAttribute(schemaDc, "dc")
.AddSchemaAttribute(schemaDcTerms, "dcterms")
.AddSchemaAttribute(schemaDcmiType, "dcmitype")
.AddSchemaAttribute(schemaXsi, "xsi")
);
// save it to the package
StreamWriter streamCore = new StreamWriter(partCore.GetStream(FileMode.Create, FileAccess.Write));
_xmlPropertiesCore.Save(streamCore);
streamCore.Dispose();
_xlPackage.Package.Flush();
// create the relationship between the workbook and the new shared strings part
_xlPackage.Package.CreateRelationship(CorePropertiesUri, TargetMode.Internal, @"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties");
_xlPackage.Package.Flush();
}
}
return (_xmlPropertiesCore);
}
}
#endregion
/// <summary>
/// Gets/sets the title property of the document (core property)
/// </summary>
public string Title
{
get { return GetCorePropertyValue("dc", "title"); }
set { SetCorePropertyValue("dc", "title", value); }
}
/// <summary>
/// Gets/sets the subject property of the document (core property)
/// </summary>
public string Subject
{
get { return GetCorePropertyValue("dc", "subject"); }
set { SetCorePropertyValue("dc", "subject", value); }
}
/// <summary>
/// Gets/sets the author property of the document (core property)
/// </summary>
public string Author
{
get { return GetCorePropertyValue("dc", "creator"); }
set { SetCorePropertyValue("dc", "creator", value); }
}
/// <summary>
/// Gets/sets the comments property of the document (core property)
/// </summary>
public string Comments
{
get { return GetCorePropertyValue("dc", "description"); }
set { SetCorePropertyValue("dc", "description", value); }
}
/// <summary>
/// Gets/sets the keywords property of the document (core property)
/// </summary>
public string Keywords
{
get { return GetCorePropertyValue("cp", "keywords"); }
set { SetCorePropertyValue("cp", "keywords", value); }
}
/// <summary>
/// Gets/sets the lastModifiedBy property of the document (core property)
/// </summary>
public string LastModifiedBy
{
get { return GetCorePropertyValue("cp", "lastModifiedBy"); }
set { SetCorePropertyValue("cp", "lastModifiedBy", value); }
}
/// <summary>
/// Gets/sets the lastPrinted property of the document (core property)
/// </summary>
public string LastPrinted
{
get { return GetCorePropertyValue("cp", "lastPrinted"); }
set { SetCorePropertyValue("cp", "lastPrinted", value); }
}
/// <summary>
/// Gets/sets the category property of the document (core property)
/// </summary>
public string Category
{
get { return GetCorePropertyValue("cp", "category"); }
set { SetCorePropertyValue("cp", "category", value); }
}
/// <summary>
/// Gets/sets the status property of the document (core property)
/// </summary>
public string Status
{
get { return GetCorePropertyValue("cp", "contentStatus"); }
set { SetCorePropertyValue("cp", "contentStatus", value); }
}
#region Get and Set Core Properties
/// <summary>
/// Gets the value of a core property
/// Private method, for internal use only!
/// </summary>
/// <param name="nameSpace">The namespace of the property</param>
/// <param name="propertyName">The property name</param>
/// <returns>The current value of the property</returns>
private string GetCorePropertyValue(string nameSpace, string propertyName)
{
string retValue = null;
string searchString = string.Format("//cp:coreProperties/{0}:{1}", nameSpace, propertyName);
var node = CorePropertiesXml.XPathSelectElement(searchString, _nsManager);
if (node != null)
{
retValue = node.Value;
}
return retValue;
}
/// <summary>
/// Sets a core property value.
/// Private method, for internal use only!
/// </summary>
/// <param name="nameSpace">The property's namespace</param>
/// <param name="propertyName">The name of the property</param>
/// <param name="propValue">The value of the property</param>
private void SetCorePropertyValue(string nameSpace, string propertyName, string propValue)
{
string searchString = string.Format("//cp:coreProperties/{0}:{1}", nameSpace, propertyName);
var node = CorePropertiesXml.XPathSelectElement(searchString, _nsManager);
if (node == null)
{
// the property does not exist, so create the XML node
string schema = schemaCore;
switch (nameSpace)
{
case "cp": schema = schemaCore; break;
case "dc": schema = schemaDc; break;
case "dcterms": schema = schemaDcTerms; break;
case "dcmitype": schema = schemaDcmiType; break;
case "xsi": schema = schemaXsi; break;
}
node = new XElement(propertyName)
.AddSchemaAttribute(schema, nameSpace);
CorePropertiesXml.Document.Add(node);
}
node.Value = propValue;
}
#endregion
#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 XDocument ExtendedPropertiesXml
{
get
{
if (_xmlPropertiesExtended == null)
{
if (_xlPackage.Package.PartExists(ExtendedPropertiesUri))
_xmlPropertiesExtended = _xlPackage.GetXmlFromUri(ExtendedPropertiesUri);
else
{
// create a new extended properties part and add to the package
PackagePart partExtended = _xlPackage.Package.CreatePart(ExtendedPropertiesUri, @"application/vnd.openxmlformats-officedocument.extended-properties+xml", CompressionOption.Normal);
// create the extended properties XML (with no entries in it)
_xmlPropertiesExtended = new XDocument(
new XElement("Properties")
.AddSchemaAttribute(XNamespace.Xmlns, schemeExtended)
.AddSchemaAttribute(schemaVt, "vt"));
// save it to the package
StreamWriter streamExtended = new StreamWriter(partExtended.GetStream(FileMode.Create, FileAccess.Write));
_xmlPropertiesExtended.Save(streamExtended);
streamExtended.Dispose();
_xlPackage.Package.Flush();
// create the relationship between the workbook and the new shared strings part
_xlPackage.Package.CreateRelationship(ExtendedPropertiesUri, TargetMode.Internal, @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties");
_xlPackage.Package.Flush();
}
}
return (_xmlPropertiesExtended);
}
}
#endregion
/// <summary>
/// Gets the Application property of the document (extended property)
/// </summary>
public string Application
{
get { return GetExtendedPropertyValue("Application"); }
}
/// <summary>
/// Gets/sets the HyperlinkBase property of the document (extended property)
/// </summary>
public Uri HyperlinkBase
{
get { return new Uri(GetExtendedPropertyValue("HyperlinkBase")); }
set { SetExtendedPropertyValue("HyperlinkBase", value.ToString()); }
}
/// <summary>
/// Gets the AppVersion property of the document (extended property)
/// </summary>
public string AppVersion
{
get { return GetExtendedPropertyValue("AppVersion"); }
}
/// <summary>
/// Gets/sets the Company property of the document (extended property)
/// </summary>
public string Company
{
get { return GetExtendedPropertyValue("Company"); }
set { SetExtendedPropertyValue("Company", value); }
}
/// <summary>
/// Gets/sets the Manager property of the document (extended property)
/// </summary>
public string Manager
{
get { return GetExtendedPropertyValue("Manager"); }
set { SetExtendedPropertyValue("Manager", value); }
}
#region Get and Set Extended Properties
private string GetExtendedPropertyValue(string propertyName)
{
string retValue = null;
string searchString = string.Format("//xp:Properties/xp:{0}", propertyName);
var node = ExtendedPropertiesXml.XPathSelectElement(searchString, _nsManager);
if (node != null)
{
retValue = node.Value;
}
return retValue;
}
private void SetExtendedPropertyValue(string propertyName, string propValue)
{
string searchString = string.Format("//xp:Properties/xp:{0}", propertyName);
var node = ExtendedPropertiesXml.XPathSelectElement(searchString, _nsManager);
if (node == null)
{
// the property does not exist, so create the XML node
node =
new XElement(propertyName)
.AddSchemaAttribute(XNamespace.Xmlns, schemeExtended);
ExtendedPropertiesXml.Document.Add(node);
}
node.Value = propValue;
}
#endregion
#endregion
#region Custom Properties
#region CustomPropertiesXml
/// <summary>
/// Provides access to the XML document which holds the document's custom properties
/// </summary>
public XDocument CustomPropertiesXml
{
get
{
if (_xmlPropertiesCustom == null)
{
if (_xlPackage.Package.PartExists(CustomPropertiesUri))
_xmlPropertiesCustom = _xlPackage.GetXmlFromUri(CustomPropertiesUri);
else
{
// create a new extended properties part and add to the package
PackagePart partCustom = _xlPackage.Package.CreatePart(CustomPropertiesUri, @"application/vnd.openxmlformats-officedocument.custom-properties+xml", CompressionOption.Normal);
// create the extended properties XML (with no entries in it)
_xmlPropertiesCustom = new XDocument(
new XElement("Properties")
.AddSchemaAttribute(XNamespace.Xmlns, schemaCustom)
.AddSchemaAttribute(schemaVt, "vt"));
// save it to the package
StreamWriter streamCustom = new StreamWriter(partCustom.GetStream(FileMode.Create, FileAccess.Write));
_xmlPropertiesCustom.Save(streamCustom);
streamCustom.Dispose();
_xlPackage.Package.Flush();
// create the relationship between the workbook and the new shared strings part
_xlPackage.Package.CreateRelationship(CustomPropertiesUri, TargetMode.Internal, @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties");
_xlPackage.Package.Flush();
}
}
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 string GetCustomPropertyValue(string propertyName)
{
string retValue = null;
string searchString = string.Format("//ctp:Properties/ctp:property/@name[.='{0}']", propertyName);
var node = CustomPropertiesXml.XPathSelectElement(searchString, _nsManager);
if (node != null)
{
retValue = (node.LastNode as XElement).Value;
}
return retValue;
}
/// <summary>
/// Allows you to set the value of a current custom property or create
/// your own custom property.
/// Currently only supports string values.
/// </summary>
/// <param name="propertyName">The name of the property</param>
/// <param name="propValue">The value of the property</param>
public void SetCustomPropertyValue(string propertyName, string propValue)
{
// TODO: provide support for other custom property data types
string searchString = @"//ctp:Properties";
var allProps = CustomPropertiesXml.XPathSelectElement(searchString, _nsManager);
searchString = string.Format("//ctp:Properties/ctp:property/@ctp:name[.='{0}']", propertyName);
var node = CustomPropertiesXml.XPathSelectElement(searchString, _nsManager);
if (node == null)
{
// the property does not exist, so first find the max PID
int pid = 4;
foreach (var prop in allProps.Nodes())
{
var attr = ((XElement)prop).Attribute("pid");
if (attr != null)
{
int attrValue = int.Parse(attr.Value);
if (attrValue > pid)
pid = attrValue;
}
}
pid++;
// the property does not exist, so create the XML node
var element = new XElement("property")
.AddSchemaAttribute(XNamespace.Xmlns, schemaCustom);
element.SetAttribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}");
element.SetAttribute("pid", pid.ToString()); // custom property pid
element.SetAttribute("name", propertyName);
XElement valueElement = new XElement("vt").AddSchemaAttribute("lpwstr", schemaVt);
valueElement.Value = propValue;
element.Add(valueElement);
CustomPropertiesXml.Document.Add(element);
}
else
{
(node.LastNode as XElement).Value = propValue;
}
}
#endregion
#endregion
#region Save // OfficeProperties save
/// <summary>
/// Saves the office document properties back to the package (if they exist!).
/// </summary>
protected internal void Save()
{
if (_xmlPropertiesCore != null)
{
_xlPackage.WriteDebugFile(_xmlPropertiesCore, "docProps", "core.xml");
_xlPackage.SavePart(CorePropertiesUri, _xmlPropertiesCore);
}
if (_xmlPropertiesExtended != null)
{
_xlPackage.WriteDebugFile(_xmlPropertiesExtended, "docProps", "app.xml");
_xlPackage.SavePart(ExtendedPropertiesUri, _xmlPropertiesExtended);
}
if (_xmlPropertiesCustom != null)
{
_xlPackage.WriteDebugFile(_xmlPropertiesCustom, "docProps", "custom.xml");
_xlPackage.SavePart(CustomPropertiesUri, _xmlPropertiesCustom);
}
}
#endregion
}
}