Mittwoch, 3. März 2010

Erkennen von Bildformaten / Recognition of image formats

Seit vielen Jahren arbeite ich bereits mit ACDSee. Die Version 5, welche meine Version 3 ablöste, lag damals einer meiner PC Zeitschriften als Vollversion bei. Sie kommt noch immer regelmäßig zum Einsatz wenn es darum geht Grafiken/Fotos zu verwalten oder auch zu konvertieren.

Was mich anfänglich erstaunte war die Fähigkeit dieser Applikation einen Grafikformattyp auch dann noch erkennen zu können, wenn das falsche oder auch gar kein Suffix vorhanden war. Bekannterweise weigert sich Windows beim Entfernen einer Dateinamenserweiterung eine Applikations- oder Dateitypzuordnung durchzuführen. Wieso also macht ACDSee was Windows nicht kann.

Die Antwort ist ganz einfach: Jedes Bildformat verfügt über eindeutige Zeichenfolgen, die eine nachträgliche Zuordnung/Erkennung ermöglichen. Hierzu ist jedoch jede einzelne Datei zu öffnen und gezielt zu verarbeiten. In den meisten Fällen befinden sich diese Kennungen direkt am Dateianfang. Die einzelnen Formate unterscheiden sich letztlich nur darin, ob spezielle Bytefolgen oder klartextliche Kennungen zum Einsatz kommen. Im heutigen Blogeintrag zeige ich, mit welch einfachen Mitteln wir aus einer Datei ihren Bildformattyp auslesen können. Der Mustercode ist natürlich nicht allumfassend, dafür gibt es einfach zu viele Bildformate.

Ein Hinweis noch zum PCX Format das bereits zu DOS Zeiten von der Firma ZSoft entwickelt wurde. Die drei Bytes welche eine Erkennung ermöglichen, lassen sich bspw. auch mit Hilfe der INLIST() Funktion herausfinden. Ich habe mich jedoch dazu entschlossen, die Überprüfung auf Basis eines regulären Ausdrucks durchzuführen. Die daraus entstandenen Funktionen lassen sich in der Folge einfacher erweitern als dies bei komplizierten INLIST Konstrukten der Fall wäre. Gleiches gilt im Übrigen für das TGA Format.

lcPic = GETFILE()
IF FILE(lcPic)
    CLEAR
    * // Übergabe als Dateiname           
    ?GetGraphicsFormat(lcPic)
    * // Übergabe als Datenstrom       
    ?GetGraphicsFormat(FILETOSTR(lcPic))
ENDIF

FUNCTION GetGraphicsFormat as String
LPARAMETERS vData as String
* // Übergabe eines Bildes wahlweise als Datenstrom oder als Dateiname   
* // Derzeit erkannte Formate:                                           
* // JPG, GIF, EMF, WMF, TIF, PNG, BMP, SWF, PDF, PCX, TGA                   
    LOCAL lcReturn as String
    lcReturn = [---]

    * // Wenn kein Datenstrom sondern ein Dateiname übergeben wurde,    
    * // dann lesen wir diesen noch schnell in eine Variable ein       
    IF FILE(vData)
        vData = FILETOSTR(vData)
    ENDIF

    IF !EMPTY(vData)
        DO CASE
        CASE LEN(vData) < 4
            lcReturn = [---]
        * // Joint Pictures Experts Group Format                       
        * // JPGs erkennbar an den ersten drei Bytes: ÿØÿ               
        CASE LEFT(vData,3) = CHR(0xFF) + CHR(0xD8) + CHR(0xFF)
            lcReturn = [JPG]
        * // Graphics Interchange Format                               
        * // GIFs erkennbar an den ersten drei Bytes: GIF               
        CASE LEFT(vData,3) = [GIF]
            lcReturn = [GIF]
        * // Enhanced Windows Metafile                                   
        * // EMFs erkennbar an Bytes 42-44: EMF                           
        CASE SUBSTR(vData,42,3)= [EMF]
            lcReturn = [EMF]
        * // Windows Metafile Format                                   
        * // WMFs erkennbar an den ersten vier Bytes: ×ÍÆš               
        CASE LEFT(vData,4) = CHR(0xD7) + CHR(0xCD) + CHR(0xC6) + CHR(0x9A)
            lcReturn = [WMF]
        * // Tagged Image File Format                                   
        * // TIFFs erkennbat an den ersten vier Bytes: MM *               
        CASE LEFT(vData,4) = CHR(0x4D) + CHR(0x4D) + CHR(0x00) + CHR(0x2A)
            lcReturn = [TIF]
        * // Portable Network Graphics Format                           
        * // PNGs erkennbar an den ersten vier Bytes: ‰PNG               
        CASE LEFT(vData,4) = CHR(0x89) + [PNG]
            lcReturn = [PNG]
        * // Windows Bitmap Graphic Format                               
        * // BMPs erkennbar an den ersten zwei Bytes: BM               
        CASE LEFT(vData,2) = [BM]
            lcReturn = [BMP]
        * // Komprimierte Shockwave/Flash Datei                           
        * // SWFs erkennbar an den ersten vier Bytes: CWS?               
        CASE LEFT(vData,3) = [CWS] AND ASC(SUBSTR(lcContents,4,1)) < 16
            lcReturn = [SWF]
        * // Unkomprimierte Shockwave/Flash Datei                       
        * // SWFs erkennbar an den ersten vier Bytes: FWS?               
        CASE LEFT(vData,3) = [FWS] AND ASC(SUBSTR(lcContents,4,1)) < 16
            lcReturn = [SWF]
        * // Adobe Portable Document File                               
        * // PDFs erkennbar an den ersten vier Bytes: %PDF               
        CASE LEFT(vData,4) = CHR(0x37) + [PDF]
            lcReturn = [PDF]
        * // Picture Exchange Format                                    
        * // PCX erkennbar an den ersten drei Bytes beginnend mit 0x0A   
        CASE IsPCX(LEFT(vData,3))
            lcReturn = [PCX]
        * // TrueVision Targa Format                                    
        * // TGA erkennbar an Bytes 2 und 3                               
        CASE IsTGA(SUBSTR(vData,2,2))
            lcReturn = [TGA]
        ENDCASE

    ENDIF
    RETURN lcReturn
ENDFUNC

FUNCTION IsPCX as Boolean
LPARAMETERS vStream as String
    * // Zusammenstellung des Abfragemusters.                       
    * // Byte        Definition    ASCII Code + Beschreibung           
    * // 1            Ersteller    10 = ZSoft .pcx (Konstante)           
    * // 2            Version         0 = Version 2.5 of PC Paintbrush   
    * //                         2 = Version 2.8 mit Paletteninfo   
    * //                         3 = Version 2.8 ohne Paletteninfo   
    * //                         4 = PC Paintbrush for Windows       
    * //                         5 = Version 3.0 und größer von       
    * //                           - PC Paintbrush                   
    * //                           - PC Paintbrush +               
    * //                           - Publisher's Paintbrush           
    * //                           - inkl. 24-bit .PCX Dateien       
    * // 3            Encoding     0 = ohne Verschlüsselung           
    * //                         1 = mit  Verschlüsselung           
    lcPattern    = "^[" + CHR(10) + "]" ;
                + "[" + CHR(0) + CHR(2) + CHR(3) + CHR(4) + CHR(5) + "]" ;
                + "[" + CHR(0) + CHR(1) + "]$"
    RETURN CheckPattern(vStream, lcPattern)
ENDFUNC

FUNCTION IsTGA as Boolean
LPARAMETERS vStream as String
    * // Zusammenstellung des Abfragemusters.                       
    * // Byte        Definition    ASCII Code + Beschreibung           
    * // 1            Farbkarte    0 o. 1                                
    * // 2            Bildtyp         0 = keine Bilddaten enthalten       
    * //                         1 = unkomprimiert, Farbkarte       
    * //                         2 = unkomprimiert, RGB               
    * //                         3 = unkomprimiert, schwarz/weiss   
    * //                         9 = verschlüsselte Farbkarte       
    * //                        10 = verschlüsselte RGB               
    * //                        11 = komprimiert, s/w               
    * //                        32 = komprimiert, farbig, verschl.   
    * //                        33 = komprimiert, farbig, verschl.2   
    lcPattern    = "^[" + CHR(0) + CHR(1) + "]" ;
                + "[" + CHR(0) + CHR(1) + CHR(2) + CHR(3) + CHR(9) + CHR(10) + CHR(11) + CHR(32) + CHR(33) + "]$"       
    RETURN CheckPattern(vStream, lcPattern)
ENDFUNC

FUNCTION CheckPattern as Boolean
LPARAMETERS vString as String, vPattern as String
    LOCAL liReturn as Integer
    * // Bedarfweise Deklaration der Klassenbibliothek für Reguläre Ausdrücke
    IF AT(UPPER(HOME()+ [ffc\_regexp.vcx]),SET([Classlib])) = 0
        SET CLASSLIB TO HOME()+ [ffc\_regexp.vcx] ADDITIVE
    ENDIF
    * // Objekt erzeugen, initialisieren, Muster zuweisen und abarbeiten   
    oexp = CREATEOBJECT([_regexp])
    oexp.clear
    oexp.pattern = vPattern
    liReturn = oexp.execute(vString)
    RETURN CAST(liReturn as L)
ENDFUNC

Keine Kommentare:

Kommentar veröffentlichen