You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
765 lines
21 KiB
765 lines
21 KiB
/* ====================================================================
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
this work for Additional information regarding copyright ownership.
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
(the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==================================================================== */
|
|
|
|
using System;
|
|
using System.IO;
|
|
using NPOI.OpenXml4Net.Exceptions;
|
|
using NPOI.OpenXml4Net.OPC;
|
|
using NPOI.OpenXml4Net.OPC.Internal;
|
|
using NPOI.OpenXmlFormats;
|
|
|
|
namespace NPOI
|
|
{
|
|
/**
|
|
* The core document properties
|
|
*/
|
|
public class CoreProperties
|
|
{
|
|
private PackagePropertiesPart part;
|
|
internal CoreProperties(PackagePropertiesPart part)
|
|
{
|
|
this.part = part;
|
|
}
|
|
|
|
public String Category
|
|
{
|
|
get
|
|
{
|
|
return part.GetCategoryProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetCategoryProperty(value);
|
|
}
|
|
}
|
|
public String ContentStatus
|
|
{
|
|
get
|
|
{
|
|
return part.GetContentStatusProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetContentStatusProperty(value);
|
|
}
|
|
}
|
|
public String ContentType
|
|
{
|
|
get
|
|
{
|
|
return part.GetContentTypeProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetContentTypeProperty(value);
|
|
}
|
|
}
|
|
public DateTime? Created
|
|
{
|
|
get
|
|
{
|
|
return part.GetCreatedProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetCreatedProperty(value);
|
|
}
|
|
}
|
|
public void SetCreated(String date)
|
|
{
|
|
part.SetCreatedProperty(date);
|
|
}
|
|
public String Creator
|
|
{
|
|
get
|
|
{
|
|
return part.GetCreatorProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetCreatorProperty(value);
|
|
}
|
|
}
|
|
public String Description
|
|
{
|
|
get
|
|
{
|
|
return part.GetDescriptionProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetDescriptionProperty(value);
|
|
}
|
|
}
|
|
public String Identifier
|
|
{
|
|
get
|
|
{
|
|
return part.GetIdentifierProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetIdentifierProperty(value);
|
|
}
|
|
}
|
|
public String Keywords
|
|
{
|
|
get
|
|
{
|
|
return part.GetKeywordsProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetKeywordsProperty(value);
|
|
}
|
|
}
|
|
public DateTime? LastPrinted
|
|
{
|
|
get
|
|
{
|
|
return part.GetLastPrintedProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetLastPrintedProperty(value);
|
|
}
|
|
}
|
|
public void SetLastPrinted(String date)
|
|
{
|
|
part.SetLastPrintedProperty(date);
|
|
}
|
|
|
|
public String LastModifiedByUser
|
|
{
|
|
get
|
|
{
|
|
return part.GetLastModifiedByProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetLastModifiedByProperty(value);
|
|
}
|
|
}
|
|
|
|
public DateTime? Modified
|
|
{
|
|
get
|
|
{
|
|
return part.GetModifiedProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetModifiedProperty(value);
|
|
}
|
|
}
|
|
public void SetModified(String date)
|
|
{
|
|
part.SetModifiedProperty(date);
|
|
}
|
|
public String Subject
|
|
{
|
|
get
|
|
{
|
|
return part.GetSubjectProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetSubjectProperty(value);
|
|
}
|
|
}
|
|
public String Title
|
|
{
|
|
get
|
|
{
|
|
return part.GetTitleProperty();
|
|
}
|
|
set
|
|
{
|
|
part.SetTitleProperty(value);
|
|
}
|
|
}
|
|
public String Revision
|
|
{
|
|
get
|
|
{
|
|
return part.GetRevisionProperty();
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
long.Parse(value);
|
|
part.SetRevisionProperty(value);
|
|
}
|
|
catch (FormatException) { }
|
|
}
|
|
}
|
|
|
|
public PackagePropertiesPart GetUnderlyingProperties()
|
|
{
|
|
return part;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extended document properties
|
|
*/
|
|
public class ExtendedProperties
|
|
{
|
|
public ExtendedPropertiesDocument props;
|
|
internal ExtendedProperties(ExtendedPropertiesDocument props)
|
|
{
|
|
this.props = props;
|
|
}
|
|
|
|
public CT_ExtendedProperties GetUnderlyingProperties()
|
|
{
|
|
return props.GetProperties();
|
|
}
|
|
|
|
public String Template
|
|
{
|
|
get
|
|
{
|
|
return props.GetProperties().Template;
|
|
}
|
|
}
|
|
public String Manager
|
|
{
|
|
get { return props.GetProperties().Manager; }
|
|
}
|
|
public String Company
|
|
{
|
|
get { return props.GetProperties().Company; }
|
|
}
|
|
public String PresentationFormat
|
|
{
|
|
get { return props.GetProperties().PresentationFormat; }
|
|
}
|
|
public String Application
|
|
{
|
|
get { return props.GetProperties().Application; }
|
|
}
|
|
public String AppVersion
|
|
{
|
|
get { return props.GetProperties().AppVersion; }
|
|
}
|
|
|
|
public int Pages
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetPages())
|
|
{
|
|
return props.GetProperties().Pages;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int Words
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetWords())
|
|
{
|
|
return props.GetProperties().Words;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int Characters
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetCharacters())
|
|
{
|
|
return props.GetProperties().Characters;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int CharactersWithSpaces
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetCharactersWithSpaces())
|
|
{
|
|
return props.GetProperties().CharactersWithSpaces;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int Lines
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetLines())
|
|
{
|
|
return props.GetProperties().Lines;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int Paragraphs
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetParagraphs())
|
|
{
|
|
return props.GetProperties().Paragraphs;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int Slides
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetSlides())
|
|
{
|
|
return props.GetProperties().Slides;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int Notes
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetNotes())
|
|
{
|
|
return props.GetProperties().Notes;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int TotalTime
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetTotalTime())
|
|
{
|
|
return props.GetProperties().TotalTime;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int HiddenSlides
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetHiddenSlides())
|
|
{
|
|
return props.GetProperties().HiddenSlides;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
public int MMClips
|
|
{
|
|
get
|
|
{
|
|
if (props.GetProperties().IsSetMMClips())
|
|
{
|
|
return props.GetProperties().MMClips;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public String HyperlinkBase
|
|
{
|
|
get { return props.GetProperties().HyperlinkBase; }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Custom document properties
|
|
*/
|
|
public class CustomProperties
|
|
{
|
|
/**
|
|
* Each custom property element Contains an fmtid attribute
|
|
* with the same GUID value ({D5CDD505-2E9C-101B-9397-08002B2CF9AE}).
|
|
*/
|
|
public static String FORMAT_ID = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
|
|
|
|
public CustomPropertiesDocument props;
|
|
internal CustomProperties(CustomPropertiesDocument props)
|
|
{
|
|
this.props = props;
|
|
}
|
|
|
|
public CT_CustomProperties GetUnderlyingProperties()
|
|
{
|
|
return props.GetProperties();
|
|
}
|
|
|
|
/**
|
|
* Add a new property
|
|
*
|
|
* @param name the property name
|
|
* @throws IllegalArgumentException if a property with this name already exists
|
|
*/
|
|
private CT_Property Add(String name)
|
|
{
|
|
if (Contains(name))
|
|
{
|
|
throw new ArgumentException("A property with this name " +
|
|
"already exists in the custom properties");
|
|
}
|
|
|
|
CT_Property p = props.GetProperties().AddNewProperty();
|
|
int pid = NextPid();
|
|
p.pid = pid;
|
|
p.fmtid = FORMAT_ID;
|
|
p.name = name;
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Add a new string property
|
|
*
|
|
* @throws IllegalArgumentException if a property with this name already exists
|
|
*/
|
|
public void AddProperty(String name, String value)
|
|
{
|
|
CT_Property p = Add(name);
|
|
p.ItemElementName = ItemChoiceType.lpwstr;
|
|
p.Item = value;
|
|
}
|
|
|
|
/**
|
|
* Add a new double property
|
|
*
|
|
* @throws IllegalArgumentException if a property with this name already exists
|
|
*/
|
|
public void AddProperty(String name, double value)
|
|
{
|
|
CT_Property p = Add(name);
|
|
p.ItemElementName = ItemChoiceType.r8;
|
|
p.Item = value;
|
|
}
|
|
|
|
/**
|
|
* Add a new integer property
|
|
*
|
|
* @throws IllegalArgumentException if a property with this name already exists
|
|
*/
|
|
public void AddProperty(String name, int value)
|
|
{
|
|
CT_Property p = Add(name);
|
|
p.ItemElementName = ItemChoiceType.i4;
|
|
p.Item = value;
|
|
}
|
|
|
|
/**
|
|
* Add a new bool property
|
|
*
|
|
* @throws IllegalArgumentException if a property with this name already exists
|
|
*/
|
|
public void AddProperty(String name, bool value)
|
|
{
|
|
CT_Property p = Add(name);
|
|
p.ItemElementName = ItemChoiceType.@bool;
|
|
p.Item = value;
|
|
}
|
|
|
|
/**
|
|
* Generate next id that uniquely relates a custom property
|
|
*
|
|
* @return next property id starting with 2
|
|
*/
|
|
protected int NextPid()
|
|
{
|
|
int propid = 1;
|
|
foreach (CT_Property p in props.GetProperties().GetPropertyList())
|
|
{
|
|
if (p.pid > propid) propid = p.pid;
|
|
}
|
|
return propid + 1;
|
|
}
|
|
|
|
/**
|
|
* Check if a property with this name already exists in the collection of custom properties
|
|
*
|
|
* @param name the name to check
|
|
* @return whether a property with the given name exists in the custom properties
|
|
*/
|
|
public bool Contains(String name)
|
|
{
|
|
foreach (CT_Property p in props.GetProperties().GetPropertyList())
|
|
{
|
|
if (p.name.Equals(name)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the custom property with this name, or null if none exists.
|
|
*
|
|
* You will need to test the various isSetX methods to work out
|
|
* what the type of the property is, before fetching the
|
|
* appropriate value for it.
|
|
*
|
|
* @param name the name of the property to fetch
|
|
*/
|
|
public CT_Property GetProperty(String name) {
|
|
foreach(CT_Property p in props.GetProperties().GetPropertyList()){
|
|
if(p.name.Equals(name)) {
|
|
return p;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper around the three different kinds of OOXML properties
|
|
* and metadata a document can have (Core, Extended and Custom),
|
|
* as well Thumbnails.
|
|
*/
|
|
public class POIXMLProperties
|
|
{
|
|
private OPCPackage pkg;
|
|
private CoreProperties core;
|
|
private ExtendedProperties ext;
|
|
private CustomProperties cust;
|
|
|
|
private PackagePart extPart;
|
|
private PackagePart custPart;
|
|
|
|
|
|
private static ExtendedPropertiesDocument NEW_EXT_INSTANCE;
|
|
private static CustomPropertiesDocument NEW_CUST_INSTANCE;
|
|
static POIXMLProperties()
|
|
{
|
|
NEW_EXT_INSTANCE = new ExtendedPropertiesDocument();
|
|
NEW_EXT_INSTANCE.AddNewProperties();
|
|
|
|
NEW_CUST_INSTANCE = new CustomPropertiesDocument();
|
|
NEW_CUST_INSTANCE.AddNewProperties();
|
|
}
|
|
|
|
public POIXMLProperties(OPCPackage docPackage)
|
|
{
|
|
this.pkg = docPackage;
|
|
|
|
// Core properties
|
|
core = new CoreProperties((PackagePropertiesPart)pkg.GetPackageProperties());
|
|
|
|
// Extended properties
|
|
PackageRelationshipCollection extRel =
|
|
pkg.GetRelationshipsByType(PackageRelationshipTypes.EXTENDED_PROPERTIES);
|
|
if (extRel.Size == 1)
|
|
{
|
|
extPart = pkg.GetPart(extRel.GetRelationship(0));
|
|
ExtendedPropertiesDocument props = ExtendedPropertiesDocument.Parse(
|
|
extPart.GetInputStream()
|
|
);
|
|
ext = new ExtendedProperties(props);
|
|
}
|
|
else
|
|
{
|
|
extPart = null;
|
|
ext = new ExtendedProperties((ExtendedPropertiesDocument)NEW_EXT_INSTANCE.Copy());
|
|
}
|
|
|
|
// Custom properties
|
|
PackageRelationshipCollection custRel =
|
|
pkg.GetRelationshipsByType(PackageRelationshipTypes.CUSTOM_PROPERTIES);
|
|
if (custRel.Size == 1)
|
|
{
|
|
custPart = pkg.GetPart(custRel.GetRelationship(0));
|
|
CustomPropertiesDocument props = CustomPropertiesDocument.Parse(
|
|
custPart.GetInputStream()
|
|
);
|
|
cust = new CustomProperties(props);
|
|
}
|
|
else
|
|
{
|
|
custPart = null;
|
|
cust = new CustomProperties((CustomPropertiesDocument)NEW_CUST_INSTANCE.Copy());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the core document properties
|
|
*/
|
|
public CoreProperties CoreProperties
|
|
{
|
|
get
|
|
{
|
|
return core;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the extended document properties
|
|
*/
|
|
public ExtendedProperties ExtendedProperties
|
|
{
|
|
get
|
|
{
|
|
return ext;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the custom document properties
|
|
*/
|
|
public CustomProperties CustomProperties
|
|
{
|
|
get
|
|
{
|
|
return cust;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link PackagePart} for the Document
|
|
* Thumbnail, or <code>null</code> if there isn't one
|
|
*
|
|
* @return The Document Thumbnail part or null
|
|
*/
|
|
protected internal PackagePart ThumbnailPart
|
|
{
|
|
get
|
|
{
|
|
PackageRelationshipCollection rels =
|
|
pkg.GetRelationshipsByType(PackageRelationshipTypes.THUMBNAIL);
|
|
if (rels.Size == 1)
|
|
{
|
|
return pkg.GetPart(rels.GetRelationship(0));
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Returns the name of the Document thumbnail, eg
|
|
* <code>thumbnail.jpeg</code>, or <code>null</code> if there
|
|
* isn't one.
|
|
*
|
|
* @return The thumbnail filename, or null
|
|
*/
|
|
public String ThumbnailFilename
|
|
{
|
|
get
|
|
{
|
|
PackagePart tPart = ThumbnailPart;
|
|
if (tPart == null) return null;
|
|
String name = tPart.PartName.Name;
|
|
return name.Substring(name.LastIndexOf('/') + 1);
|
|
}
|
|
}
|
|
/**
|
|
* Returns the Document thumbnail image data, or
|
|
* <code>null</code> if there isn't one.
|
|
*
|
|
* @return The thumbnail data, or null
|
|
*/
|
|
public Stream ThumbnailImage
|
|
{
|
|
get
|
|
{
|
|
PackagePart tPart = ThumbnailPart;
|
|
if (tPart == null) return null;
|
|
return tPart.GetInputStream();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the Thumbnail for the document, replacing any existing
|
|
* one.
|
|
*
|
|
* @param name The filename for the thumbnail image, eg <code>thumbnail.jpg</code>
|
|
* @param imageData The inputstream to read the thumbnail image from
|
|
*/
|
|
public void SetThumbnail(String filename, Stream imageData)
|
|
{
|
|
PackagePart tPart = ThumbnailPart;
|
|
if (tPart == null) {
|
|
// New thumbnail
|
|
pkg.AddThumbnail(filename, imageData);
|
|
} else {
|
|
// Change existing
|
|
String newType = ContentTypes.GetContentTypeFromFileExtension(filename);
|
|
if (!newType.Equals(tPart.ContentType))
|
|
{
|
|
throw new ArgumentException("Can't set a Thumbnail of type " +
|
|
newType + " when existing one is of a different type " +
|
|
tPart.ContentType);
|
|
}
|
|
StreamHelper.CopyStream(imageData, tPart.GetOutputStream());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Commit Changes to the underlying OPC namespace
|
|
*/
|
|
public virtual void Commit()
|
|
{
|
|
|
|
if (extPart == null && !NEW_EXT_INSTANCE.ToString().Equals(ext.props.ToString()))
|
|
{
|
|
try
|
|
{
|
|
PackagePartName prtname = PackagingUriHelper.CreatePartName("/docProps/app.xml");
|
|
pkg.AddRelationship(prtname, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties");
|
|
extPart = pkg.CreatePart(prtname, "application/vnd.openxmlformats-officedocument.extended-properties+xml");
|
|
}
|
|
catch (InvalidFormatException e)
|
|
{
|
|
throw new POIXMLException(e);
|
|
}
|
|
}
|
|
if (custPart == null && !NEW_CUST_INSTANCE.ToString().Equals(cust.props.ToString()))
|
|
{
|
|
try
|
|
{
|
|
PackagePartName prtname = PackagingUriHelper.CreatePartName("/docProps/custom.xml");
|
|
pkg.AddRelationship(prtname, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties");
|
|
custPart = pkg.CreatePart(prtname, "application/vnd.openxmlformats-officedocument.custom-properties+xml");
|
|
}
|
|
catch (InvalidFormatException e)
|
|
{
|
|
throw new POIXMLException(e);
|
|
}
|
|
}
|
|
if (extPart != null)
|
|
{
|
|
Stream out1 = extPart.GetOutputStream();
|
|
|
|
if (extPart.Size > 0)
|
|
extPart.Clear();
|
|
ext.props.Save(out1);
|
|
out1.Close();
|
|
}
|
|
if (custPart != null)
|
|
{
|
|
Stream out1 = custPart.GetOutputStream();
|
|
cust.props.Save(out1);
|
|
out1.Close();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|