Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Sichere Erkennung von PNG bzw. JPG? (https://www.delphipraxis.net/210191-sichere-erkennung-von-png-bzw-jpg.html)

Frickler 15. Mär 2022 17:32

Sichere Erkennung von PNG bzw. JPG?
 
Ich treffe immer wieder auf Bilddateien, die nicht dem Typ der Dateierweiterung entsprechen. Also Bild ist ein JPEG, heißt aber "Bild.PNG" oder Bild ist ein PNG, heißt aber "Bild.JPG". Programme wie IrfanView machen mich darauf aufmerksam und bieten an, die Datei passend umzubenennen. Wenn ich selbst versuche, die Datei per TPNGImage/TJPEGImage zu laden, wirfts mir eine Exception. Gibt es eine fertige Funktion irgendwo in den Tiefen der Delphi Bibliothek, die die Unterscheidung (nach dem Dateianfang) machen kann oder muss ich da selbst ran?

Bernhard Geyer 15. Mär 2022 17:55

AW: Sichere Erkennung von PNG bzw. JPG?
 
"magic bytes" ist das Schlagwort.
Hier eine Liste der Signaturen von bekannten Dateitypen
https://en.wikipedia.org/wiki/List_of_file_signatures

Der Delphi-Ladeprozess geht über den Dateinamen. Du musst also entweder selbst die Datei in einen Memory-Stream zuerst laden oder über eine Temporäre Datei mit richtiger Extension arbeiten.

Redeemer 15. Mär 2022 18:13

AW: Sichere Erkennung von PNG bzw. JPG?
 
In neueren Delphis haben die Grafikklassen das glaube ich selbst.

Ansonsten:
Delphi-Quellcode:
type TImageFormat = (unknown, BMP, GIF, JPG, PNG);

function GetImageFormat(Stream: TStream): TImageFormat;
var
  Memory: array[0..8] of Byte;
begin
  Result := unknown;

  try
    if Stream.Size < 10 then
    Exit;

    Stream.Position := 0;
    Stream.Read(Memory, 9);
    case Memory[0] of
      66: if Memory[1] = 77 then // BM: Bitmap
          Result := BMP;
      137: if Memory[1] = 80 then // &#8240;PNG: PNG
           if Memory[2] = 78 then
           if Memory[3] = 71 then
           if Memory[4] = 13 then
           if Memory[5] = 10 then
           if Memory[6] = 26 then
           if Memory[7] = 10 then
           Result := PNG;
      71: if Memory[1] = 73 then // GIF8...a
          if Memory[2] = 70 then
          if Memory[3] = 56 then
          if Memory[5] = 97 then
          case Memory[4] of
            55: Result := GIF; // 7: GIF, old
            57: Result := GIF; // 9: GIF, new
          end;
      255: if Memory[1] = 216 then // ÿØ: JPEG
           Result := JPG;
    end;
  except
  end;
  Stream.Position := 0;
end;

function LoadImageFromStream(Target: TPersistent; Stream: TStream): Boolean;
var
  GraphicClass: TGraphicClass;
  Graphic: TGraphic;
begin
  Result := False;
  case GetImageFormat(Stream) of
    BMP: GraphicClass := Graphics.TBitmap;
    PNG: GraphicClass := TPNGImage;
    JPG: GraphicClass := TJPEGImage;
    GIF: GraphicClass := TGIFImage;
    else raise EInvalidGraphic.CreateFmt(SInvalidImage, []);
  end;
  if not (GraphicClass = nil) then
  begin
    Graphic := GraphicClass.Create;
    try
      Graphic.LoadFromStream(Stream);
      Target.Assign(Graphic);
      Result := True;
    finally
      Graphic.Free;
    end;
  end;
end;

Gausi 15. Mär 2022 18:21

AW: Sichere Erkennung von PNG bzw. JPG?
 
Bei den neueren Delphi-Versionen gibt es das TWICImage (Unit VCL.Graphics.pas). Das macht bei LoadfromFile() keinen Abgleich über die Dateiendung, sondern geht über den Dateiinhalt und lädt automatisch den passenden Decoder.

Delphi-Quellcode:
WicImage := TWICImage.Create
WicImage.LoadFromFile('EinPNGBild.jpg);
wird dann z.B. korrekt geladen, auch wenn die Dateiendung nicht passt.

Ansonsten halt selber machen, siehe oben. ;-)

completestranger 16. Mär 2022 07:29

AW: Sichere Erkennung von PNG bzw. JPG?
 
Ich mach das immer so:

Delphi-Quellcode:
const
  jpg: array[0..2] of byte = ($FF, $D8, $FF);
  png: array[0..2] of byte = ($89, $50, $4E);
  gif: array[0..2] of byte = ($47, $49, $46);

...

var
  buf: array [0..2] of byte;
  ...
begin
  ...
  stream.Seek(0, soFromBeginning); // stream.Position := 0;
  stream.Read(buf, SizeOf(buf));
  if (stream.Size > 0) then begin
    if CompareMem(@buf, @jpg, SizeOf(buf)) then begin
      bildtyp := btJPEG // JPG
    end else if CompareMem(@buf, @png, SizeOf(buf)) then begin
      bildtyp := btPNG // PNG
    end else if CompareMem(@buf, @gif, SizeOf(buf)) then begin
      bildtyp := btGIF // GIF
    end else
      result := false;
  end;
  stream.Seek(0, soFromBeginning);
  ...

Frickler 17. Mär 2022 12:26

AW: Sichere Erkennung von PNG bzw. JPG?
 
Danke an alle, das hilft mir weiter.

TSchnuckenbock 17. Mär 2022 13:43

AW: Sichere Erkennung von PNG bzw. JPG?
 
Ich hatte das Thema "Magic Numbers" und auch File-Handling mit Streams seit Jahren auf dem Zettel.
Die obigen Lösungsvorschläge waren für mich also ein gegebener Anlass, mich damit mal etwas intensiver zu befassen und hab' mir kurzerhand jeweils ein Beispielprojekt zusammengebaut. Beides läuft.

...man muß also manchmal nur in seinem Leben abwarten können ;-)

completestranger 17. Mär 2022 14:35

AW: Sichere Erkennung von PNG bzw. JPG?
 
vllt noch so als tipp: häufige stolperfalle, stream vergessen zurückzusetzen


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:04 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz