Montag, 15. März 2010

Flexible Prüfziffernberechnung mit Modulo und Gewichtung / Flexible check digit calculation with modulo and weighting

Prüfziffernberechnungen gibt es wie Sand am Meer. Speziell im Bereich Barcoding sind sie in den verschiedensten Varianten vorzufinden. Egal ob Code 2aus5il, Code128 oder EAN128. Sie alle verfügen über Prüfziffern.

Die Unterschiede liegen bei fast allen diesen Varianten bei der Wertigkeit der Multiplikatoren und dem Modulowert. Beginnt bei einem EAN-13 die Multiplikation mit 1 und wechselt sich danach mit 3 ab so arbeitet die Berechnung für EAN-14 genau umgekehrt. Beim EAN-128 hingegen liegt der Unterschied zum EAN-13 im Modulowert. Anstelle von 10 wird hier mit 103 gearbeitet. Die Berechnung des Code128 wiederum beruht auf den selben Multiplikatoren wie beim EAN128, arbeitet jedoch wie der EAN13 mit einem Modulowort von 10.

Am Beispiel einer Code128 Nummer sähe die Berechnung wie folgt aus:    
EAN-Nummer        8 154711 00011                                        
Ziffern           8  1  5  4  7  1  1  0  0  0  1  1                    
Multiplikation    3  1  3  1  3  1  3  1  3  1  3  1                    
Ergebnis          24 1  15 4  21 1  3  0  0  0  3  1 Summe: 73        
Division durch 10 ergibt einen Restwert von 3                        
Subtrahiert von 10 erhalten wir als Prüfziffer die 7                

Um nun mit verschiedenen Multiplikatoren und Modulowerten arbeiten zu können benötigen wir zwei Funktionen. Die Erste beinhaltet die eigentliche Berechnung welche wiederum als erstes die zweite Funktion aufruft, in welcher die Wertezuweisung für die Multiplikatoren und den Modulowert vorgenommen werden.

Diese zweite Funktion kann natürlich anstatt als fixe Funktion auch als Cursor, basierend auf einer XML Datei oder einer Tabelle, verfügbar gemacht werden.

* // Funktionstest
CLEAR
?GetCheckDigit([815471100011],[EAN-13])
?GetCheckDigit([815471100012],[EAN-14])
?GetCheckDigit([815471100013],[Code25])
?GetCheckDigit([815471100014],[Code128])
?GetCheckDigit([815471100015],[Leitcode])

FUNCTION GetCheckDigit as String
LPARAMETERS pcString as String, pcVariante as String

    LOCAL    lcReturn as String, i as Integer, liQuersumme as Integer, ;
            liMP1 as Integer, liMP2 as Integer, liModulo as Integer
    STORE [] TO lcReturn
    STORE 0 TO liQuersumme, liMP1, liMP2, liModulo
    
    IF GetCheckDigitValues(pcVariante,@liMP1,@liMP2,@liModulo)

        FOR i = 1 TO LEN(pcString)
            * // Bei ungeraden Position mit liMP1 multiplizieren, andern-    
            * // falls nur mit liMP2 (=Beibehaltung des Originalwertes)      
            liQuersumme = liQuersumme + (INT(VAL(SUBSTR(pcString,i,1)) * IIF(MOD(i,2) > 0,liMP1,liMP2)))
        ENDFOR 

        * // Die berechnete Quersumme nun mit Modulo auf den         
        * // Restwert reduzieren                                    
        lcReturn = CAST(IIF(10 - MOD(liQuersumme, liModulo) = liModulo,0,liModulo - MOD(liQuersumme, liModulo)) as C(1))
    ENDIF  
    
    RETURN lcReturn
    
ENDFUNC 

FUNCTION GetCheckDigitValues as Boolean
LPARAMETERS pcVariante as String, riMP1 as Integer, riMP2 as Integer, riModulo as Integer
    LOCAL llReturn as Boolean
    lLReturn = .T.
    pcVariante = UPPER(pcVariante)
    DO CASE 
    CASE pcVariante = [EAN-13]
        riMP1        = 1
        riMP2        = 3
        riModulo    = 10
    CASE INLIST(pcVariante,[CODE25],[CODE128],[EAN-14],[ITF-14],[SCC-14],[DUN-14])
        riMP1        = 3
        riMP2        = 1
        riModulo    = 10
    CASE INLIST(pcVariante,[EAN-128],[GS1-128],[UCC-128])
        riMP1        = 1
        riMP2        = 3
        riModulo    = 103
    CASE pcVariante = [LEITCODE]
        riMP1        = 4
        riMP2        = 9
        riModulo    = 10
    OTHERWISE 
        llReturn = .F.
    ENDCASE 
    RETURN llReturn 
ENDFUNC 

Umstellung meines Blog von 'br' auf 'cr' / Blog conversion from 'br' to 'cr'

Letzte Woche habe ich meine Blog Konfiguration umgestellt, damit der von mir bereitgestellte Code zukünftig problemlos über die Zwischenablage ins VFP Codefenster übernommen werden kann. Blogger setzt nun nicht mehr < br > als Zeilenumbruch ein, sondern arbeitet mit normalen Carriage Returns.

Diese Umstellung sorgte jedoch dafür, dass in ca. 80% der Blogeinträge die Formatierung der normalen Texte zerstört wurde. Mittlerweile habe ich über die Hälfte der betroffenen Einträge nachbearbeitet. Die Jahrgänge 2007 und 2008 fehlen jedoch noch.

Sollte also ein älterer Beitrag als 'absatzloser Wirrwarr' angezeigt werden, dann wird sich dies im Laufe dieser Woche wieder ändern.

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