有没有支持“复制为图片”的Word插件

这个问题困扰我好几年了:

幕布自身并不支持表格。每次需要表格,我都是先用Word编辑好,再以图片的形式粘贴到幕布中。

步骤十分繁琐:

在Word中选中 > 复制 > 粘贴为图片 > 再次剪切图片 > 在幕布中粘贴
image

那么有没有Office插件能直接把Word选定的内容复制为图片呢?

像Excel的自带功能这样
image

新建一个宏,录制动作就好了.如果运行出错,编辑宏,把picture n的这一行注释掉.

参考代码
Sub copy2img()

’ copy2img
’ office复制内容为图片 宏
’ 快捷键: Ctrl+m

Selection.Copy
ActiveSheet.Pictures.Paste.Select

’ ActiveSheet.Shapes.Range(Array(“Picture 6”)).Select
Application.CutCopyMode = False
Selection.Cut
End Sub

我终于懂了 OneNote 里复制为图片是干什么用的了…

OneNote也没有复制为图片的选项呀

试了下,注不注释都不能运行 :joy:

重新录了一个。倒是可以一键执行了,不过由于并没有实质上避开“粘贴为图片”的操作,执行时屏幕会闪一下,偶尔边距还会出现奇怪的空白

Sub 复制为图片()
'
' 复制为图片 宏
'
'
    Selection.Copy
    Selection.MoveRight Unit:=wdCharacter, Count:=1
    Selection.TypeParagraph
    Selection.PasteSpecial Link:=False, DataType:=wdPasteEnhancedMetafile, _
        Placement:=wdInLine, DisplayAsIcon:=False
    Selection.MoveLeft Unit:=wdCharacter, Count:=2, Extend:=wdExtend
    Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
    Selection.Cut
End Sub

啊,那为什么我复制的就是图片呢

点那个框框复制就行

Screen-Appinn2020-12-22 20.38.59

可能版本不一样?反正就那么回事,录制一下,如果哪个步骤录制的和期望不一样,手动再改下就完事。office的宏还是蛮好用的,只是有的公司处于安全考虑,会禁止使用宏。

office内容复制后一般有EMF格式的剪贴板数据。
可以使用代码读取后,转换为位图格式,再写回剪贴板,然后就可以在别的地方粘贴了。

粘贴结果:

Quicker动作:

主要代码:

//.cs  文件类型,便于外部编辑时使用
// 引用必要的命名空间
using System.Windows.Forms;
using System.Runtime.InteropServices;  
using System.Drawing;
using System.Drawing.Imaging;
using System.IO; 

public class ClipboardFunctions  
{  
    [DllImport("user32.dll", EntryPoint = "OpenClipboard", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]  
    public static extern bool OpenClipboard(IntPtr hWnd);  

    [DllImport("user32.dll", EntryPoint = "EmptyClipboard", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]  
    public static extern bool EmptyClipboard();  

    [DllImport("user32.dll", EntryPoint = "SetClipboardData", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]  
    public static extern IntPtr SetClipboardData(int uFormat, IntPtr hWnd);  

    [DllImport("user32.dll", EntryPoint = "CloseClipboard", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]  
    public static extern bool CloseClipboard();  

    [DllImport("user32.dll", EntryPoint = "GetClipboardData", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]  
    public static extern IntPtr GetClipboardData(int uFormat);  

    [DllImport("user32.dll", EntryPoint = "IsClipboardFormatAvailable", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]  
    public static extern short IsClipboardFormatAvailable(int uFormat);  
}  


// Quicker将会调用的函数
public static void Exec(Quicker.Public.IStepContext context)
{
    //var oldValue = context.GetVarValue("varName");  // 读取动作里的变量值
    //MessageBox.Show(oldValue as string);
    //context.SetVarValue("varName", "从脚本输出的内容。"); // 向变量里输出值
    //MessageBox.Show("Hello World!");
    
    const int CF_ETAFILE = 14;  
    IntPtr intptr;  

    System.Drawing.Imaging.Metafile myMetaFile = null;  
    if (ClipboardFunctions.OpenClipboard(IntPtr.Zero))  
    {  
        if (ClipboardFunctions.IsClipboardFormatAvailable(CF_ETAFILE) != 0)  
        {  
            intptr = ClipboardFunctions.GetClipboardData(CF_ETAFILE);  
            myMetaFile = new System.Drawing.Imaging.Metafile(intptr, true);  
            ClipboardFunctions.CloseClipboard();  
          
            
          
          using(MemoryStream stream = new MemoryStream())
          {
           	myMetaFile.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            var bitmap = Image.FromStream(stream);
            //Clipboard.SetImage(bitmap);
            SetClipboardImage((Bitmap)bitmap, null,null);
            
            return;
           }
        }  
    }  
    
    throw new Exception("剪贴板中没有EMF图片信息,请从Office中复制内容后重试。");
}

/// <summary>
/// Copies the given image to the clipboard as PNG, DIB and standard Bitmap format.
/// </summary>
/// <param name="image">Image to put on the clipboard.</param>
/// <param name="imageNoTr">Optional specifically nontransparent version of the image to put on the clipboard.</param>
/// <param name="data">Clipboard data object to put the image into. Might already contain other stuff. Leave null to create a new one.</param>
public static void SetClipboardImage(Bitmap image, Bitmap imageNoTr, DataObject data)
{
    Clipboard.Clear();
    if (data == null)
        data = new DataObject();
    if (imageNoTr == null)
        imageNoTr = image;
    using (MemoryStream pngMemStream = new MemoryStream())
    using (MemoryStream dibMemStream = new MemoryStream())
    {
        // As standard bitmap, without transparency support
        data.SetData(DataFormats.Bitmap, true, imageNoTr);
        // As PNG. Gimp will prefer this over the other two.
        image.Save(pngMemStream, ImageFormat.Png);
        data.SetData("PNG", false, pngMemStream);
        // As DIB. This is (wrongly) accepted as ARGB by many applications.
        Byte[] dibData = ConvertToDib(image);
        dibMemStream.Write(dibData, 0, dibData.Length);
        data.SetData(DataFormats.Dib, false, dibMemStream);
        // The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation.
        Clipboard.SetDataObject(data, true);
    }
}
    
/// <summary>
/// Converts the image to Device Independent Bitmap format of type BITFIELDS.
/// This is (wrongly) accepted by many applications as containing transparency,
/// so I'm abusing it for that.
/// </summary>
/// <param name="image">Image to convert to DIB</param>
/// <returns>The image converted to DIB, in bytes.</returns>
public static Byte[] ConvertToDib(Image image)
{
    Byte[] bm32bData;
    Int32 width = image.Width;
    Int32 height = image.Height;
    // Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
    using (Bitmap bm32b = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb))
    {
        using (Graphics gr = Graphics.FromImage(bm32b))
            gr.DrawImage(image, new Rectangle(0, 0, bm32b.Width, bm32b.Height));
        // Bitmap format has its lines reversed.
        bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
        Int32 stride;
        bm32bData = GetImageData(bm32b, out stride);
    }
    // BITMAPINFOHEADER struct for DIB.
    Int32 hdrSize = 0x28;
    Byte[] fullImage = new Byte[hdrSize + 12 + bm32bData.Length];
    //Int32 biSize;
    WriteIntToByteArray(fullImage, 0x00, 4, true, (UInt32)hdrSize);
    //Int32 biWidth;
    WriteIntToByteArray(fullImage, 0x04, 4, true, (UInt32)width);
    //Int32 biHeight;
    WriteIntToByteArray(fullImage, 0x08, 4, true, (UInt32)height);
    //Int16 biPlanes;
    WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
    //Int16 biBitCount;
    WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
    //BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
    WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
    //Int32 biSizeImage;
    WriteIntToByteArray(fullImage, 0x14, 4, true, (UInt32)bm32bData.Length);
    // These are all 0. Since .net clears new arrays, don't bother writing them.
    //Int32 biXPelsPerMeter = 0;
    //Int32 biYPelsPerMeter = 0;
    //Int32 biClrUsed = 0;
    //Int32 biClrImportant = 0;

    // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
    WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
    WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
    WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
    Array.Copy(bm32bData, 0, fullImage, hdrSize + 12, bm32bData.Length);
    return fullImage;
}

public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt32 value)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        data[offs] = (Byte)(value >> (8 * index) & 0xFF);
    }
}

public static UInt32 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
    UInt32 value = 0;
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        value += (UInt32)(data[offs] << (8 * index));
    }
    return value;
}

/// <summary>
/// Creates a bitmap based on data, width, height, stride and pixel format.
/// </summary>
/// <param name="sourceData">Byte array of raw source data</param>
/// <param name="width">Width of the image</param>
/// <param name="height">Height of the image</param>
/// <param name="stride">Scanline length inside the data</param>
/// <param name="pixelFormat">Pixel format</param>
/// <param name="palette">Color palette</param>
/// <param name="defaultColor">Default color to fill in on the palette if the given colors don't fully fill it.</param>
/// <returns>The new image</returns>
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
{
    Bitmap newImage = new Bitmap(width, height, pixelFormat);
    BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
    Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
    // Compensate for possible negative stride on BMP format.
    Boolean isFlipped = stride < 0;
    stride = Math.Abs(stride);
    // Cache these to avoid unnecessary getter calls.
    Int32 targetStride = targetData.Stride;
    Int64 scan0 = targetData.Scan0.ToInt64();
    for (Int32 y = 0; y < height; y++)
        Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
    newImage.UnlockBits(targetData);
    // Fix negative stride on BMP format.
    if (isFlipped)
        newImage.RotateFlip(RotateFlipType.Rotate180FlipX);
    // For indexed images, set the palette.
    if ((pixelFormat & PixelFormat.Indexed) != 0 && palette != null)
    {
        ColorPalette pal = newImage.Palette;
        for (Int32 i = 0; i < pal.Entries.Length; i++)
        {
            if (i < palette.Length)
                pal.Entries[i] = palette[i];
            else if (defaultColor.HasValue)
                pal.Entries[i] = defaultColor.Value;
            else
                break;
        }
        newImage.Palette = pal;
    }
    return newImage;
}

/// <summary>
/// Gets the raw bytes from an image.
/// </summary>
/// <param name="sourceImage">The image to get the bytes from.</param>
/// <param name="stride">Stride of the retrieved image data.</param>
/// <returns>The raw bytes of the image</returns>
public static Byte[] GetImageData(Bitmap sourceImage, out Int32 stride)
{
    BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    stride = sourceData.Stride;
    Byte[] data = new Byte[stride * sourceImage.Height];
    Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
    sourceImage.UnlockBits(sourceData);
    return data;
}
4 Likes

好厉害的思路,感谢~

我刚好也遇到这个问题,刚好这个问题的需求是“表格转图片”,分享一下我的解决办法:

  1. 访问这个网站
  2. 点击 Import > Excel 粘贴复制的内容(Word 的表格也支持)> Imprt Data
  3. 下面一堆格式中选择 MORE > JPEG ,然后点击 Download

这比先粘贴为图片再剪切更麻烦了 :joy:

粘贴 > 导入 >导出图片
原来我需要的是转换格式啊,需求不同

什么也不需要.

4 Likes

设个赞啊,完全没见过这个页面

这…隐藏的好深啊……不像excel能直接从复制按钮展开

word excel

你可以设置个快捷键, 或者添加到工具栏, 甚至是快速工具栏上.

厉害了,office提供了300%的功能给我们使用…

你的目的是要把这个表格转化成图片而已,没必要纠结用wrod插件或者word本身。其实用以下的方法步数都是差不多的。

  1. snipaste截图,复制到剪贴板,粘贴。
  2. 复制表格,snipaste按F3贴图,发现也是能出现原格式的表格图片
  3. 复制表格,粘贴到微信,发送给自己,就是一张图片。