Last Updated 2011/09/21
このページでは、XPS ドキュメントを作成する手順について説明します。
以下のコードは、Kaxaml を使って作成しました。コードを入力しながら結果をチェックできますので、便利です。しかし、見てのとおり、コードはかなり面倒です。この例では示しませんが、画像を表示したり、テーブルを挿入することもできます。
ところで、このコードを Window.xaml のほうに持っていくと、「'Pages' は、型 'PageContent' の値をサポートしません」エラーになりました。原因は不明ですが、内部的な事情があるのかもしれません。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<DocumentViewer>
<FixedDocument>
<PageContent>
<FixedPage Background="AliceBlue" Width="400" Height="300">
<Canvas FixedPage.Top="20" FixedPage.Left="40" Background="Beige" Width="340" Height="260">
<TextBlock Canvas.Top="20" FontSize="14">
これは FixedDocument オブジェクトの例です。
</TextBlock>
<TextBlock Canvas.Top="40" FontSize="14">
薄いブルーの範囲は FixedPage 要素の領域で、<LineBreak/>いわゆる、用紙のサイズになります。
</TextBlock>
<TextBlock Canvas.Top="80" FontSize="14">
ベージュ色の範囲が Canvas 要素の領域です。
</TextBlock>
<TextBlock Canvas.Top="100" FontSize="14">
テキストは Canvas 要素に TextBlock 要素を配置して<LineBreak/>表示しています。
</TextBlock>
<Rectangle Canvas.Top="150" Canvas.Left="20" Width="300" Height="10">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Canvas.Top="170" FontSize="14">
フロードキュメントとは異なり、テキストの位置や<LineBreak />改行を明示的に設定しなければなりません。
</TextBlock>
</Canvas>
</FixedPage>
</PageContent>
</FixedDocument>
</DocumentViewer>
</Page>
前の項で、XAML コードだけで FixedDocument を構築する手順を説明しましたが、ここでは分離コードだけを使って構築します。ドキュメントの内容は 2 ページの構成で、1 ページ目は「あいうえお」と、2 ページ目には「かきくけこ」と表示するだけのものです。しかし、これだけのために、いかに面倒かを実感してもらえばいいと思います。
フォームに DocumentViewer コントロールと button1 を配置します。button1 をクリックすると、c:\Test.xps を出力します。
private void button1_Click(object sender, ExecutedRoutedEventArgs e)
{
const int WIDTH = 794; // A4 版の幅 = (210 / 25.4) * 96
const int HEIGHT = 1123; // 同、高さ = (297 / 25.4) * 96
const int MARGIN_LEFT = 94; // = (25 / 25.4) * 96
const int MARGIN_TOP = 57; // = (15 / 25.4) * 96
const int MARGIN_RIGHT = 57; // = (15 / 25.4) * 96
const int MARGIN_BOTTOM = 57; // = (15 / 25.4) * 96
FixedDocument fixedDocument = new FixedDocument();
// FixedDocument クラスの DocumentPaginator プロパティからDocumentPaginator オブジェクトを取得し、
// その PageSize プロパティで用紙のサイズを指定する
fixedDocument.DocumentPaginator.PageSize = new Size(WIDTH, HEIGHT);
// 最初のページの PageContent オブジェクトを作成する
PageContent pageContent1 = new PageContent();
// 最初のページ
FixedPage fixedPage1 = new FixedPage();
fixedPage1.Background = Brushes.AliceBlue; // ページの背景色
fixedPage1.Width = WIDTH; // ページのサイズ
fixedPage1.Height = HEIGHT;
Canvas canvas1 = new Canvas();
canvas1.Background = Brushes.Beige; // canvas1 の背景色
canvas1.Width = WIDTH - MARGIN_LEFT - MARGIN_RIGHT; // canvas1 の幅
canvas1.Height = HEIGHT - MARGIN_TOP - MARGIN_BOTTOM; // 同、高さ
// ページ内における canvas1 の位置
FixedPage.SetLeft(canvas1, MARGIN_LEFT);
FixedPage.SetTop(canvas1, MARGIN_TOP);
TextBlock textBlock1 = new TextBlock();
textBlock1.FontSize = 16.0;
textBlock1.Text = "あいうえお";
Canvas.SetLeft(textBlock1, 20); // canvas1 内における textBlock1 の X 座標
Canvas.SetTop(textBlock1, 20); // 同、Y 座標
canvas1.Children.Add(textBlock1);
// ページの子要素として追加
fixedPage1.Children.Add(canvas1);
// AddChild メソッドは PageContent クラスの直接のメソッドではないので、キャストが必要
((IAddChild)pageContent1).AddChild(fixedPage1);
//-----------------------------------------------------
// 2 番目ののページの PageContent オブジェクトを作成する
PageContent pageContent2 = new PageContent();
// 2 番目のページ
FixedPage fixedPage2 = new FixedPage();
fixedPage2.Background = Brushes.AliceBlue; // ページの背景色
fixedPage2.Width = WIDTH; // ページのサイズ
fixedPage2.Height = HEIGHT;
Canvas canvas2 = new Canvas();
canvas2.Background = Brushes.MistyRose; // canvas2 の背景色
canvas2.Width = WIDTH - MARGIN_LEFT - MARGIN_RIGHT; // canvas2 の幅
canvas2.Height = HEIGHT - MARGIN_TOP - MARGIN_BOTTOM; // 同、高さ
// ページ内における canvas2 の位置
FixedPage.SetLeft(canvas2, MARGIN_LEFT);
FixedPage.SetTop(canvas2, MARGIN_TOP);
TextBlock textBlock2 = new TextBlock();
textBlock2.FontSize = 16.0;
textBlock2.Text = "かきくけこ";
Canvas.SetLeft(textBlock2, 20); // canvas2 内における textBlock2 の X 座標
Canvas.SetTop(textBlock2, 20); // 同、Y 座標
canvas2.Children.Add(textBlock2);
// ページの子要素として追加
fixedPage2.Children.Add(canvas2);
((IAddChild)pageContent2).AddChild(fixedPage2);
// PageContent オブジェクトを FixedDocument オブジェクトの Pages プロパティから
// 参照する PageContentCollection オブジェクトに追加する
fixedDocument.Pages.Add(pageContent1);
fixedDocument.Pages.Add(pageContent2);
// パッケージを作成
Package package = Package.Open(@"c:\test.xps", FileMode.Create);
// パッケージと関連付ける XpsDocument オブジェクトを作成
XpsDocument xpsDoc = new XpsDocument(package);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
// FixedDocument オブジェクトをファイルに出力する
writer.Write(fixedDocument.DocumentPaginator);
// 必須
xpsDoc.Close();
package.Close();
}
XPS ファイルはフロードキュメントからも作ることができます。ここでは、FlowDocument オブジェクトを分離コードだけで作成し、XPS ファイルとして保存します。
private void button1_Click(object sender, RoutedEventArgs e)
{
FlowDocument flowDocument = new FlowDocument();
flowDocument.Background = Brushes.AliceBlue;
Paragraph paragraph = new Paragraph(new Run("あいうえお"));
flowDocument.Blocks.Add(paragraph);
// FlowDocument クラスには DocumentPaginator プロパティはないが、
// IDocumentPaginatorSource インターフェースを継承するので
// キャストすれば取得可能
DocumentPaginator docPaginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator;
// ページのサイズを設定する(ここでは A4 版 にした)
docPaginator.PageSize = new Size(794, 1123);
XpsDocument xpsDoc = new XpsDocument(@"c:\test_flow.xps", FileAccess.ReadWrite);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
writer.Write(docPaginator);
// 必須
xpsDoc.Close();
}
上記では XPS ファイルを手作りする手順を説明しましたが、一発で作る方法もあります。Windows Vista において、テキストファイルを印刷するときに表示される印刷ダイアログボックスのプリンタ一覧の項を見ると、[Microsoft XPS Document Writer] があるはずです。これはいわゆる仮想プリンタで、たとえば、テキストファイルを印刷するときにこの仮想プリンタを選択すると、テキストファイルの内容が XPS ファイルとして出力されます。もちろん、テキストファイルでは愛想のないものになりますが、HTML ファイルを同じ手順で印刷すると、かなり立派な XPS ファイルになります。
XPS ファイルの作成を自動化することはできませんが、手軽さは評価できます。ドキュメントの表示だけでなく、作成も無料なので、PDF にとってかわるだろうと予想するむきもあります。私は無条件には賛成しませんが。
下図は、XPS Viewer を使って XML Paper Specification v.1.0 である XPS_1_0.xps を表示したところです。ビューワーの [表示]-[ドキュメントアウトライン] を選択すると、ツリー構造で表示する目次があらわれます。

私もぜひほしい機能の一つですが、これはどうやれば実現できるのでしょうか。WPF の中にはそのための機能はありません。
XPS_1_0.xps を見ると、DocumentOutline 要素がありますので、アウトラインを構築することは可能だということは分かります。つまり、直接 XAML コードを書き込めば可能なのかもしれません。
−以上−