요약: 이 문서는 Microsoft Office Word 2007 파일 액세스와 조작에 사용 가능한
Open XML 개체 모델 코드에 대해 설명하는 시리즈의 3 번째에 해당합니다.
Frank Rice, Microsoft Corporation
2007 년 9 월
적용 대상 : Microsoft Office Word 2007
목차
개요
2007 Microsoft Office system에서는 Office Open XML 형식으로 불리는 XML 기반 새로운 파일 형식이 도입되었습니다. Microsoft Office Word 2007, Microsoft Office
Excel 2007 및 Microsoft Office PowerPoint 2007에서는 이 파일 형식이 기본값의 파일
형식으로 사용됩니다. Microsoft에서는 Microsoft .NET Framework 3.0 기술의 일부로서, System.IO.Packaging
네임 스페이스에 이러한 파일에 액세스하기 위한 라이브러리가 「Microsoft
SDK for Open XML Formats 기술 프리뷰」에 있습니다. Open XML 개체 모델은 System.IO.Packaging
API 를 기반으로 생성되어, Open XML 문서를 조작하기 위한 엄밀하게 형식 지정된 파트 클래스(part
class)를 갖추고
있습니다. 여기에서는 샘플코드에 의해, Open XML 개체 모델을 사용한 Word 2007 파일에 액세스 및
조작하는 방법을 설명합니다.
사용자 지정 문서 속성 설정
이하의 코드에서는 문서의 사용자 지정 속성의 값을 설정합니다. 문서는 custom.xml 파트에 있는 사용자 지정
속성을 포함한 경우도 있고, 포함되지 않는 경우도 있습니다. 이 때문에 프로시저는 다음 처리를 실행합니다.
-
custom.xml 파트가 문서에 존재하지 않는 경우는 그것을 추가합니다.
-
custom.xml 파트가 문서에 존재하며, 속성이 존재 없는 경우는 속성을 추가합니다.
-
속성이 존재하며, 같은 종류인 경우는 값을 옮겨놓습니다.
-
속성이 존재하며, 다른 종류인 경우는 기존 속성을 업데이트 합니다.
완료되면 프로시저는 조작이 정상적으로 완료되었는지 보여주는 Boolean 값을 반환합니다.
Public Enum PropertyTypes
YesNo
Text
DateTime
NumberInteger
NumberDouble
End Enum
Public Function WDSetCustomProperty(ByVal docName As String, ByVal propertyName As String, ByVal propertyValue As Object, ByVal
propertyType As PropertyTypes) As Boolean
' Given a document name, a property name/value, and the property
' type, add a custom property to a document. Return true if the
' property is added/updated, or False if the property cannot be updated.
Const customPropertiesSchema As String = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
Const customVTypesSchema As String = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
Dim retVal As Boolean = False
Dim propertyTypeName As String = "vt:lpwstr"
Dim propertyValueString As String = Nothing
' Calculate the correct type.
Select Case (propertyType)
Case PropertyTypes.DateTime
propertyTypeName = "vt:filetime"
If (TypeOf propertyValue Is String) Then
propertyValueString = String.Format("{0:s}Z", Convert.ToDateTime(propertyValue))
End If
Case PropertyTypes.NumberInteger
propertyTypeName = "vt:i4"
If (TypeOf propertyValue Is Int32) Then
propertyValueString = Convert.ToInt32(propertyValue).ToString
End If
Case PropertyTypes.NumberDouble
propertyTypeName = "vt:r8"
If (TypeOf propertyValue Is Double) Then
propertyValueString = Convert.ToDouble(propertyValue).ToString
End If
Case PropertyTypes.Text
propertyTypeName = "vt:lpwstr"
propertyValueString = Convert.ToString(propertyValue)
Case PropertyTypes.YesNo
propertyTypeName = "vt:bool"
If (TypeOf propertyValue Is Boolean) Then
' Must be lowercase!
propertyValueString = Convert.ToBoolean(propertyValue).ToString.ToLower
End If
End Select
If (propertyValueString = Nothing) Then
' If the code cannot convert the
' property to a valid value, throw an exception.
Throw New InvalidDataException("Invalid parameter value.")
End If
Dim wdPackage As WordprocessingDocument = WordprocessingDocument.Open(docName, True)
' Work with the custom properties part.
Dim customPropsPart As CustomFilePropertiesPart = wdPackage.CustomFilePropertiesPart
' Manage namespaces to perform XML XPath queries.
Dim nt As NameTable = New NameTable
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
nsManager.AddNamespace("d", customPropertiesSchema)
nsManager.AddNamespace("vt", customVTypesSchema)
Dim customPropsUri As Uri = New Uri("/docProps/custom.xml", UriKind.Relative)
Dim customPropsDoc As XmlDocument = Nothing
Dim rootNode As XmlNode = Nothing
' There may not be a custom properties part.
If (customPropsPart Is Nothing) Then
customPropsDoc = New XmlDocument(nt)
' The part does not exist. Create it now.
customPropsPart = wdPackage.AddCustomFilePropertiesPart
' Set up the rudimentary custom part.
rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema)
rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"))
rootNode.Attributes("xmlns:vt").Value = customVTypesSchema
customPropsDoc.AppendChild(rootNode)
Else
' Load the contents of the custom properties part into an XML document.
customPropsDoc = New XmlDocument(nt)
customPropsDoc.Load(customPropsPart.GetStream)
rootNode = customPropsDoc.DocumentElement
End If
' Now that you have a reference to an XmlDocument object that
' corresponds to the custom properties part,
' check to see if the required property is already there.
Dim searchString As String = String.Format("d:Properties/d:property[@name='{0}']", propertyName)
Dim node As XmlNode = customPropsDoc.SelectSingleNode(searchString, nsManager)
Dim valueNode As XmlNode = Nothing
If (Not (node) Is Nothing) Then
' You found the node. Now check its type.
If node.HasChildNodes Then
valueNode = node.ChildNodes(0)
If (Not (valueNode) Is Nothing) Then
Dim typeName As String = valueNode.Name
If (propertyTypeName = typeName) Then
' The types are the same.
' Replace the value of the node.
valueNode.InnerText = propertyValueString
' If the property existed, and its type
' has not changed, you are finished.
retVal = True
Else
' Types are different. Delete the node
' and clear the node variable.
node.ParentNode.RemoveChild(node)
node = Nothing
End If
End If
End If
End If
' The previous block of code may have cleared the value in the
' variable named node.
If (node Is Nothing) Then
' Either you did not find the node, or you
' found it, its type was incorrect, and you deleted it.
' Either way, you need to create the new property node now.
' Find the highest existing "pid" value.
' The default value for the "pid" attribute is "2".
Dim pidValue As String = "2"
Dim propertiesNode As XmlNode = customPropsDoc.DocumentElement
If propertiesNode.HasChildNodes Then
Dim lastNode As XmlNode = propertiesNode.LastChild
If (Not (lastNode) Is Nothing) Then
Dim pidAttr As XmlAttribute = lastNode.Attributes("pid")
If Not (pidAttr Is Nothing) Then
pidValue = pidAttr.Value
' Increment pidValue, so that the new property
' gets a pid value one higher. This value should be
' numeric, but you should confirm that.
Dim value As Integer = 0
If Integer.TryParse(pidValue, value) Then
pidValue = Convert.ToString((value + 1))
End If
End If
End If
End If
node = customPropsDoc.CreateElement("property", customPropertiesSchema)
node.Attributes.Append(customPropsDoc.CreateAttribute("name"))
node.Attributes("name").Value = propertyName
node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"))
node.Attributes("fmtid").Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
node.Attributes.Append(customPropsDoc.CreateAttribute("pid"))
node.Attributes("pid").Value = pidValue
valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema)
valueNode.InnerText = propertyValueString
node.AppendChild(valueNode)
rootNode.AppendChild(node)
retVal = True
End If
' Save the properties XML back to its part.
customPropsDoc.Save(customPropsPart.GetStream)
Return retVal
End Function
public enum PropertyTypes
{
YesNo,
Text,
DateTime,
NumberInteger,
NumberDouble,
}
public static bool WDSetCustomProperty(string docName, string propertyName, object propertyValue, PropertyTypes propertyType)
{
// Given a document name, a property name/value, and the property type, add a custom property
// to a document. Return True if the property was added/updated, or False if the property cannot be updated.
// The function's return value is true if the code could add/update the property,
// and false otherwise.
const string customPropertiesSchema = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
const string customVTypesSchema = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes";
bool retVal = false;
string propertyTypeName = "vt:lpwstr";
string propertyValueString = null;
// Calculate the correct type.
switch (propertyType)
{
case PropertyTypes.DateTime:
propertyTypeName = "vt:filetime";
// Make sure you were passed a real date,
// and if so, format in the correct way. The date/time
// value passed in should represent a UTC date/time.
if (propertyValue.GetType() == typeof(System.String))
{
propertyValueString = string.Format("{0:s}Z", Convert.ToDateTime(propertyValue));
}
break;
case PropertyTypes.NumberInteger:
propertyTypeName = "vt:i4";
if (propertyValue.GetType() == typeof(System.Int32))
{
propertyValueString = Convert.ToInt32(propertyValue).ToString();
}
break;
case PropertyTypes.NumberDouble:
propertyTypeName = "vt:r8";
if (propertyValue.GetType() == typeof(System.Double))
{
propertyValueString = Convert.ToDouble(propertyValue).ToString();
}
break;
case PropertyTypes.Text:
propertyTypeName = "vt:lpwstr";
propertyValueString = Convert.ToString(propertyValue);
break;
case PropertyTypes.YesNo:
propertyTypeName = "vt:bool";
if (propertyValue.GetType() == typeof(System.Boolean))
{
// IMPORTANT: This value must be lowercase.
propertyValueString = Convert.ToBoolean(propertyValue).ToString().ToLower();
}
break;
}
if (propertyValueString == null)
{
// If the code cannot convert the
// property to a valid value, throw an exception.
throw new InvalidDataException("Invalid parameter value.");
}
using (WordprocessingDocument wdPackage = WordprocessingDocument.Open(docName, true))
{
// This part is working with the custom properties part.
CustomFilePropertiesPart customPropsPart = wdPackage.CustomFilePropertiesPart;
// You must manage namespaces to perform XML XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("d", customPropertiesSchema);
nsManager.AddNamespace("vt", customVTypesSchema);
Uri customPropsUri = new Uri("/docProps/custom.xml", UriKind.Relative);
XmlDocument customPropsDoc = null;
XmlNode rootNode = null;
// There may not be a custom properties part.
if (customPropsPart == null)
{
customPropsDoc = new XmlDocument(nt);
// The part does not exist. Create it now.
customPropsPart = wdPackage.AddCustomFilePropertiesPart();
// Set up the rudimentary custom part.
rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema);
rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"));
rootNode.Attributes["xmlns:vt"].Value = customVTypesSchema;
customPropsDoc.AppendChild(rootNode);
}
else
{
// Load the contents of the custom properties part into an XML document.
customPropsDoc = new XmlDocument(nt);
customPropsDoc.Load(customPropsPart.GetStream());
rootNode = customPropsDoc.DocumentElement;
}
// Now that you have a reference to an XmlDocument object that
// corresponds to the custom properties part,
// check to see if the required property is already there.
string searchString = string.Format("d:Properties/d:property[@name='{0}']", propertyName);
XmlNode node = customPropsDoc.SelectSingleNode(searchString, nsManager);
XmlNode valueNode = null;
if (node != null)
{
// You found the node. Now check its type.
if (node.HasChildNodes)
{
valueNode = node.ChildNodes[0];
if (valueNode != null)
{
string typeName = valueNode.Name;
if (propertyTypeName == typeName)
{
// The types are the same.
// Replace the value of the node.
valueNode.InnerText = propertyValueString;
// If the property existed, and its type
// has not changed, you are finished.
retVal = true;
}
else
{
// Types are different. Delete the node
// and clear the node variable.
node.ParentNode.RemoveChild(node);
node = null;
}
}
}
}
// The previous block of code may have cleared the value in the
// variable named node.
if (node == null)
{
// Either you did not find the node, or you
// found it, its type was incorrect, and you deleted it.
// Either way, you need to create the new property node now.
// Find the highest existing "pid" value.
// The default value for the "pid" attribute is "2".
string pidValue = "2";
XmlNode propertiesNode = customPropsDoc.DocumentElement;
if (propertiesNode.HasChildNodes)
{
XmlNode lastNode = propertiesNode.LastChild;
if (lastNode != null)
{
XmlAttribute pidAttr = lastNode.Attributes["pid"];
if (!(pidAttr == null))
{
pidValue = pidAttr.Value;
// Increment pidValue, so that the new property
// gets a pid value one higher. This value should be
// numeric, but you should confirm that.
int value = 0;
if (int.TryParse(pidValue, out value))
{
pidValue = Convert.ToString(value + 1);
}
}
}
}
node = customPropsDoc.CreateElement("property", customPropertiesSchema);
node.Attributes.Append(customPropsDoc.CreateAttribute("name"));
node.Attributes["name"].Value = propertyName;
node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"));
node.Attributes["fmtid"].Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
node.Attributes.Append(customPropsDoc.CreateAttribute("pid"));
node.Attributes["pid"].Value = pidValue;
valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema);
valueNode.InnerText = propertyValueString;
node.AppendChild(valueNode);
rootNode.AppendChild(node);
retVal = true;
}
// Save the properties XML back to its part.
customPropsDoc.Save(customPropsPart.GetStream());
//wdPackage.Save();
}
return retVal;
}
이 코드는 먼저, 가능한 사용자 지정 속성 종류 열거를 정의합니다.
Public Enum PropertyTypes
YesNo
Text
DateTime
NumberInteger
NumberDouble
End Enum
public enum PropertyTypes
{
YesNo,
Text,
DateTime,
NumberInteger,
NumberDouble,
}
다음에 이 샘플코드는 WDSetCustomProperty 를 호출하여, Word 2007 문서 참조, 사용자 지정
속성 이름, 속성을 설정하는 새로운 값 및 열거된 값에서 속성 종류를 건네줍니다.
Dim propertyTypeName As String = "vt:lpwstr"
string propertyTypeName = "vt:lpwstr";
다음 사용자는 propertyTypeName 변수를 CustomFilePropertiesPart
파트의 WordprocessingML 마크 업의 Text 값을 나타내는 기본값 노드명 (vt:lpwstr)으로
설정합니다. 문서의 사용자 지정 속성은 CustomFilePropertiesPart 파트에 있습니다.
마지막으로 이 변수는 사용자가 설정하는 속성 값을 포함한 노드를 참조합니다.
다음은 일련의 Select Case 구문 (Microsoft Visual C# 의switch
구문)이 업데이트하는 속성 종류를 테스트 합니다. 속성 값에 따라, 코드는 이 값을 보관 유지하는 특정 노드의
이름과 동일하도록 변수를 설정합니다. 그리고 코드는 속성을 업데이트하는 값을 지정합니다.
Select Case (propertyType)
Case PropertyTypes.DateTime
propertyTypeName = "vt:filetime"
If (TypeOf propertyValue Is String) Then
propertyValueString = String.Format("{0:s}Z", Convert.ToDateTime(propertyValue))
End If
Case PropertyTypes.NumberInteger
propertyTypeName = "vt:i4"
If (TypeOf propertyValue Is Int32) Then
propertyValueString = Convert.ToInt32(propertyValue).ToString
End If
......
......
End Select
switch (propertyType)
{
case PropertyTypes.DateTime:
propertyTypeName = "vt:filetime";
// Make sure you were passed a real date,
// and if so, format in the correct way. The date/time
// value passed in should represent a UTC date/time.
if (propertyValue.GetType() == typeof(System.String))
{
propertyValueString = string.Format("{0:s}Z", Convert.ToDateTime(propertyValue));
}
break;
case PropertyTypes.NumberInteger:
propertyTypeName = "vt:i4";
if (propertyValue.GetType() == typeof(System.Int32))
{
propertyValueString = Convert.ToInt32(propertyValue).ToString();
}
break;
.......
.......
}
다음은 Office Open XML 파일 형식 패키지를 나타내는 WordprocessingDocument
개체를 입력 문서에 기반하여 생성합니다. 그리고, 코드는 CustomFilePropertiesPart 를
취득합니다. 그리고 코드는 XPath 쿼리를 설치하는 네임 스페이스 관리자를 생성합니다.
코드 다음 섹션은 사용자 지정 속성 파트가 존재하는지 판정합니다.
If (customPropsPart Is Nothing) Then
customPropsDoc = New XmlDocument(nt)
' The part does not exist. Create it now.
customPropsPart = wdPackage.AddCustomFilePropertiesPart
' Set up the rudimentary custom part.
rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema)
rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"))
rootNode.Attributes("xmlns:vt").Value = customVTypesSchema
customPropsDoc.AppendChild(rootNode)
Else
' Load the contents of the custom properties part into an XML document.
customPropsDoc = New XmlDocument(nt)
customPropsDoc.Load(customPropsPart.GetStream)
rootNode = customPropsDoc.DocumentElement
End If
if (customPropsPart == null)
{
customPropsDoc = new XmlDocument(nt);
// The part does not exist. Create it now.
customPropsPart = wdPackage.AddCustomFilePropertiesPart();
// Set up the rudimentary custom part.
rootNode = customPropsDoc.CreateElement("Properties", customPropertiesSchema);
rootNode.Attributes.Append(customPropsDoc.CreateAttribute("xmlns:vt"));
rootNode.Attributes["xmlns:vt"].Value = customVTypesSchema;
customPropsDoc.AppendChild(rootNode);
}
else
{
// Load the contents of the custom properties part into an XML document.
customPropsDoc = new XmlDocument(nt);
customPropsDoc.Load(customPropsPart.GetStream());
rootNode = customPropsDoc.DocumentElement;
}
파트가 존재하지 않는 경우, 코드는 사용자 지정 속성 파트 셸을 생성하여, 기본 속성을 로드합니다. 파트가 존재하는 경우는 사용자는 그 내용을
메모리 상주 XML 문서내에 로드합니다. d:Properties/d:property
노드를 검색하는 XPath 쿼리로서 검색 문자열을 설치합니다.
If (Not (node) Is Nothing) Then
' You found the node. Now check its type.
If node.HasChildNodes Then
valueNode = node.ChildNodes(0)
If (Not (valueNode) Is Nothing) Then
Dim typeName As String = valueNode.Name
If (propertyTypeName = typeName) Then
' The types are the same.
' Replace the value of the node.
valueNode.InnerText = propertyValueString
' If the property existed, and its type
' did not change, you are finished.
retVal = True
Else
' The types are different. Delete the node
' and clear the node variable.
node.ParentNode.RemoveChild(node)
node = Nothing
End If
End If
End If
End If
if (node != null)
{
// You found the node. Now check its type.
if (node.HasChildNodes)
{
valueNode = node.ChildNodes[0];
if (valueNode != null)
{
string typeName = valueNode.Name;
if (propertyTypeName == typeName)
{
// The types are the same.
// Replace the value of the node.
valueNode.InnerText = propertyValueString;
// If the property existed, and its type
// did not change, you are finished.
retVal = true;
}
else
{
// Types are different. Delete the node
// and clear the node variable.
node.ParentNode.RemoveChild(node);
node = null;
}
}
}
}
이 코드에서는 다음 액션이 발생할 가능성이 있습니다.
-
노드가 발견되지 않았던 경우는 그 노드를 파트에 추가합니다.
-
노드가 발견되어, 종류가 새로운 속성과는 다른 경우, 그 노드를 삭제합니다.
-
노드가 발견되어, 종류가 새로운 속성과 같은 경우는 속성 값을 옮겨놓습니다. 그렇지 않은 경우, 새로운 값과
종류를 가지는 새로운 노드를 추가합니다.
-
노드가 발견되지 않았던 경우, 또는 발견되어 종류가 적절하지 않은 경우에 노드를 삭제했을 경우, 새로운 속성
노드를 생성합니다.
Dim pidValue As String = "2"
Dim propertiesNode As XmlNode = customPropsDoc.DocumentElement
If propertiesNode.HasChildNodes Then
Dim lastNode As XmlNode = propertiesNode.LastChild
If (Not (lastNode) Is Nothing) Then
Dim pidAttr As XmlAttribute = lastNode.Attributes("pid")
If Not (pidAttr Is Nothing) Then
pidValue = pidAttr.Value
' Increment pidValue, so that the new property
' gets a pid value one higher. This value should be
' numeric, but you should confirm that.
Dim value As Integer = 0
If Integer.TryParse(pidValue, value) Then
pidValue = Convert.ToString((value + 1))
End If
End If
End If
End If
string pidValue = "2";
XmlNode propertiesNode = customPropsDoc.DocumentElement;
if (propertiesNode.HasChildNodes)
{
XmlNode lastNode = propertiesNode.LastChild;
if (lastNode != null)
{
XmlAttribute pidAttr = lastNode.Attributes["pid"];
if (!(pidAttr == null))
{
pidValue = pidAttr.Value;
// Increment pidValue, so that the new property
// gets a pid value one higher. This value should be
// numeric, but you should confirm that.
int value = 0;
if (int.TryParse(pidValue, out value))
{
pidValue = Convert.ToString(value + 1);
}
}
}
}
각 속성은 id값 (pidValue)을 가지고 있고, 기본값은 2 입니다. 이 값은 기존 속성 id 의
값보다 "1" 밖에 크지 않으면 안됩니다. 이 code segment는 기존 속성 노드 id 값 (존재하는
경우)을 검색하여, 새로운 속성 노드의 id 가 "1"만 큰 것을 확인합니다.
node = customPropsDoc.CreateElement("property", customPropertiesSchema)
node.Attributes.Append(customPropsDoc.CreateAttribute("name"))
node.Attributes("name").Value = propertyName
node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"))
node.Attributes("fmtid").Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
node.Attributes.Append(customPropsDoc.CreateAttribute("pid"))
node.Attributes("pid").Value = pidValue
valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema)
valueNode.InnerText = propertyValueString
node.AppendChild(valueNode)
rootNode.AppendChild(node)
retVal = True
node = customPropsDoc.CreateElement("property", customPropertiesSchema);
node.Attributes.Append(customPropsDoc.CreateAttribute("name"));
node.Attributes["name"].Value = propertyName;
node.Attributes.Append(customPropsDoc.CreateAttribute("fmtid"));
node.Attributes["fmtid"].Value = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
node.Attributes.Append(customPropsDoc.CreateAttribute("pid"));
node.Attributes["pid"].Value = pidValue;
valueNode = customPropsDoc.CreateElement(propertyTypeName, customVTypesSchema);
valueNode.InnerText = propertyValueString;
node.AppendChild(valueNode);
rootNode.AppendChild(node);
retVal = true;
나머지 코드는 속성 요소를 생성하여, 그 특성을 추가하여 노드를 루트노드에 추가합니다. 마지막 순서는 조작이
정상적으로 완료했는지를 보여주는 Boolean 값을 반환합니다.
문서 인쇄 방향 설정
다음 코드는 문서 인쇄 방향을 변경합니다.
Public Enum PrintOrientation
Landscape
Portrait
End Enum
Public Sub WDSetPrintOrientation(ByVal docName As String, ByVal newOrientation As PrintOrientation)
' Given a document name, set the print orientation for all the sections of the document.
' Example:
' WDSetPrintOrientation(@"C:\Samples\SetOrientation.docx", PrintOrientation.Landscape);
Const wordmlNamespace As String = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
Dim wdPackage As WordprocessingDocument = WordprocessingDocument.Open(docName, True)
' Get the officeDocument part.
Dim documentPart As MainDocumentPart = wdPackage.MainDocumentPart
' Load the officeDocument part into an XML document.
Dim doc As XmlDocument = New XmlDocument
doc.Load(documentPart.GetStream)
' Manage namespaces to perform XPath queries.
Dim nt As NameTable = New NameTable
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
nsManager.AddNamespace("w", wordmlNamespace)
Dim nodes As XmlNodeList = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager)
For Each node As System.Xml.XmlNode In nodes
' Retrieve the current orientation for the section.
' Assume the orientation is portrait.
Dim orientation As PrintOrientation = PrintOrientation.Portrait
Dim attr As XmlAttribute = node.Attributes("w:orient")
If (Not (attr) Is Nothing) Then
Select Case (attr.Value)
Case "portrait"
orientation = PrintOrientation.Portrait
Case "landscape"
orientation = PrintOrientation.Landscape
End Select
End If
' Compare the current orientation to the requested orientation.
' If it is the same, get out. Otherwise, make the changes.
If (newOrientation <> orientation) Then
If (attr Is Nothing) Then
' Create the attribute. Although this is not necessary
' when there is no change in orientation,
' setting it has no negative effect.
attr = node.Attributes.Append(doc.CreateAttribute("w:orient", wordmlNamespace))
End If
Select Case (newOrientation)
Case PrintOrientation.Landscape
attr.Value = "landscape"
Case PrintOrientation.Portrait
attr.Value = "portrait"
End Select
Dim pageSizeNode As XmlNode = node.ParentNode.SelectSingleNode("w:pgMar", nsManager)
If (Not (pageSizeNode) Is Nothing) Then
' Swap page dimensions.
Dim width As String = Nothing
Dim height As String = Nothing
Dim widthAttr As XmlAttribute = Nothing
Dim heightAttr As XmlAttribute = Nothing
widthAttr = node.Attributes("w:w")
If (Not (widthAttr) Is Nothing) Then
width = widthAttr.Value
End If
heightAttr = node.Attributes("w:h")
If (Not (heightAttr) Is Nothing) Then
height = heightAttr.Value
End If
If (Not (widthAttr) Is Nothing) Then
widthAttr.Value = height
End If
If (Not (heightAttr) Is Nothing) Then
heightAttr.Value = width
End If
' Rotate margins. Printer settings determine how far you
' rotate when switching to landscape mode. Not having those
' settings, this code rotates 90 degrees. You can
' modify this behavior, or make it a parameter for the
' procedure.
Dim top As String = Nothing
Dim bottom As String = Nothing
Dim left As String = Nothing
Dim right As String = Nothing
Dim topAttr As XmlAttribute = Nothing
Dim leftAttr As XmlAttribute = Nothing
Dim bottomAttr As XmlAttribute = Nothing
Dim rightAttr As XmlAttribute = Nothing
topAttr = pageSizeNode.Attributes("w:top")
If (Not (attr) Is Nothing) Then
top = topAttr.Value
End If
leftAttr = pageSizeNode.Attributes("w:left")
If (Not (attr) Is Nothing) Then
left = leftAttr.Value
End If
rightAttr = pageSizeNode.Attributes("w:right")
If (Not (attr) Is Nothing) Then
right = rightAttr.Value
End If
bottomAttr = pageSizeNode.Attributes("w:bottom")
If (Not (attr) Is Nothing) Then
bottom = bottomAttr.Value
End If
If (Not (topAttr) Is Nothing) Then
topAttr.Value = left
End If
If (Not (leftAttr) Is Nothing) Then
leftAttr.Value = bottom
End If
If (Not (rightAttr) Is Nothing) Then
rightAttr.Value = top
End If
If (Not (bottomAttr) Is Nothing) Then
bottomAttr.Value = right
End If
End If
End If
Next
' Save the document XML back to its part.
doc.Save(documentPart.GetStream)
End Sub
public enum PrintOrientation
{
Landscape,
Portrait,
}
public static void WDSetPrintOrientation(string docName, PrintOrientation newOrientation)
{
// Given a document name, set the print orientation for all the sections of the document.
// Example:
// WDSetPrintOrientation(@"C:\Samples\SetOrientation.docx", PrintOrientation.Landscape);
const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
using (WordprocessingDocument wdPackage = WordprocessingDocument.Open(docName, true))
{
// Get the officeDocument part.
MainDocumentPart documentPart = wdPackage.MainDocumentPart;
// Load the officeDocument part into an XML document.
XmlDocument doc = new XmlDocument();
doc.Load(documentPart.GetStream());
// Manage namespaces to perform XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);
XmlNodeList nodes = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager);
foreach (System.Xml.XmlNode node in nodes)
{
// Retrieve the current orientation for the section.
// Assume the orientation is portrait.
PrintOrientation orientation = PrintOrientation.Portrait;
XmlAttribute attr = node.Attributes["w:orient"];
if (attr != null)
{
switch (attr.Value)
{
case "portrait":
orientation = PrintOrientation.Portrait;
break;
case "landscape":
orientation = PrintOrientation.Landscape;
break;
}
}
// Compare the current orientation to the requested orientation.
// If it is the same, get out. Otherwise, make the changes.
if (newOrientation != orientation)
{
if (attr == null)
{
// Create the attribute. Although this is not necessary
// when there is no change in orientation,
// setting it has no negative effect.
attr = node.Attributes.Append(doc.CreateAttribute("w:orient", wordmlNamespace));
}
switch (newOrientation)
{
case PrintOrientation.Landscape:
attr.Value = "landscape";
break;
case PrintOrientation.Portrait:
attr.Value = "portrait";
break;
}
XmlNode pageSizeNode = node.ParentNode.SelectSingleNode("w:pgMar", nsManager);
if (pageSizeNode != null)
{
// Swap page dimensions.
string width = null;
string height = null;
XmlAttribute widthAttr = null;
XmlAttribute heightAttr = null;
widthAttr = node.Attributes["w:w"];
if (widthAttr != null)
{
width = widthAttr.Value;
}
heightAttr = node.Attributes["w:h"];
if (heightAttr != null)
{
height = heightAttr.Value;
}
if (widthAttr != null)
{
widthAttr.Value = height;
}
if (heightAttr != null)
{
heightAttr.Value = width;
}
// Rotate margins. Printer settings determine how far you
// rotate when switching to landscape mode. Not having those
// settings, this code rotates 90 degrees. You can
// modify this behavior, or make it a parameter for the
// procedure.
string top = null;
string bottom = null;
string left = null;
string right = null;
XmlAttribute topAttr = null;
XmlAttribute leftAttr = null;
XmlAttribute bottomAttr = null;
XmlAttribute rightAttr = null;
topAttr = pageSizeNode.Attributes["w:top"];
if (attr != null)
{
top = topAttr.Value;
}
leftAttr = pageSizeNode.Attributes["w:left"];
if (attr != null)
{
left = leftAttr.Value;
}
rightAttr = pageSizeNode.Attributes["w:right"];
if (attr != null)
{
right = rightAttr.Value;
}
bottomAttr = pageSizeNode.Attributes["w:bottom"];
if (attr != null)
{
bottom = bottomAttr.Value;
}
if (topAttr != null)
{
topAttr.Value = left;
}
if (leftAttr != null)
{
leftAttr.Value = bottom;
}
if (rightAttr != null)
{
rightAttr.Value = top;
}
if (bottomAttr != null)
{
bottomAttr.Value = right;
}
}
}
}
// Save the document XML back to its part.
doc.Save(documentPart.GetStream());
}
}
이 코드는 먼저 2 개의 인쇄 옵션 열거체를 정의합니다.
Public Enum PrintOrientation
Landscape
Portrait
End Enum
public enum PrintOrientation
{
Landscape,
Portrait,
}
그리고 코드는 WDSetPrintOrientation 를 호출하여, Word 2007 문서 참조
및 필요한 인쇄 방향 (가로 또는 세로)을 건네줍니다. 다음에 사용자는 Office Open XML Format 패키지를
나타내는 WordprocessingDocument 개체를 설치하여, MainDocumentPart
파트 참조를 설정합니다. 사용자는 메모리 상주 XML 문서를 생성하여, 메인 문서 파트의 내용을 로드합니다.
다음에 사용자는 XmlNamespaceManager 개체를 사용하여, w 수식자를 사용하고,
기본값 WordprocessingML 네임 스페이스 참조를 설정하여 네임 스페이스 관리자를 설정합니다. 그리고
사용자는 다음 XPath 식을 사용하여 프린터 고유의 노드를 선택합니다.
Dim nodes As XmlNodeList = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager)
XmlNodeList nodes = doc.SelectNodes("//w:sectPr/w:pgSz", nsManager);
사용자는 w:orient 노드를 테스트하여 현재 설정을 판정합니다. 이 프로시저는 세로 방향을
전제로 합니다.
Dim attr As XmlAttribute = node.Attributes("w:orient")
If (Not (attr) Is Nothing) Then
Select Case (attr.Value)
Case "portrait"
orientation = PrintOrientation.Portrait
Case "landscape"
orientation = PrintOrientation.Landscape
End Select
End If
XmlAttribute attr = node.Attributes["w:orient"];
if (attr != null)
{
switch (attr.Value)
{
case "portrait":
orientation = PrintOrientation.Portrait;
break;
case "landscape":
orientation = PrintOrientation.Landscape;
break;
}
}
프로시저는 요청된 방향이 현재 방향과 같은지 확인하여, 같은 경우는 종료합니다.
If (newOrientation <> orientation) Then
if (newOrientation != orientation)
그렇지 않은 경우, 코드의 나머지 부분은 문서의 방향을 바꾸기 위해서 필요한 인쇄 특성을 변경합니다.
요약
이 문서에서 설명한 것처럼, 「Microsoft
SDK for Open XML Formats 기술 프리뷰」를 참조하면 Word 2007 파일 조작이
매우 간단합니다. 이 일련의 문서 코드를 습득하여, Office Open XML 개체 모델을 사용한
사용자 독자적인 프로그래밍 문제를 해결해 주세요.