Word から InlineShape の画像を取り出す
Word 文書に埋め込まれた画像を取り出すにはどうしたら…といろいろ調べて回ってみると、クリップボードを経由して画像を取りだしているサンプルは見つけた。が、Word は拡縮前(劣化前)の画像データを持っている*1のでそれを何とかして取りだしてみた。
スマートなやり方ではない気がするけどポイントは下記の通り
- Range.get_XML() から該当部分の DOCX(XML) を取得して、Base64Encode された元の画像データを取得する
- 取得した画像データから実際に Word 上に表示されているサムネイル画像を作成する
- Word 上の画像の単位は Point なので Pixel へ変換するため、PointsToPixels を用いて変換する
下記サンプルを実行すれば、Shape.png と Thumb.png が生成される。
object missing = Type.Missing;
// 取り出したい InlineShape
var shape = ActiveDocument.InlineShapes[1];
// 画像じゃなければ処理しない(チャートの場合などがある)
if ( shape.Type != Word.WdInlineShapeType.wdInlineShapePicture )
return;
System.Drawing.Image image = null; // 元画像
System.Drawing.Image thumb = null; // サムネイル
// InlineShape の高さと幅を取得する
// Width, Height はそのままだと単位が pt なので pixel へ変換する
int pixWidth = (int)shape.Application.PointsToPixels(shape.Width, ref missing);
int pixHeight = (int)shape.Application.PointsToPixels(shape.Height, ref missing);
// 素の画像データを取得するため DOCX に埋め込まれる XML を取得する
// この中に Base64 エンコードされた素の画像データが入っている
XmlDocument xmlShape = new XmlDocument();
xmlShape.LoadXml(shape.Range.get_XML(false));
XmlNamespaceManager nm = new XmlNamespaceManager(xmlShape.NameTable);
nm.AddNamespace("w", "http://schemas.microsoft.com/office/word/2003/wordml");
// 画像データがあるノードを取得する
var binDataNode = xmlShape.DocumentElement.SelectSingleNode("//w:pict/w:binData", nm);
if ( binDataNode == null )
{ // XML 内に画像データが見つからなかった
// 代替として表示されているものをクリップボードを経由して画像作成
shape.Select();
shape.Application.Selection.Copy();
image = Clipboard.GetImage();
}
else
{ // 素の画像データが取得できた場合
// メモリに展開した後に Image クラスを作成
MemoryStream ms = new MemoryStream(System.Convert.FromBase64String(binDataNode.InnerText));
image = System.Drawing.Image.FromStream(ms);
// クリッピングされている場合の処理
// PictureFormat に CropXXXX として値が入っているので取得する
// (Width とかと同じく pt から pixel へ変換する)
int cropTop = (int)shape.Application.PointsToPixels(shape.PictureFormat.CropTop, ref missing);
int cropBottom = (int)shape.Application.PointsToPixels(shape.PictureFormat.CropBottom, ref missing);
int cropLeft = (int)shape.Application.PointsToPixels(shape.PictureFormat.CropLeft, ref missing);
int cropRight = (int)shape.Application.PointsToPixels(shape.PictureFormat.CropRight, ref missing);
// クリッピング用 Rectangle 作成
System.Drawing.Rectangle clippingRect = new System.Drawing.Rectangle
{
X = cropLeft,
Y = cropTop,
Width = image.Width - ( cropRight + cropLeft ),
Height = image.Height - ( cropBottom + cropTop ),
};
// サムネイル用の Bitmap を作成(Height と Width は現在表示しているサイズで)
thumb = new System.Drawing.Bitmap(pixWidth, pixHeight, image.PixelFormat);
// サムネイルに描画する
var g = System.Drawing.Graphics.FromImage(thumb);
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawImage(image,
new System.Drawing.Rectangle(0, 0, thumb.Width, thumb.Height),
clippingRect,
System.Drawing.GraphicsUnit.Pixel);
}
image.Save("Shape.png", ImageFormat.Png);
if ( thumb != null )
{
thumb.Save("Thumb.png", ImageFormat.Png);
}
*1:大きい画像を貼り付けると自動的に縮小されるが、画像を右クリック>サイズからリセットをすれば実寸大に表示される