Open XML 개체 모델을 사용하여  Word 2007 파일 조작 (파트 3/3)

요약: 이 문서는 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 개체 모델을 사용한  사용자 독자적인 프로그래밍 문제를 해결해 주세요. 


표시: