僕としてはマルチ TIFF の変換が目的でしたが、このコードで JPEG やら PNG やらも変換できます。
Window1.xaml.cs
using System;
using System.IO;
using System.IO.Packaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media.Imaging;
using System.Windows.Markup;
using System.Windows.Xps.Packaging;
using System.Windows.Xps;
using Microsoft.Win32;
namespace WpfApplication1
{
public partial class Window1 : Window
{
private XpsDocument _xpsDocument;
private readonly Uri _packageUri;
private readonly OpenFileDialog _openImageFileDialog;
public Window1()
{
InitializeComponent();
this._xpsDocument = null;
this._packageUri = new Uri("pack://work.xps");
this._openImageFileDialog = new OpenFileDialog();
this._openImageFileDialog.Filter = "Image Files|*.bmp;*.jpg;*.jpeg;*.png;*.gif;*.tif;*.tiff";
}
private void _openButton_Click(object sender, RoutedEventArgs e)
{
if (!this._openImageFileDialog.ShowDialog(this).Value)
{
return;
}
if (this._xpsDocument != null)
{
this.CloseDocument();
}
using (Stream imageStream = this._openImageFileDialog.OpenFile())
{
this.BuildXpsDocument(imageStream);
this._documentViewer.Document = this._xpsDocument.GetFixedDocumentSequence();
}
}
private void BuildXpsDocument(Stream imageStream)
{
/*** FixedDocument の作成 ***/
FixedDocument imagesDocument = new FixedDocument();
BitmapDecoder bitmapDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.None, BitmapCacheOption.Default);
foreach (BitmapFrame sourceFrame in bitmapDecoder.Frames)
{
// sourceFrame を直接使用してしまうと、先頭フレーム以外リソース化されない
BitmapFrame separatedFrame = BitmapFrame.Create(sourceFrame);
Size frameSize = new Size(separatedFrame.PixelWidth, separatedFrame.PixelHeight);
PageContent imagePageContent = new PageContent()
{
Width = frameSize.Width,
Height = frameSize.Height
};
FixedPage imagePage = new FixedPage()
{
Width = frameSize.Width,
Height = frameSize.Height
};
Image image = new Image()
{
Width = frameSize.Width,
Height = frameSize.Height,
Source = separatedFrame
};
FixedPage.SetTop(image, 0);
FixedPage.SetLeft(image, 0);
imagePage.Children.Add(image);
((IAddChild)imagePageContent).AddChild(imagePage);
imagesDocument.Pages.Add(imagePageContent);
}
/*** XpsDocument の作成 ***/
MemoryStream packageStream = new MemoryStream();
Package package = Package.Open(packageStream, FileMode.Create, FileAccess.ReadWrite);
// メモリ上の XPS を DocumentViewer に表示するためには Package に URI を割り当てる必要がある
PackageStore.AddPackage(this._packageUri, package);
this._xpsDocument = new XpsDocument(package, CompressionOption.NotCompressed, this._packageUri.AbsoluteUri);
/*** XpsDocument に FixedDocument を書き込む ***/
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(this._xpsDocument);
writer.Write(imagesDocument);
}
private void CloseDocument()
{
this._documentViewer.Document = null;
this._xpsDocument.Close();
Package package = PackageStore.GetPackage(this._packageUri);
package.Close();
PackageStore.RemovePackage(this._packageUri);
}
}
}
Window1.xaml
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="419" Width="648">
<Grid>
<ToolBar Band="1" BandIndex="1" Height="26" Name="_mainToolBar" VerticalAlignment="Top" >
<Button Name="_openButton" Height="20" Width="40" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Click="_openButton_Click">開く</Button>
</ToolBar>
<DocumentViewer Name="_documentViewer" VerticalAlignment="Top" Margin="0,26,0,0" />
</Grid>
</Window>
動作させるには、Visual Studio にて "WpfApplication1" という名前の WPF アプリケーションプロジェクトを作成して、各コードファイルを上書きしてください。
また、ReachFramework.dll と System.Printing.dll への参照設定が必要となります。
// using System;
// using System.Drawing;
// using System.Drawing.Imaging;
// using System.IO;
static void Main()
{
Image page1 = new Bitmap("C:\\work\\page3.bmp");
Image page2 = new Bitmap("C:\\work\\page3.bmp");
Image page3 = new Bitmap("C:\\work\\page3.bmp");
// TIFF 形式のエンコーダ。
ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders();
Predicate<ImageCodecInfo> tiffEncoderPredicate =
delegate(ImageCodecInfo input)
{
return (input.FormatID == ImageFormat.Tiff.Guid);
};
ImageCodecInfo tiffEncoder = Array.Find(imageEncoders, tiffEncoderPredicate);
// エンコーダのパラメータ。
EncoderParameters tiffEncoderParameters = new EncoderParameters(2);
tiffEncoderParameters.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
tiffEncoderParameters.Param[1] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionNone);
// 作業領域となるメモリストリーム。
MemoryStream tiffStream = new MemoryStream();
// Save メソッドによって、1ページ目が保存され、更に Image オブジェクトとメモリストリームが関連付けられる。
page1.Save(tiffStream, tiffEncoder, tiffEncoderParameters);
// 2ページ目, 3ページ目の保存に使用するエンコーダのパラメータ。
EncoderParameters pageEncoderParameters = new EncoderParameters(2);
pageEncoderParameters.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
pageEncoderParameters.Param[1] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionNone);
// 先ほどの Save メソッドで関連付けられたメモリストリームに対して保存される。
// この操作は page1 にフレームを追加するわけではない。
page1.SaveAdd(page2, pageEncoderParameters);
page1.SaveAdd(page3, pageEncoderParameters);
// メモリストリームの内容をファイルに保存する。
File.WriteAllBytes("C:\\work\\test.tif", tiffStream.ToArray());
}
EncoderParameter のインスタンスを生成する際、EncoderValue 列挙値を long にキャストして渡しています。これは int ではなく long にしなければいけません。でないと失敗します。
サンプルなんで Dispose メソッドは呼び出してません。Image, MemoryStream 以外にも、EncoderParameters や EncoderParameter も IDisposable を実装してます。