目次
概要
Word文書において、見出しや強調表示などの「スタイル」をプログラムから適用する実装です。
OpenXMLでスタイルを扱う場合、単にスタイルIDを指定するだけでは不十分で、そのスタイル定義自体が文書内(styles.xml)に存在する必要があります。ここでは「スタイルがなければ定義を追加し、その上で段落に適用する」という安全な手法を紹介します。
仕様(入出力)
- 入力:
- 対象のWordファイルパス
- 適用したいスタイルID(例: “MyCustomStyle”)
- スタイルの表示名(例: “My Custom Style”)
- 適用対象の段落
- 出力: スタイルが適用されたWord文書
- ライブラリ: DocumentFormat.OpenXml (NuGetパッケージ)
実装メソッド
| メソッド名 | 説明 |
AddStyleIfNotDefined | 文書内に指定されたスタイルIDが存在するか確認し、なければ新規作成して登録します。 |
ApplyStyleToParagraph | 指定された段落のプロパティを操作し、スタイルIDを割り当てます。 |
基本の使い方
using var editor = new MyWordStyleEditor("StyleDoc.docx");
// 1. スタイル定義が存在することを保証(なければ青色・太字のスタイルを作成)
editor.AddStyleIfNotDefined("BlueHeader", "Blue Header Style");
// 2. 段落を作成して追加
var para = editor.AppendParagraph("この行にカスタムスタイルを適用します。");
// 3. スタイルを適用
editor.ApplyStyleToParagraph(para, "BlueHeader");
コード全文
using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
class Program
{
static void Main()
{
string filePath = "StyledDocument.docx";
// テスト用にファイル作成
CreateDummyFile(filePath);
try
{
Console.WriteLine($"Opening {filePath}...");
using var editor = new MyWordStyleEditor(filePath);
// 1. カスタムスタイル定義の追加(ID: CustomTitle, 名前: Custom Title)
// 定義がない場合のみ作成されます
editor.AddStyleIfNotDefined("CustomTitle", "Custom Title");
// 2. 通常の段落追加
editor.AppendParagraph("これは通常のテキストです。");
// 3. スタイル適用対象の段落追加
var styledPara = editor.AppendParagraph("ここはスタイルが適用されたテキストです。");
// 4. スタイルの適用
editor.ApplyStyleToParagraph(styledPara, "CustomTitle");
Console.WriteLine("Style applied and saved.");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static void CreateDummyFile(string path)
{
using var doc = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document);
var main = doc.AddMainDocumentPart();
main.Document = new Document(new Body());
}
}
/// <summary>
/// Wordのスタイル操作を行うクラス
/// </summary>
public sealed class MyWordStyleEditor : IDisposable
{
private WordprocessingDocument _document;
private Body _body;
public MyWordStyleEditor(string filePath)
{
_document = WordprocessingDocument.Open(filePath, true);
_body = _document.MainDocumentPart.Document.Body;
}
public void Dispose()
{
_document?.Dispose();
}
/// <summary>
/// 指定したIDのスタイルが文書にない場合、新規作成して追加します
/// </summary>
public void AddStyleIfNotDefined(string styleId, string styleName)
{
// StyleDefinitionsPartを取得(なければ作成)
var stylePart = _document.MainDocumentPart.StyleDefinitionsPart
?? _document.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
// Stylesルート要素を取得(なければ作成)
if (stylePart.Styles == null)
{
stylePart.Styles = new Styles();
stylePart.Styles.Save();
}
// 既にIDが存在するか確認
if (stylePart.Styles.Elements<Style>().Any(s => s.StyleId == styleId))
{
return; // 既に存在するので何もしない
}
// --- 新しいスタイルの作成 ---
var style = new Style()
{
Type = StyleValues.Paragraph,
StyleId = styleId,
CustomStyle = true
};
style.Append(new StyleName() { Val = styleName });
style.Append(new BasedOn() { Val = "Normal" });
style.Append(new NextParagraphStyle() { Val = "Normal" });
// 書式設定(例:青文字、太字、サイズ24pt)
var rPr = new StyleRunProperties();
rPr.Append(new Color() { Val = "0000FF" }); // 青
rPr.Append(new Bold()); // 太字
rPr.Append(new FontSize() { Val = "48" }); // 24pt (半ポイント指定なので48)
style.Append(rPr);
// Stylesに追加して保存
stylePart.Styles.Append(style);
stylePart.Styles.Save();
}
/// <summary>
/// 指定した段落にスタイルIDを適用します
/// </summary>
public void ApplyStyleToParagraph(Paragraph p, string styleId)
{
// プロパティがなければ作成
if (p.ParagraphProperties == null)
{
p.ParagraphProperties = new ParagraphProperties();
}
// スタイルIDを設定
p.ParagraphProperties.ParagraphStyleId = new ParagraphStyleId() { Val = styleId };
}
// 補助用:段落追加
public Paragraph AppendParagraph(string text)
{
var p = new Paragraph(new Run(new Text(text)));
_body.AppendChild(p);
return p;
}
}
カスタムポイント
- スタイル定義の内容変更:AddStyleIfNotDefined メソッド内の StyleRunProperties に対する設定を変更することで、好みのデザインを作成できます。
Color: 文字色(16進数RGB)FontSize: 文字サイズ(単位は半ポイント。24ptなら “48”)Underline: 下線
- 段落スタイルの設定:文字単位の装飾(RunProperties)だけでなく、段落単位の設定(ParagraphProperties)もスタイルに含めることができます。例えば Justification(配置)や SpacingBetweenLines(行間)などを style.Append(…) で追加します。
注意点
- StyleIdとStyleNameの違い:StyleId はプログラム内部で使用する識別子(スペースなし推奨)、StyleName はWordの画面上でユーザーに見える名前です。ApplyStyleToParagraph では StyleId を使用します。
- StyleDefinitionsPartの欠落:空の文書を新規作成した直後は、StyleDefinitionsPart(styles.xml)自体が存在しないことがあります。コード例のように AddNewPart で作成するロジックが必須です。
- 組み込みスタイル:”Heading1″(見出し1)などの組み込みスタイルも、文書によっては定義が含まれていない場合があります。その場合も同様に定義を追加する必要がありますが、組み込みスタイルの定義は複雑であるため、あらかじめWord側でテンプレートを作成しておく手法も有効です。
応用
既存のスタイルを検索して適用する
IDが不明な場合に、スタイル名(表示名)からIDを検索して適用する方法です。
public void ApplyStyleByName(Paragraph p, string styleName)
{
var styles = _document.MainDocumentPart.StyleDefinitionsPart?.Styles;
if (styles == null) return;
// 名前が一致するスタイルを探す
var style = styles.Elements<Style>()
.FirstOrDefault(s => s.StyleName != null && s.StyleName.Val == styleName);
if (style != null)
{
ApplyStyleToParagraph(p, style.StyleId);
}
}
まとめ
OpenXMLでスタイルを適用するには、「スタイル定義の登録(AddStyle)」と「段落への紐付け(ApplyStyle)」の2段階のプロセスが必要です。特にスタイル定義部分はXML構造が複雑になりがちですが、コード例のように「存在確認をしてから追加する」ロジックをクラス化しておくことで、メインの処理コードを簡潔に保つことができます。
