解像度を変更したJpegをクリップボードから貼り付けるには?

解決


boon  2007-04-17 20:12:44  No: 25810

// GetAPP0_Offset 過去ログより引用

function GetAPP0_Offset(Ms: TMemoryStream): Integer;
var
  p1, p2: PByte;
begin
  result := -1;
  p1 := Ms.Memory;
  while (Integer(p1) - Integer(Ms.Memory)) < Ms.Size do
  begin
    if p1^=$FF then
    begin
      p2:=p1;
      Inc(p2);
      if p2^=$E0 then
      begin
        result := Integer(p1) - Integer(Ms.Memory);
        exit;
      end;
    end;
    Inc(p1);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Bmp: TBitmap;
  Jpg: TJpegImage;
  Ms: TMemoryStream;
  APPO_Offset: Integer;
  Units, Hidpi, Lodpi: Byte;
  dpi: WORD;
  ExcelFile, ExcelSheet: String;
  MyFormat: Word;
  AData: THandle;
  APalette: HPALETTE;
begin
  Jpg := TJpegImage.Create;
  try
    Bmp := TBitmap.Create;
    Ms := TMemoryStream.Create;
    try
      Bmp.Width := 2955;
      Bmp.Height := 1622;
      SetStretchBltMode(Bmp.Canvas.Handle, HALFTONE);
      StretchBlt(Bmp.Canvas.Handle, 219, 0, 2736, 1622,
        Image1.Canvas.Handle, 0, 0, Image1.Picture.Width, Image1.Picture.Height, SRCCOPY);
      Jpg.Assign(Bmp);
      Jpg.SaveToStream(Ms);

      APPO_Offset := GetAPP0_Offset(Ms);
      Ms.Position := APPO_Offset +2 +9;
      Units := 1;
      Ms.Write(Units, 1);

// 解像度を300に設定

      dpi := 300;

      Hidpi := HiByte(dpi);
      Lodpi := LoByte(dpi);
      Ms.Write(Hidpi, 1);
      Ms.Write(Lodpi, 1);
      Ms.Write(Hidpi, 1);
      Ms.Write(Lodpi, 1);
      Ms.Position:=0;

      Jpg.LoadFromStream(Ms);
    finally
      Bmp.Free;
      Ms.Free;
    end;

    ExcelFile := 'C:\Test.xls';
    ExcelSheet := 'Test';

    try
      ExcelApplication1.Connect;
      ExcelApplication1.Visible[0] := True;
      ExcelWorkbook1.ConnectTo(ExcelApplication1.Workbooks.Open
        (ExcelFile, null, null, null, null, null,
        null, null, null, null, null, null, null, null, null, 0));
      ExcelWorksheet1.ConnectTo(ExcelWorkbook1.Worksheets[ExcelSheet] as _Worksheet);
      try

// SaveToClipboardFormatではBitmap形式になり解像度が72dpiに戻る

        Jpg.SaveToClipboardFormat(MyFormat, AData, APalette);
        ClipBoard.SetAsHandle(MyFormat, AData);

// SaveToFileでは300dpiで出力できる

        Jpg.SaveToFile('d:\test.jpg');

        ExcelWorksheet1.Range['A1','A1'].Select;

// クリップボードからシートに画像貼り付け

        ExcelWorksheet1.Paste;

      except
        ExcelWorksheet1.Range['A1','A1'].Select;
      end;
      ExcelWorkbook1.SaveAs('c:\Test2.xls', Null, Null, Null, Null, Null,
        xlNoChange, Null, Null, Null, Null, Null, 0);
    finally
      ExcelApplication1.Disconnect;
    end;
  finally
    Jpg.Free;
  end;
end;

上記ソースで、Image1に表示された画像を拡大し、解像度を300に変更したJpegを
ExcelのSheetにクリップボード経由で貼り付けたいと思っています。
クリップボードを経由しなくてもいいんですが、それしか思いつきませんでした。

そこで、TJpegImage の SaveToClipboardFormat を使って見たんですが、
Bitmapに戻ってしまい、解像度も72dpiに戻ってしまいます。

SaveToFile を使うと300dpiのJpegで保存出来るので、SaveToClipboardFormat
を使っては無理なんだと勝手に思っています。

なにか良い方法はないでしょうか?
よろしくお願いします。

環境は、Delphi7 + Excel2003 です。


とおりすがり  2007-04-17 21:59:10  No: 25811

拡大縮小  と  dpiの変更  との間には直接の関係はないんじゃないですか???私はそう思っていましたが。

過去ログでもjokさんは
>これは出力デバイスというかソフトへの指示になりますので画像そのものには変化ありません。
と書いていますし・・・


boon  2007-04-17 23:07:56  No: 25812

とおりすがりさん
こんにちは。

確かに拡大縮小とdpi変更との間には直接関係はないと思います。
その部分は特に問題はないのですが、欲しい結果は「300dpiの画像をExcelの
シートに貼り付ける」という事なのです。

同じサイズの画像の72dpiの物と、300dpiの物をExcelに貼り付けると、
当然300dpiの物の方が小さく表示されてしまいます。
それを防ぐために拡大しているわけです。

Excel上で300dpiの画像をコピーすると、貼り付けた画像も300dpiなので、
結局の所クリップボードの使い方の問題なんじゃなかと思っています。


とおりすがり  2007-04-18 00:09:12  No: 25813

EXCELで  挿入->図->ファイルから  で挿入すると確かにdpiによって大きさが変わりますね・・知らなかった。

しかしこれはクリップボードとは何の関係もないわけで・・

「挿入->図->ファイルから」の操作をクリップボード経由の貼り付けでやろうとしたってできません。(と思います)

クリップボード上にjpegデータとして存在するわけじゃあないでしょうし。


boon  2007-04-18 00:39:14  No: 25814

挿入->図->ファイルから で挿入するとdpiによって大きさが変わります。
印刷時も高画質で印刷できます。

Excel上でその挿入した画像をコピーすると、クリップボード経由でも解像度を保持したまま貼り付け出来るので、出来ない事はないと思うんですが、どうなんでしょう?

ちなみにExcel上でコピーした画像は、Jpegデータかどうかは調べていなんで分かりませんが、Jpegとして保持されると思います。(違うかも・・・)
Jpeg や Gif や Png や メタファイルとして変換して貼り付ける事も出来ます。

まぁクリップボードにこだわる事もないんですが、それ以外にExcelにデータ渡す方法が分からないんですよね・・・


とおりすがり  2007-04-18 01:08:20  No: 25815

やりたいことをEXCELマクロで記録してそれを試行錯誤したら大抵動きますよ。

私のBDS2006ProではDelphiプロジェクトだと何故だかofficeコンポーネントが無い(!)ので、試せませんが、
挿入->図->ファイルから  のマクロの中身は

ActiveSheet.Pictures.Insert ("適当ファイル72DPI.jpg")

でした。
上の記述に即するなら、

ExcelWorksheet1.Pictures.Insert ('適当ファイル72DPI.jpg');

てな感じでしょうか・・・(試せませんが)
これで試行錯誤してみてください(無責任)

dpiを変更してセーブし、そのファイルを上述の方法でインサートすればできませんか?


boon  2007-04-18 01:20:14  No: 25816

とおりすがりさん

上記方法は私も考えましたが、Pictures は使えるのですが、Insertメソッドが使えません・・・というかありません・・・

バージョンの問題なのかもしれませんね。

もう少し試行錯誤してみます。
ありがとうございました。


うんと  2007-04-18 04:47:23  No: 25817

> Jpegとして保持されると思います。

Windows は SDK レベルで Jpeg を認識しません。ビットマップになると思います。


boon  2007-04-18 18:09:10  No: 25818

うんとさん

なるほど。
という事は、クリップボード上ではビットマップとして持つことしか出来ないと言う事ですね。
なるほど。なるほど。

まぁビットマップではまずいですが、Jpegでなくてもいいので 300dpi さえ確保出来ればいいんですけどね。

_Worksheet.PasteSpecial メソッド っていうのを見つけたんで、これでなんとか出来ないかなと考え中です。

http://msdn2.microsoft.com/ja-jp/library/microsoft.office.interop.excel._worksheet.pastespecial(vs.80).aspx


boon  2007-04-18 18:16:27  No: 25819

PasteSpecial では無理ですね・・・


とおりすがり  2007-04-18 20:20:35  No: 25820

実は解像度なんてものを変更せずとも、挿入した画像のスケールを変更するだけでよいと思う。
でもまあ、今までの流れに沿ってやってみました。

ちなみに私のBDS2006ProではOfficeコンポが無い(何故かな・・?)ので  ComObjでやります。

実は便乗?質問したいです。
私のBDS2006ProのDelphiプロジェクトではOfficeコンポが無いんですけど、どうすれば・・?

※Uses  に  comobj  を追加すること

procedure TForm1.Button1Click(Sender: TObject);
var
  E_Excel       : Variant;
  E_Application : Variant;
  E_WorkBook    : Variant;
  E_WorkSheet   : Variant;
begin
// Excel をオープン
  try
  E_Excel               := CreateOleObject('Excel.Application');
  E_Application         := E_Excel.Application;
  except
      on EOleSysError do begin
         //起動失敗
         ShowMessage('Excelが起動できません');
         E_Excel := Null;
         Exit;
      end;
  end;
  E_Application.Visible := True;
  E_Application.WorkBooks.Open( ExtractFilePath(Application.ExeName) +  'MyData.xls');
  E_WorkBook            := E_Application.ActiveWorkbook;
  E_WorkSheet           := E_Application.ActiveSheet;

  // セルA1に画像を挿入
  E_Application.Range['A1:A1'].Select;
  E_WorkSheet.Pictures.Insert('c:\自宅72DPI.jpg').select;

  //ここで画像を自由に伸縮(水平垂直半分)
  E_WorkSheet.Pictures[1].ShapeRange.ScaleWidth(0.5,0,0);
  E_WorkSheet.Pictures[1].ShapeRange.ScaleHeight(0.5,0,0);

  E_Excel     := unAssigned;
  E_WorkBook  := Unassigned;
  E_WorkSheet := Unassigned;

end;

参考
https://www.petitmonte.com/bbs/answers?question_id=3651


boon  2007-04-18 20:52:11  No: 25821

var
  Xlsheet: Variant;

Jpg.SaveToFile('c:\test.jpg');

Xlsheet := ExcelWorkbook1.ActiveSheet;
Xlsheet.Pictures.Insert('c:\test.jpg');

TExcelWorksheetでは、Insertメソッドをサポートしていないようで、
上記のようにする事で解決いたしました。

ちょっとタイトルとは主旨がが違ってしまいましたが、自己解決しました。
ありがとうございました。


boon  2007-04-18 21:14:58  No: 25822

とおりすがりさん

わざわざソースありがとうございます。
結局のところ ComObj 使った方が良いようですね。

Officeコンポはイマイチのようで・・・w
使い方がイマイチなのかなぁ〜w


boon  2007-04-18 21:16:18  No: 25823

解決押すの忘れてました(爆)


boon  2007-04-19 02:44:26  No: 25824

とおりすがりさん

BDS2006ProのOfficeコンポの件ですけど、私はBDS持っていないのでなんとも言えないのですが、下のリンクを見る限りでは、インストール時にC++用かDelphi用のどちらかしか選べないのではないかと思います。
参考になるかどうか・・・

http://homepage2.nifty.com/Mr_XRAY/Delphi/CompoInstall/BDS2006Install.htm


とおりすがり  2007-04-19 21:59:15  No: 25825

ありがとうございます。
そういえばそんなダイアログが出ていました。もう1年前になるのですっかり失念していました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加