web-dev-qa-db-ja.com

プログラムで画像をRTFドキュメントに追加する

RTF作成しているドキュメントに画像を追加しようとしています。「コピー/貼り付け」メソッド(RichTextBox内に画像を貼り付けてからアクセスすることを含む)を使用したくない.RTFプロパティ)は、クリップボードを削除します(これは、エンドユーザーにとって煩わしく混乱するためです)。

これまでのコードでは、画像を印刷するためにRTFドキュメントに挿入する必要がある文字列を返します。入力された画像($ pathにあります)は通常bmpまたはjpeg形式ですが、この段階では、RTF内に画像がどのように保存されるかは関係ありません。

public string GetImage(string path, int width, int height)
{
    MemoryStream stream = new MemoryStream();
    string newPath = Path.Combine(Environment.CurrentDirectory, path);
    Image img = Image.FromFile(newPath);
    img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);

    byte [] bytes = stream.ToArray();

    string str = BitConverter.ToString(bytes, 0).Replace("-", string.Empty);
    //string str = System.Text.Encoding.UTF8.GetString(bytes);

    string mpic = @"{\pict\pngblip\picw" + 
        img.Width.ToString() + @"\pich" + img.Height.ToString() +
        @"\picwgoa" + width.ToString() + @"\pichgoa" + height.ToString() + 
        @"\hex " + str + "}";
    return mpic
}

ただし、問題は、このコードが機能しないことです。私が知る限り、文字列strにはRTF内で機能する正しい文字列変換がありません。

編集:私の問題は、@ "\ hex"の\ hexの後にスペースがなく、BitConverterの戻り値から "-"文字を削除していなかったことです。

25
DerX

これらのリンクを試してください

「picwgoa」を「picwgoal」に、「pichgoa」を「pichgoal」に変更する必要があります

string mpic = @"{\pict\pngblip\picw" + 
    img.Width.ToString() + @"\pich" + img.Height.ToString() +
    @"\picwgoal" + width.ToString() + @"\pichgoal" + height.ToString() + 
    @"\bin " + str + "}";

ここにサポートされている画像フォーマットのリストがあります

\emfblip画像のソースはEMF(拡張メタファイル)です。
\pngblip画像のソースはPNGです。
\jpegblip画像のソースはJPEGです。
\shppict Word 97-2000の画像を指定します。これは宛先コントロールWordです。
\nonshppict Word 97-2000が{\ pict宛先を書き込んだことを指定しますこのキーワードは他のリーダーとの互換性のためです。
\macpict画像のソースはQuickDrawです。
\pmmetafileN画像のソースはOS/2メタファイルです。 N引数はメタファイルタイプを識別します。N値は以下の\ pmmetafileテーブルで説明されています。
\wmetafileN画像のソースはWindowsメタファイルです。N引数はメタファイルタイプを識別します(デフォルトは1)。
\dibitmapN画像のソースは、Windowsデバイスに依存しないビットマップです。N引数は、ビットマップタイプを識別します(0に等しくなければなりません)。RTF indowsデバイスに依存しないビットマップは、実際のピクセルデータが後に続くBITMAPINFO構造を連結したものです。 
\wbitmapN画像のソースは、Windowsデバイスに依存するビットマップです。 N引数はビットマップタイプを識別します(0に等しくなければなりません)。RTF Windowsデバイスに依存するビットマップから含まれる情報は、GetBitmapBits関数の結果です。
31
RRUZ

1日かそこらでググる答えを過ごした。 Stackoverflowやその他のソース全体の情報をまとめました。これに画像をフィードすると、richtextbox.rtf拡張に追加する必要がある文字列が返されます。画像の幅が変化し、計算する必要がある場合は、式を示します。

    // RTF Image Format
    // {\pict\wmetafile8\picw[A]\pich[B]\picwgoal[C]\pichgoal[D]
    //  
    // A    = (Image Width in Pixels / Graphics.DpiX) * 2540 
    //  
    // B    = (Image Height in Pixels / Graphics.DpiX) * 2540 
    //  
    // C    = (Image Width in Pixels / Graphics.DpiX) * 1440 
    //  
    // D    = (Image Height in Pixels / Graphics.DpiX) * 1440 

    [Flags]
    enum EmfToWmfBitsFlags
    {
        EmfToWmfBitsFlagsDefault = 0x00000000,
        EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
        EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
        EmfToWmfBitsFlagsNoXORClip = 0x00000004
    }

    const int MM_ISOTROPIC = 7;
    const int MM_ANISOTROPIC = 8;

    [DllImport("gdiplus.dll")]
    private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize,
        byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
    [DllImport("gdi32.dll")]
    private static extern IntPtr SetMetaFileBitsEx(uint _bufferSize,
        byte[] _buffer);
    [DllImport("gdi32.dll")]
    private static extern IntPtr CopyMetaFile(IntPtr hWmf,
        string filename);
    [DllImport("gdi32.dll")]
    private static extern bool DeleteMetaFile(IntPtr hWmf);
    [DllImport("gdi32.dll")]
    private static extern bool DeleteEnhMetaFile(IntPtr hEmf);

        public static string GetEmbedImageString(Bitmap image)
        {
                Metafile metafile = null;
                float dpiX; float dpiY;

                using (Graphics g = Graphics.FromImage (image)) 
                {
                    IntPtr hDC = g.GetHdc ();
                    metafile = new Metafile (hDC, EmfType.EmfOnly);
                    g.ReleaseHdc (hDC);
                }

                using (Graphics g = Graphics.FromImage (metafile)) 
                {
                    g.DrawImage (image, 0, 0);
            dpiX = g.DpiX;
            dpiY = g.DpiY;
                }

                IntPtr _hEmf = metafile.GetHenhmetafile ();
                uint _bufferSize = GdipEmfToWmfBits (_hEmf, 0, null, MM_ANISOTROPIC,
                EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
                byte[] _buffer = new byte[_bufferSize];
                GdipEmfToWmfBits (_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
                                            EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
                IntPtr hmf = SetMetaFileBitsEx (_bufferSize, _buffer);
                string tempfile = Path.GetTempFileName ();
                CopyMetaFile (hmf, tempfile);
                DeleteMetaFile (hmf);
                DeleteEnhMetaFile (_hEmf);

                var stream = new MemoryStream ();
                byte[] data = File.ReadAllBytes (tempfile);
                //File.Delete (tempfile);
                int count = data.Length;
                stream.Write (data, 0, count);

                string proto = @"{\rtf1{\pict\wmetafile8\picw" + (int)( ( (float)image.Width / dpiX ) * 2540 )
                                  + @"\pich" + (int)( ( (float)image.Height / dpiY ) * 2540 )
                                  + @"\picwgoal" + (int)( ( (float)image.Width / dpiX ) * 1440 )
                                  + @"\pichgoal" + (int)( ( (float)image.Height / dpiY ) * 1440 )
                                  + " " 
                      + BitConverter.ToString(stream.ToArray()).Replace("-", "")
                                  + "}}";                   
                return proto;
        }
14
JiffyWhip

後でこのページにアクセスした場合(数日前など)、次のリンクが役立つ場合があります。 。NETで画像をWMFに変換する

ワードパッドは、適切なWindowsメタファイル形式で保存されていない画像を無視します。したがって、このページの前の例はまったく表示されません(たとえそれがOpenOfficeとWord自体でうまく機能するとしても)。ワードパッドがサポートする形式は次のとおりです。

{/ pict/wmetafile8/picw [width]/pich [height]/picwgoal [scaledwidth]/pichgoal [scaledheight] [image-as-string-of-byte-hex-values]}(角括弧内の用語を適切なデータ)。

上記のリンクの手順に従って、「適切なデータ」を取得できます。 pythonで解決策を探している人のために、ここに解決策の始まりがあります(dpi /スケーリングの問題が残っていると思います)これにはが必要です[〜#〜 ] pil [〜#〜](またはPillow)、ctypes、およびclr(Python .NET)。PIL/Pillowを使用して、最初に画像を開きます。ここにそれがあります「canv」として開く:

from ctypes import *
import clr
clr.AddReference("System.IO")
clr.AddReference("System.Drawing")
from System import IntPtr
from System.Drawing import SolidBrush
from System.Drawing import Color
from System.Drawing import Imaging
from System.Drawing import Graphics
from System.IO import FileStream
from System.IO import FileMode
from System.IO import MemoryStream
from System.IO import File

def byte_to_hex(bytefile):
  acc = ''
  b = bytefile.read(1)
  while b:
    acc+=("%02X" % ord(b))
    b = bytefile.read(1)
  return acc.strip()

#... in here is some code where 'canv' is created as the PIL image object, and
#...   'template' is defined as a string with placeholders for picw, pich, 
#...   picwgoal, pichgoal, and the image data


mfstream     = MemoryStream()
offscrDC     = Graphics.FromHwndInternal(IntPtr.Zero)
imgptr       = offscrDC.GetHdc()
mfile        = Imaging.Metafile(mfstream, imgptr, Imaging.EmfType.EmfOnly)
gfx          = Graphics.FromImage(mfile)
width,height = canv.size
pixels       = canv.load()
for x in range(width):
  for y in range(height):
    _r,_g,_b = pixels[x, y]
    c     = Color.FromArgb(_r, _g, _b)
    brush = SolidBrush(c)
    gfx.FillRectangle(brush, x, y, 1, 1)
gfx.Dispose()
offscrDC.ReleaseHdc()
_hEmf            = mfile.GetHenhmetafile()
GdipEmfToWmfBits = windll.gdiplus.GdipEmfToWmfBits
_bufferSize      = GdipEmfToWmfBits(
                      int(str(_hEmf)),
                      c_uint(0),
                      None,
                      c_int(8),           # MM_ANISOTROPIC
                      c_uint(0x00000000)) # Default flags
_buffer = c_int * _bufferSize
_buffer = _buffer(*[0 for x in range(_bufferSize)])
GdipEmfToWmfBits( int(str(_hEmf)),
                  c_uint(_bufferSize),
                  _buffer,
                  c_int(8),            # MM_ANISOTROPIC
                  c_uint(0x00000000) ) # Default flags
hmf = windll.gdi32.SetMetaFileBitsEx(c_uint(_bufferSize), _buffer)
windll.gdi32.CopyMetaFileA(int(str(hmf)), "temp.wmf")
windll.gdi32.DeleteMetaFile(int(str(hmf)))
windll.gdi32.DeleteEnhMetaFile(int(str(_hEmf)))
mfstream.Close()

imgstr = open("temp.wmf", 'rb')
imgstr = byte_to_hex(imgstr)
with open('script-out.rtf','wb') as outf:
  template = template % (str(_cx),str(_cy),str(15*_cx),str(15*_cy),imgstr)
  outf.write(template)
3
Darren Ringer

RTFボックスのほとんどが次の形式を使用していることがわかりました。

{\object\objemb{\*\objclass Paint.Picture}\objw2699\objh4799{\*\objdata [hex/bin]}}

いつ [hex/bin]は、画像形式を表す大量の16進文字列です。この方法は、Word rtfとRTF boxの両方で機能するため、より効率的です。

180x320ピクセルのサイズの私のコンピューターイメージでは、2699x4799 twipに変換されます。つまり、私が見る限り、1pix = 15 twipを意味します。これは、テストした3台のコンピューター間で、WinXP prof、WinXPの間です。ホームと勝利7。

1
Xerix

RRUZとJiffyWhipからの回答をマージし、コードをクリーンアップしました。

これで、画像をPNGまたはWMFとして挿入できます。

また、WMFは不透明度をサポートしていないため、透明度を白色に置き換えました。

private static string GetWidthAndHeight(Image image, float dpiX, float dpiY)
{
    float width = (float)image.Width / dpiX;
    float height = (float)image.Height / dpiY;

    int picw = (int)(width * 2540);
    int pich = (int)(height * 2540);

    int picwgoal = (int)(width * 1440);
    int pichgoal = (int)(height * 1440);

    return "\\picw" + picw + "\\pich" + pich + "\\picwgoal" + picwgoal + "\\pichgoal" + pichgoal;
}

public static string InsertPngImage(Image image)
{
    byte[] buffer;

    using (var stream = new MemoryStream())
    {
        image.Save(stream, ImageFormat.Png);
        buffer = stream.ToArray();
    }

    string hex = BitConverter.ToString(buffer, 0).Replace("-", string.Empty);

    string widthAndHeight = GetWidthAndHeight(image, image.HorizontalResolution, image.VerticalResolution);

    return "{\\pict\\pngblip" + widthAndHeight + " " + hex + "}";
}

[Flags]
enum EmfToWmfBitsFlags
{
    EmfToWmfBitsFlagsDefault = 0x00000000,
    EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
    EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
    EmfToWmfBitsFlagsNoXORClip = 0x00000004
}

private static int MM_ANISOTROPIC = 8;

[DllImportAttribute("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits(IntPtr hEmf, uint bufferSize, byte[] buffer, int mappingMode, EmfToWmfBitsFlags flags);

public static string InsertWmfImage(Image image)
{
    image = ReplaceTransparency(image, Color.White);

    Metafile metaFile;
    float dpiX;
    float dpiY;

    using (Graphics graphics = Graphics.FromImage(image))
    {
        IntPtr hdc = graphics.GetHdc();
        metaFile = new Metafile(hdc, EmfType.EmfOnly);
        graphics.ReleaseHdc(hdc);
    }

    using (Graphics graphics = Graphics.FromImage(metaFile))
    {
        graphics.DrawImage(image, 0, 0, image.Width, image.Height);
        dpiX = graphics.DpiX;
        dpiY = graphics.DpiY;
    }

    IntPtr hEmf = metaFile.GetHenhmetafile();

    uint bufferSize = GdipEmfToWmfBits(hEmf, 0, null, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);

    byte[] buffer = new byte[bufferSize];

    uint convertedSize = GdipEmfToWmfBits(hEmf, bufferSize, buffer, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);

    string hex = BitConverter.ToString(buffer, 0).Replace("-", string.Empty);

    string widthAndHeight = GetWidthAndHeight(image, dpiX, dpiY);

    return "{\\pict\\wmetafile8" + widthAndHeight + " " + hex + "}";
}

private static Image ReplaceTransparency(Image image, Color background)
{
    Bitmap bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);

    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
        graphics.Clear(background);
        graphics.CompositingMode = CompositingMode.SourceOver;
        graphics.DrawImage(image, 0, 0, image.Width, image.Height);
    }

    return bitmap;
}
0
Jinjinov