Share via


[方法] Open XML API を使用して Word 2007 文書内のすべての変更を反映する

Office Open XML パッケージの仕様は、コンテンツを含み、単一のパッケージに格納されているすべてのドキュメント パーツのリレーションシップを定義する XML ファイルのセットを定義しています。これらのパッケージは、Microsoft® Office Excel® 2007、Microsoft Office PowerPoint® 2007、および Microsoft Office Word® 2007 のドキュメント ファイルを構成するドキュメント パーツを結合します。 Open XML API を使用して、パッケージを作成したり、パッケージを構成するファイルを操作したりできます。このトピックでは、Office Open XML パッケージ内の XML を変更して、すべての変更を反映することを Office Word 2007 に伝えるためのコードと手順について説明していますが、手順は Office Open XML 形式をサポートする 3 つのどの 2007 Microsoft Office system プログラムでも同じです。

注意

このトピックのコード サンプルは、Microsoft Visual Basic® .NET および Microsoft Visual C#® で記述されており、Microsoft Visual Studio® 2008 で作成されたアドインで使用できます。Visual Studio 2008 でアドインを作成する方法の詳細については、「Open XML 形式 SDK 1.0 を使用して作業を開始する」を参照してください。

文書内のすべての変更を反映する

以下のコードでは、外部ファイルからのカスタム XML を含む新しいドキュメント パーツを追加し、その後でドキュメント パーツを設定します。

Public Sub WDAcceptRevisions(ByVal docName As String, ByVal authorName As String)
    ' Given a document name and an author name, accept revisions. 
    ' Note: leave author name blank to accept revisions for all authors.
    Const wordmlNamespace As String = "https://schemas.openxmlformats.org/wordprocessingml/2006/main"
    Dim wdDoc As WordprocessingDocument = WordprocessingDocument.Open(docName, True)
    '  Manage namespaces to perform Xml XPath queries.
    Dim nt As NameTable = New NameTable
    Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
    nsManager.AddNamespace("w", wordmlNamespace)
    '  Get the document part from the package.
    Dim xdoc As XmlDocument = New XmlDocument(nt)
    '  Load the XML in the document part into an XmlDocument instance.
    xdoc.Load(wdDoc.MainDocumentPart.GetStream)
    '  Handle the formatting changes.
    Dim nodes As XmlNodeList = Nothing
    If String.IsNullOrEmpty(authorName) Then
        nodes = xdoc.SelectNodes("//w:pPrChange", nsManager)
    Else
        nodes = xdoc.SelectNodes(String.Format("//w:pPrChange[@w:author='{0}']", authorName), nsManager)
    End If
    For Each node As System.Xml.XmlNode In nodes
        node.ParentNode.RemoveChild(node)
    Next
    '  Handle the deletions.
    If String.IsNullOrEmpty(authorName) Then
        nodes = xdoc.SelectNodes("//w:del", nsManager)
    Else
        nodes = xdoc.SelectNodes(String.Format("//w:del[@w:author='{0}']", authorName), nsManager)
    End If
    For Each node As System.Xml.XmlNode In nodes
        node.ParentNode.RemoveChild(node)
    Next
    '  Handle the insertions.
    If String.IsNullOrEmpty(authorName) Then
        nodes = xdoc.SelectNodes("//w:ins", nsManager)
    Else
        nodes = xdoc.SelectNodes(String.Format("//w:ins[@w:author='{0}']", authorName), nsManager)
    End If
    For Each node As System.Xml.XmlNode In nodes
        '  You found one or more new content.
        '  Promote them to the same level as node, and then
        '  delete the node.
        Dim childNodes As XmlNodeList
        childNodes = node.SelectNodes(".//w:r", nsManager)
        For Each childNode As System.Xml.XmlNode In childNodes
            If (childNode Is node.FirstChild) Then
                node.ParentNode.InsertAfter(childNode, node)
            Else
                node.ParentNode.InsertAfter(childNode, node.NextSibling)
            End If
        Next
        node.ParentNode.RemoveChild(node)
        '  Remove the modification id from the node 
        '  so Word can merge it on the next save.
        node.Attributes.RemoveNamedItem("w:rsidR")
        node.Attributes.RemoveNamedItem("w:rsidRPr")
    Next
    '  Save the document XML back to its part.
    xdoc.Save(wdDoc.MainDocumentPart.GetStream(FileMode.Create))
End Sub
public static void WDAcceptRevisions(string docName, string authorName)
{
    // Given a document name and an author name, accept revisions. 
    // Note: leave author name blank to accept revisions for all    

    const string wordmlNamespace = "https://schemas.openxmlformats.org/wordprocessingml/2006/main";

    using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(docName, true))
    {
        //  Manage namespaces to perform Xml XPath queries.
        NameTable nt = new NameTable();
        XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
        nsManager.AddNamespace("w", wordmlNamespace);

        //  Get the document part from the package.
        XmlDocument xdoc = new XmlDocument(nt);
        //  Load the XML in the document part into an XmlDocument instance.
        xdoc.Load(wdDoc.MainDocumentPart.GetStream());

        //  Handle the formatting changes.
        XmlNodeList nodes = null;
        if (string.IsNullOrEmpty(authorName))
        {
            nodes = xdoc.SelectNodes("//w:pPrChange", nsManager);
        }
        else
        {
            nodes = xdoc.SelectNodes(string.Format("//w:pPrChange[@w:author='{0}']", authorName), nsManager);
        }
        foreach (System.Xml.XmlNode node in nodes)
        {
            node.ParentNode.RemoveChild(node);
        }

        //  Handle the deletions.
        if (string.IsNullOrEmpty(authorName))
        {
            nodes = xdoc.SelectNodes("//w:del", nsManager);
        }
        else
        {
            nodes = xdoc.SelectNodes(string.Format("//w:del[@w:author='{0}']", authorName), nsManager);
        }

        foreach (System.Xml.XmlNode node in nodes)
        {
            node.ParentNode.RemoveChild(node);
        }


        //  Handle the insertions.
        if (string.IsNullOrEmpty(authorName))
        {
            nodes = xdoc.SelectNodes("//w:ins", nsManager);
        }
        else
        {
            nodes = xdoc.SelectNodes(string.Format("//w:ins[@w:author='{0}']", authorName), nsManager);
        }

        foreach (System.Xml.XmlNode node in nodes)
        {
            //  You found one or more new content.
            //  Promote them to the same level as node, and then
            //  delete the node.
            XmlNodeList childNodes;
           childNodes = node.SelectNodes(".//w:r", nsManager);
            foreach (System.Xml.XmlNode childNode in childNodes)
            {
                if (childNode == node.FirstChild)
                {
                    node.ParentNode.InsertAfter(childNode, node);
                }
                else
                {
                    node.ParentNode.InsertAfter(childNode, node.NextSibling);
                }
            }
            node.ParentNode.RemoveChild(node);

            //  Remove the modification id from the node 
            //  so Word can merge it on the next save.
            node.Attributes.RemoveNamedItem("w:rsidR");
            node.Attributes.RemoveNamedItem("w:rsidRPr");
        }

        //  Save the document XML back to its document part.
        xdoc.Save(wdDoc.MainDocumentPart.GetStream(FileMode.Create));
    }
}

外部ファイルからのカスタム XML を含む新しいパーツを追加し、その後でパーツを設定するには

  1. 最初に、ソース Word 2007 文書のパスと名前、および必要に応じてドキュメント作成者の名前を表すパラメータを渡します。

  2. 次に、文書を WordprocessingDocument オブジェクトとして開きます。

  3. 次に、文書全体で使用される既定の word 名前空間 (w) への参照を設定します。メイン ドキュメント パーツ (/word/document.xml) のコンテンツが XML ドキュメントに読み込まれます。

  4. その後、if...else ステートメントで、特定の作成者に対応付けられた変更について検査できるようになります。

    作成者の名前がプロシージャに渡されなかった場合は、作成者に関係なく、すべての変更が対象になります。

  5. いずれの場合も、以下のステートメントで、すべての w:pPrChange ノード (存在する場合) を選択します。

    nodes = xdoc.SelectNodes("//w:pPrChange", nsManager)
    
    nodes = xdoc.SelectNodes("//w:pPrChange", nsManager);
    

これらのノードは、文書に対する保留中の書式変更を表します。これらのノードのいずれかが見つかった場合、その子ノードは削除されます。このしくみを理解するために、次の例について考えて見ましょう。

文書の校閲中に、1 行のテキストを強調表示し、そのスタイルを [表題] に設定するとします。この操作を実行すると、メイン ドキュメント パーツに次の XML マークアップが生成されます。

<w:pPr>
   <w:pStyle w:val="Title" /> 
      <w:pPrChange w:id="0" w:author="Nancy Davolio" w:date="2007-07-03T08:22:00Z">
         <w:pPr /> 
      </w:pPrChange>
</w:pPr>
<w:r w:rsidRPr="00655EFA">
  <w:t>Gettysburg Address</w:t> 
</w:r>

w:pStyle 要素は、これがスタイル変更であることを指定します。この場合の変更では、強調表示されたテキストを [表題] スタイルに設定します。w:pPrChange 要素は、変更の作成者と日付を指定します。さらに、この要素は変更が保留中であることを Word 2007 に伝えます。w:r 要素は実行を指定し、w:t 要素は強調表示されたテキストを含むテキストを指定します。この場合のテキストは「Gettysburg Address」の段落です。Office Word 2007 で文書を校閲するときは、テキストを強調表示し、[校閲] タブの [承諾] をクリックし、次に [承諾して次へ進む] をクリックします。Office Word 2007 は、変更を実行し、w:pPrChange 要素を削除します。コード内でこの動作をエミュレートするには、w:pPrChange 要素を削除すします。これにより変更が反映されます。この処理は、コード内で次のステートメントを使用して実行します。

node.ParentNode.RemoveChild(node)
node.ParentNode.RemoveChild(node);

ここでは、現在のノードは w:pPrChange 要素です。現在のノードの親 (w:pStyle 要素) を指定し、RemoveChild メソッドを呼び出して、現在のノード (w:pPrChange 要素) を削除します。これで、変更の反映と同じ結果が得られます。w:del 要素による削除の場合も、同様の処理が実行されます。

挿入は、書式変更よりも複雑です。挿入では、w:ins 要素を使用します。これらの要素のいずれかが、1 つ以上の挿入のコンテナである場合があります。たとえば、次のマークアップに示すように、テキストとスペースを同じ操作で挿入するとします。

<w:ins w:id="12" w:author="Nancy Davolio" w:date="2007-07-03T08:23:00Z">
   <w:r w:rsidR="00655EFA">
      <w:t>word</w:t> 
   </w:r>
   <w:proofErr w:type="spellEnd" /> 
   <w:r w:rsidR="00655EFA">
      <w:t xml:space="preserve"></w:t> 
   </w:r>
</w:ins>

マークアップのこの部分では、「word」という語に続けて 1 個の空白スペースが文書に挿入されます。これらのテキスト要素 (w:t) はそれぞれ実行要素 (w:r) 内に含まれており、それらの実行要素は同じ挿入要素 (w:ins) に含まれています。プログラミング コード プロシージャでは、これらのノード (w:ins ノードの子ノード) は w:ins ノードと同じレベルに昇格されます。その後、w:ins ノードが削除されることで、変更の反映と同じ結果が得られます。

nodes = xdoc.SelectNodes("//w:ins", nsManager)
...
For Each childNode As System.Xml.XmlNode In childNodes
   If (childNode Is node.FirstChild) Then
      node.ParentNode.InsertAfter(childNode, node)
   Else
      node.ParentNode.InsertAfter(childNode, node.NextSibling)
   End If
Next
node.ParentNode.RemoveChild(node)
nodes = xdoc.SelectNodes("//w:ins", nsManager);
...
childNodes = node.SelectNodes(".//w:r", nsManager);
foreach (System.Xml.XmlNode childNode in childNodes)
{
   if (childNode == node.FirstChild)
   {
       node.ParentNode.InsertAfter(childNode, node);
   }
   else
   {
       node.ParentNode.InsertAfter(childNode, node.NextSibling);
   }
}
node.ParentNode.RemoveChild(node);

テキスト要素 (w:t) が処理される順序は重要です。たとえば、文書内の特定の位置に、「word」という語と、それに続けて空白スペースを挿入する場合、「word」の w:t 要素が処理され、親ノード (w:r) の後に挿入されます。次に、スペースの w:t が処理され、親ノードの後に挿入されます。その結果、親要素 (w:r) の後にスペースが表示され、その後にテキスト「word」が表示されます。これは期待した結果ではありません。この問題を解決するには、テキスト要素の順序を検知する必要があります。前のコード例では、最初に出現する w:t 要素を親ノードの後に挿入し、その後、後続の要素を最初の子ノードの後に挿入する方法をとっています。

このコードでは、ノードを適切な順序で配置した後、該当するノードを削除し、更新された XML を文書に保存します。更新された文書を開いたときに、XML ステートメントが不足していることによって、変更が反映されたことが Word 2007 に伝えられます。