views:

483

answers:

1

How can I read a photo from an XMPP vcard (an avatar picture, which I think is in JPEG format) and display it in a Delphi TImage control?

The XMPP server sends this XML:

<presence id="e3T50-75" to="cvg@esx10-2022/spark" from="semra@esx10-2022" 
 type="unavailable">
  <x xmlns="vcard-temp:x:update">
    <photo>897ce4538a4568f2e3c4838c69a0d60870c4fa49</photo>
  </x>
  <x xmlns="jabber:x:avatar">
    <hash>897ce4538a4568f2e3c4838c69a0d60870c4fa49</hash>
  </x>
</presence>
+4  A: 

The XML you posted does not contain the picture. It contains the SHA-1 hash of the picture's contents. You only get the hash, initially, in case you have already fetched that image once before, so you can display the cached version instead of requesting it anew.

If you don't have an image with that hash, then request a new vcard. When it arrives, read the PHOTO element, if it's available. It may have two subelements, BINVAL and TYPE. BINVAL will contain the Base-64-encoded version of the image, and TYPE will contain the MIME type identifier for the image type, such as image/jpeg or image/png.

Decode the binary data and store it in a stream, such as TFileStream or TMemoryStream. Next, choose which TGraphic descendant is appropriate for the kind of image you have. It might be TPngImage, or it might be TBitmap. Instantiate the class, and tell it to load the stream's contents. It would go something like this:

function CreateGraphicFromVCardPhoto(const BinVal, MimeType: string): TGraphic;
var
  Stream: TStream;
  GraphicClass: TGraphicClass;
begin
  Stream := TMemoryStream.Create;
  try
    if not Base64Decode(BinVal, Stream) then
      raise EBase64Decode.Create;
    Stream.Position := 0;
    GraphicClass := ChooseGraphicClass(MimeType);
    Result := GraphicClass.Create;
    try
      Result.LoadFromStream(Stream);
    except
      Result.Free;
      raise;
    end;
  finally
    Stream.Free;
  end;
end;

The code above uses the Base64Decode function from OmniXML, described in the answer to Saving a Base64 string to disk as a binary using Delphi 2007. Once you have the TGraphic value, you can assign it to a TImage or do whatever else you can do with TGraphics.

The ChooseGraphicClass function might work like this:

function ChooseGraphicClass(const MimeType: string): TGraphicClass;
begin
  if MimeType = 'image/bmp' then
    Result := TBitmap
  else if MimeType = 'image/png' then
    Result := TPngImage
  else if MimeType = 'image/gif' then
    Result := TGifImage
  else if MimeType = 'image/jpeg' then
    Result := TJpegImage
  else
    raise EUnknownGraphicFormat.Create(MimeType);
end;
Rob Kennedy
Awesome answer, Rob!Suggestion: add an example vCard XML.
Joe Hildebrand
No can do. I've never seen such a thing. I assume that the people who need this code already know how to get a vcard. If they don't, they're free to ask a new question here.
Rob Kennedy
Thanks Rob Kenndy but i have get some error Jpeg error #53 implementation uses omnixmlutils; {$R *.dfm} ...function ChooseGraphicClass(const MimeType: string): TGraphicClass; ...function CreateGraphicFromVCardPhoto(const BinVal, MimeType: string): TGraphic; procedure TForm1.Button1Click(Sender: TObject); begin memo1.lines.loadfromfile('c:\cvg.bin'); image1.Picture.Graphic := CreateGraphicFromVCardPhoto(memo1.text,'image/jpeg'); end; { cvg.bin file is here http://www.anasel.com.tr/cvgxml.txt } { http://www.anasel.com.tr/cvg.bin}
cvg
Delphi's JPEG library can't handle all JPEG files. http://newsgroups.archived.at/borland/public.delphi.graphics/200608/0608162573.html
Rob Kennedy
Yes Robert, delphi cant handle all type jpeg. i use this procedure and memo1.lines.savetofile... savetodisk and see then right picture.procedure SaveBase64ToFile(const encoded, fileName: string);var fs: TFileStream;begin fs := TFileStream.Create(fileName, fmCreate); try if not Base64Decode(encoded, fs) then raise Exception.Create('Invalid data!'); finally FreeAndNil(fs); end;end;
cvg