【问题标题】:Word Interop - Save embedded shape as imageWord 互操作 - 将嵌入的形状另存为图像
【发布时间】:2020-08-14 08:58:13
【问题描述】:

我正在尝试使用 C# 将嵌入的 shape 保存为图像。

如果对象嵌入为实际图像 (WMF/JPEG),我可以毫无问题地检索图像,但是当对象是嵌入形状或在 Word 中显示为图像的 OLE 对象时,我似乎无法提取或检索然后将所述对象复制到剪贴板或保存所述图像。

这是我当前的代码示例;要么对象为空,要么出现以下错误:

System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'

感谢任何帮助。谢谢

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ImageMagickSandboxWinForms
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        public static BitmapSource ConvertBitmap(Bitmap source)
        {
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                          source.GetHbitmap(),
                          IntPtr.Zero,
                          Int32Rect.Empty,
                          BitmapSizeOptions.FromEmptyOptions());
        }

        public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
        {
            Bitmap bitmap;
            using (var outStream = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bitmapsource));
                enc.Save(outStream);
                bitmap = new Bitmap(outStream);
            }
            return bitmap;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string physicsDocLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
            physicsDocLocation += @"\[Doc path Here].docx";
            var wordApp = new Microsoft.Office.Interop.Word.Application();

            var wordDoc = wordApp.Documents.Open(physicsDocLocation);
            int iCount = wordDoc.InlineShapes.Count;
            for (int i = 1; i < (wordDoc.InlineShapes.Count + 1); i++)
            {
                var currentInlineShape = wordDoc.InlineShapes[i];
                currentInlineShape.Range.Select();
                wordDoc.ActiveWindow.Selection.Range.Copy();
                BitmapSource clipBoardImage = System.Windows.Clipboard.GetImage();
                Bitmap bmpClipImage = BitmapFromSource(clipBoardImage);
                string finalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), @"TestConversions");
                finalPath += @"\" + Guid.NewGuid().ToString() + ".jpg";
                using (MemoryStream memory = new MemoryStream())
                {
                    using (FileStream fs = new FileStream(finalPath, FileMode.Create, FileAccess.ReadWrite))
                    {
                        bmpClipImage.Save(memory, ImageFormat.Jpeg); <<<---- Error happens here.
                        byte[] bytes = memory.ToArray();
                        fs.Write(bytes, 0, bytes.Length);
                    }
                }
            }
            wordDoc.Close();
            wordApp.Quit();
        }
    }
}

【问题讨论】:

  • 您能否提供更多关于这些嵌入对象类型的信息,如果可能,它们是如何创建的?如果没有这些信息,就不可能测试哪种方法可行。嵌入式 OLE 对象是创建它的程序的“子”,必须访问该服务器才能“获取”它。一般来说,您可能会看到右键单击是否可以选择从嵌入式转换为其他内容。复制(剪切)然后特别粘贴回纯图像格式也可能是一种可能性。
  • @CindyMeister,所以源文档有数学方程和相关图表,我假设它们来自微软的数学方程编辑器(或类似的东西)。奇怪的是,通过上述方法可以轻松地将某些方程式复制为剪贴板中的图像,而其他方程式则不能。
  • @CindyMeister,此外,如果我手动复制/粘贴,方程式会按预期作为图像出现。希望这会有所帮助。
  • 然后使用 PasteSpecial 执行此操作,以便您可以指定粘贴时使用的格式。并且应该将其他信息真正编辑到问题中,而不是留在评论中。

标签: c# ms-word office-interop data-conversion


【解决方案1】:

我的库中有这些代码,不知道我在哪里找到的,但希望你为你完成这项工作:我正在使用剪贴板来捕获不同的图像,只是不要忘记,访问剪贴板需要线程

       for (var i = 1; i <= wordApplication.ActiveDocument.InlineShapes.Count; i++)
        {

            var inlineShapeId = i; 


            var thread = new Thread(() => SaveInlineShapeToFile(inlineShapeId, wordApplication));

            // STA is needed in order to access the clipboard
            // https://stackoverflow.com/a/518724/700926
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
        }

   // General idea is based on: https://stackoverflow.com/a/7937590/700926
    protected static void SaveInlineShapeToFile(int inlineShapeId, Application wordApplication)
    {
        // Get the shape, select, and copy it to the clipboard
        var inlineShape = wordApplication.ActiveDocument.InlineShapes[inlineShapeId];
        inlineShape.Select();
        wordApplication.Selection.Copy();

        // Check data is in the clipboard
        if (Clipboard.GetDataObject() != null)
        {
            var data = Clipboard.GetDataObject();

            // Check if the data conforms to a bitmap format
            if (data != null && data.GetDataPresent(DataFormats.Bitmap))
            {
                // Fetch the image and convert it to a Bitmap
                var image = (Image) data.GetData(DataFormats.Bitmap, true);
                var currentBitmap = new Bitmap(image);

                // Save the bitmap to a file
                currentBitmap.Save(@"C:\Users\Username\Documents\" + String.Format("img_{0}.png", inlineShapeId));
            }
        }
    }

如果您使用的是 Winform 或 WPF,则剪贴板对图像的作用会有所不同:

if (Clipboard.ContainsImage())
{
// ImageUIElement.Source = Clipboard.GetImage(); // does not work
    System.Windows.Forms.IDataObject clipboardData = System.Windows.Forms.Clipboard.GetDataObject();
  if (clipboardData != null)
  {
    if (clipboardData.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
    {
        System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)clipboardData.GetData(System.Windows.Forms.DataFormats.Bitmap);
        ImageUIElement.Source =  System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,BitmapSizeOptions.FromEmptyOptions());
        Console.WriteLine("Clipboard copied to UIElement");
    }
  }
}

如果由于格式转换中的错误而无法正常工作,则有此 solution 。所以它很简单,但很容易理解“DeviceIndependentBitmap”的使用逻辑

【讨论】:

  • 你好@Frenchy,谢谢你的分享。不幸的是,我收到此错误:System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Interop.InteropBitmap' to type 'System.Drawing.Image'.' 当我尝试在您的 (Image) 投射线上运行时。
  • 哼..我没有错误..你能把你的文件放在网上的某个地方吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多