/*******************************************************
***       Bildverarbeitungs- und OCR-Routinen        *** 
***                                                  ***
*******************************************************/

/** includes **/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include "ocr.h"
#include "baum.h"
#include "semantik.h"
#include "main.h"

/** defines **/
#define MAXDIFFERENZ 64
#define Segment_X 128
#define Segment_Y 180
#define Segment_Bytes 16
#define MAXSTACK 30000
#define MAXWINKEL 0.2094395102

#define MAXANZAHL 60

/************ Globale Variablen  *****************/
extern short int Wsa[];
extern long Quadrat[];
extern struct Parameter ParaAktuell;
 
struct LearndListe *ZeichenListeStart[35][16][16];
                                               
unsigned char Bits[8]        = {128, 64, 32, 16,  8,  4,  2,  1};
unsigned char RandRechts[8]  = {  1,  3,  7, 15, 31, 63,127,254};
unsigned char RandLinks[8]   = {128,192,224,240,248,252,254,255}; 

struct BitBlockListe  *BitBlockListeStartPtr        = NULL;
struct BitBlockListe  *SegmentBitBlockListeStartPtr = NULL;
struct BitBlockListe  *KopierePtr                   = NULL;

long Page_x,Page_y,Page_Bytes_pro_Zeile;
long Max_x,Min_x,Min_y,Max_y;
long maxcount,rek_x,rek_y,xx,yy,offset_x,offset_y;

unsigned char *OrginalBildPuffer,*ArbeitsPuffer,*HilfsPtr;
unsigned char Value,Value1;
unsigned char *rek_y_zeile;

unsigned short int PunkteInByte[256];
unsigned short int ByteIstGespiegelt[256];
 
float faktorx,faktory;
long durch_dx, durch_dy;

/******** Sonstiges/Deklarationen  *************/

void SucheBitBloecke();
void SucheSegmenteBitBloecke();

/***************************************************************
Procedure        : void Spiegle()

Uebergabe        : short int
Rueckgabe        : short int
Zugriffe/Global  : Keine
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:  
         Es soll der Eingabeparameter (8-Bit) einfach gespiegelt
         werden. Die muss nicht effizient geloest sein !          
         
****************************************************************
   Arithmetic is being able to count up to twenty without 
   taking off your shoes. 
****************************************************************/

unsigned short int Spiegle(wert)
unsigned long wert;
{
  long l,r,t;
  unsigned short int wert_1;
  l=128;
  r=1;
  wert_1=0; 
for (t=0;t<4;t++) 
{
 if (l & wert) wert_1 = wert_1 | r;
 if (r & wert) wert_1 = wert_1 | l;
 l=l>>1;
 r=r<<1;
}
return(wert_1);
}


/***************************************************************
Procedure        : void ZaehlePunkte()

Uebergabe        : short int
Rueckgabe        : short int
Zugriffe/Global  : Keine
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:  
         Es sollen alle im Eingabeparameter (8-Bit) gesetzten Bits 
         gezaehlt werden. Die muss nicht effizient geloest sein !          
         
*****************************************************************/
/* Remember:  Silly is a state of Mind, Stupid is a way of Life.*/
/****************************************************************/
unsigned short int ZaehlePunkte(wert)
unsigned long wert;
{
 unsigned long l,t;
 unsigned char wert_1;
 l=1;
 wert_1=0; 
 for (t=0;t<8;t++) 
 {
  if (l & wert) wert_1++;
  l=l<<1;
 }
 return(wert_1);
}


/***************************************************************
Procedure        : MemoryError()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Autor            : Martin Bauer
Letzte Aenderung : 3.6.1995

Getestet         : JA , Resultat: fehlerfrei 
                   

Aufgabe:    
            Bei zuwenig Speicher soll ein Fehler ausgegeben werden.
****************************************************************/
void MemoryError()
{
 /* "Heisenberg may have slept here" */
 fprintf(stderr,"        -------> FEHLER <-------           \n");
 fprintf(stderr,"                                           \n");
 fprintf(stderr,"- Das System hat den angefordeten Speicher \n");
 fprintf(stderr,"      nicht zur verfuegung gestellt !      \n");
 fprintf(stderr,"                                           \n");
 fprintf(stderr,"- Behebung des Fehlers:                    \n");
 fprintf(stderr,"                                           \n");
 fprintf(stderr,"  1. Swapspace erhoehen                    \n");
 fprintf(stderr,"  2. Mehr Arbeitsspeicher kaufen           \n");
 fprintf(stderr,"  3. Andere Applikationen schliessen       \n");
 fprintf(stderr,"                                           \n");
 fflush(stderr);
 exit(-1); 
}


/***************************************************************
Procedure        : OcrInit()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Autor            : Martin Bauer
Letzte Aenderung : 3.6.1995

Getestet         : JA , Resultat: fehlerfrei 
                   

Aufgabe:    
            Es werden diverse Initialisierungen und Berechnungen
            vorgenommen .
****************************************************************/
void LadeGelernteZeichen();

void OcrInit()
{
 unsigned long t,t1,t2;
 for (t=0;t<256;t++)
 {
  PunkteInByte[t]=ZaehlePunkte(t);
  ByteIstGespiegelt[t]=Spiegle(t);
 } 
 /* init der Zeichenliste */
 for (t=0;t<35;t++) for (t1=0;t1<16;t1++) for (t2=0;t2<16;t2++) ZeichenListeStart[t][t1][t2]=NULL;
 LadeGelernteZeichen();
}


/***************************************************************
Procedure        : void LoescheBildFeld(x,y,dx,dy)

Uebergabe        : Kordinaten x,y,dx,dy
Rueckgabe        : Nichts
Zugriffe/Global  : OrginalBildPuffer,Page_Bytes_Pro_Zeile
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei
                   

Aufgabe:    
           Es soll in dem Bild ein Bereich beginnend bei
           x,y mit der Groesse dx,dy geloescht werden.
      
****************************************************************/
void LoescheBildFeld(x,y,dx,dy)
long x,y,dx,dy;
{
 char *P;
 long xx,yy,x1,y1,beginx,endex;
 x--;
 dx++; 
 xx=x+dx;
 yy=y+dy+1;
 if (xx>=Page_x) xx=Page_x-1;
 if (yy>=Page_y) yy=Page_y-1;
 

 endex=(xx>>3);
 beginx=(x>>3);
 
 for (y1=y;y1<yy;y1++)
 {
  P=OrginalBildPuffer+Page_Bytes_pro_Zeile*y1+beginx;
  *P=*P & RandLinks[x&7];
  P++;
  for (x1=beginx;x1<endex;x1++)
  {
   *P=0; P++;  
  } 
  *P=*P & RandRechts[xx&7];
 }
}

/***************************************************************
Procedure        : void SetzeBildFeld(x,y,dx,dy)

Uebergabe        : Kordinaten x,y,dx,dy
Rueckgabe        : Nichts
Zugriffe/Global  : OrginalBildPuffer,Page_Bytes_Pro_Zeile
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei
                   

Aufgabe:    
           Es soll in dem Bild ein Bereich beginnend bei
           x,y mit der Groesse dx,dy gesetzt werden.
      
****************************************************************/
void SetzeBildFeld(Puffer,max,x,y,dx,dy)
unsigned char *Puffer;
long max,x,y,dx,dy;
{
 unsigned char *P;
 long xx,yy,x1,y1,beginx,endex; 
 xx=(--x)+(++dx);
 yy=y+dy+1;
 if (xx>=Page_x) xx=Page_x-1;
 if (yy>=Page_y) yy=Page_y-1;
 

 endex=(xx>>3);
 beginx=(x>>3);
 x &= 7;
 xx &= 7;
 for (y1=y;y1<yy;y1++)
 {
  P=Puffer+max*y1+beginx;
  *P++ |= ~RandLinks[x];
  for (x1=beginx;x1<endex;x1++) *P++=255;  
  *P |= ~RandRechts[xx];
 }
}


/***************************************************************
Procedure        : void KopiereRekursiv

Uebergabe        : Nichts
Rueckgabe        : Nichts
Zugriffe/Global  : maxcount,offset_x,offset_y, y_zeile,faktory,
                   KopierePtr,Page_y,Page_x,Value1,Value,HilfsPtr;
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)
                   

Aufgabe:    
          Es wurde aus Performance- und Stackminimierungsgruenden
          auf lokale Variablen und Parameter verzichtet !
          Es sollen zusammenhaengende Punkte aus dem Ur-Bild wegkopiert
          und mittels dem Wert von faktory auf ein 16x16 grosses Bild
          Kopiert werden. Das Ziel-Bild ist: KopierePtr->Bilddaten.
          Das Ur-Bild (y_zeile zeigt darauf) wird dabei VERNICHTET !
      
****************************************************************/
void KopiereRekursiv()
{
  maxcount++;
  if (maxcount>MAXSTACK) { maxcount--; return; }   /* notfall-rek. abbruch */
  if ((rek_x<0) || (Page_x<rek_x) || (rek_y<0) || (Page_y<rek_y)) { maxcount--; return; }
  HilfsPtr=(rek_y_zeile+(rek_x >> 3));
  Value=*HilfsPtr;
  Value1=Value &  (~(128>>(rek_x&7)));
  if ( Value!=Value1 )
  {
   *HilfsPtr=Value1; 
   {  
    long x,y;
    x=((rek_x-KopierePtr->x)*faktory) ;
    y=((rek_y-KopierePtr->y)*faktory) ;
    x=x+offset_x;
    y=y+offset_y;
    if (y>15) y=15;
    if (x>15) x=15;
    HilfsPtr=(KopierePtr->BildDaten+y+y+(x>>3));
    *HilfsPtr=*HilfsPtr | (128>>(x&7)); 
    }
   if (Min_x>rek_x) Min_x=rek_x; if (Min_y>rek_y) Min_y=rek_y;
   if (Max_x<rek_x) Max_x=rek_x; if (Max_y<rek_y) Max_y=rek_y;
   rek_x--;
   KopiereRekursiv();
   rek_x+=2;
   KopiereRekursiv();
   rek_x--;
   rek_y--;
   rek_y_zeile-=Page_Bytes_pro_Zeile;
   KopiereRekursiv();
   rek_y+=2;
   rek_y_zeile=rek_y_zeile+Page_Bytes_pro_Zeile+Page_Bytes_pro_Zeile; 
   KopiereRekursiv();
   rek_y--;
   rek_y_zeile-=Page_Bytes_pro_Zeile;
  }
  maxcount--;
}


/***************************************************************
Procedure        : void Finde_Bloecke()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:    
         Es sollen in einem Bild (*OrginalBildPuffer) der Groesse
         Page_x,Page_y (Page_Bytes_pro_Zeile) einzelne zusammen-
         haengende Punkte gefunden werden. Diese Bloecke sollen
         dann in eine Liste eingehaengt werden. Der Startzeiger dieser
         Liste ist: *BitBlockListeStartPtr.
         Die Elemente dieser Liste sind vo Typ: struct BitBlockListe

****************************************************************/
void Finde_Bloecke()
{
 char *ArbeitsPtr,*P1,*P2;
 long t,x,y,dx,dy,y_zeile;
 long n_dx,n_dy,anzahl;
 struct BitBlockListe *Ptr,*Ptr1;

 /* Init der lokalen Variablen */
 t=0; x=0; y=0; dx=0; dy=0; y_zeile=0; n_dx=0;
 n_dy=0; anzahl=0; Ptr=NULL; Ptr1=NULL; P1=NULL; P2=NULL;

 /* Kopiere Orginaldaten in einen Arbeitspuffer */
 x=Page_y*Page_Bytes_pro_Zeile; ArbeitsPtr=malloc(x);
 if (ArbeitsPtr==NULL) { MemoryError(); }
 P1=ArbeitsPtr; P2=OrginalBildPuffer;
 for (t=0;t<x;t++) { *P1=*P2; P1++; P2++; }
 
 /* untersuche nach Bloecken */  
 y_zeile=-Page_Bytes_pro_Zeile-Page_Bytes_pro_Zeile;
 for (y=0;y<Page_y;y+=2)
 {
  y_zeile=y_zeile+Page_Bytes_pro_Zeile+Page_Bytes_pro_Zeile;
  ArbeitsPuffer=ArbeitsPtr;
  /* gehe alle 2 x-Spalten voran */
  for (x=0;x<Page_x;x+=2)
  {
   /* Punkt im Bild gesetzt ? */
   if (Bits[(x&7)] & (*(ArbeitsPuffer+y_zeile+(x>>3))))
   {
    /* Pixel im ArbeitsBild gesetzt ! */
    Min_x=x; Max_x=x; Min_y=y; Max_y=y; maxcount=0;
    rek_x=x; rek_y=y; rek_y_zeile=y_zeile+ArbeitsPuffer;
    /* suche nun den groessten Block */
    SucheBitBloecke();
    dx=Max_x-Min_x; dy=Max_y-Min_y;
    if ((dx>0) && (dy>0))
    {
     /* erstelle Datenelement */
     anzahl++; n_dx+=dx; n_dy+=dy;
     Ptr=calloc(1,sizeof(struct BitBlockListe));    
     if (Ptr==NULL) { MemoryError(); }
     Ptr->Bytes_pro_Zeile=t;
     Ptr->x=Min_x;
     Ptr->y=Min_y;
     Ptr->Startx=x;
     Ptr->Starty=y;
     Ptr->Geloescht=0;
     Ptr->SpaltenNummer=0;
     Ptr->SegmentNummer=0;
     Ptr->XSpiegel=0;
     Ptr->YSpiegel=0;
     Ptr->Untere_Zeile=-1;
     Ptr->Obere_Zeile=-1;
     Ptr->Bild=0;
     Ptr->dx=dx;
     Ptr->Elemente=1;
     Ptr->dy=dy;  
     Ptr->xx=Min_x+dx;
     Ptr->yy=Min_y+dy;     
     Ptr->NextPtrInZeile=NULL;
     Ptr->NextSegmentBlockPtr=NULL;
     Ptr->NextPtrInSegment=NULL;
     Ptr->NextPtr=BitBlockListeStartPtr;
     BitBlockListeStartPtr=Ptr;
    } 
   x=x+dx-1;   
   }  /* punkt gesetzt */  
  }  /* x loop */
 } /* y loop */
 if (anzahl!=0)
 { /* es gab also Bitbloecke */
   long schnittx,schnitty;
   schnittx=(n_dx / (anzahl+0.0))*15;
   schnitty=(n_dy / (anzahl+0.0))*15;
   for (Ptr=BitBlockListeStartPtr;Ptr!=NULL;Ptr=Ptr->NextPtr)
   {
    if (Ptr->dx>schnittx) { Ptr->Bild=1; }
    if (Ptr->dy>schnitty) { Ptr->Bild=1; }
   }
   /* suche nach bilder die sich ueberschneiden */
   for (Ptr=BitBlockListeStartPtr;Ptr!=NULL;Ptr=Ptr->NextPtr)
   {
    if ((Ptr->Geloescht==0) && (Ptr->Bild==1))
    {
     for (Ptr1=BitBlockListeStartPtr;Ptr1!=NULL;Ptr1=Ptr1->NextPtr)
     {
      if ((Ptr1!=Ptr) && (Ptr1->Geloescht==0)) 
      {
       if (Ptr->x>Ptr1->x) { x=Ptr->x; } else { x=Ptr1->x; }
       if (Ptr->y>Ptr1->y) { y=Ptr->y; } else { y=Ptr1->y; }
       if (Ptr->xx<Ptr1->xx) { xx=Ptr->xx; } else { xx=Ptr1->xx; }
       if (Ptr->yy<Ptr1->yy) { yy=Ptr->yy; } else { yy=Ptr1->yy; }
       if (((xx-x)>=0) && ((yy-y)>=0))
       { /* verschmelzen ! */
        if (Ptr->x>Ptr1->x) { x=Ptr1->x; } else { x=Ptr->x; }
        if (Ptr->y>Ptr1->y) { y=Ptr1->y; } else { y=Ptr->y; }
        if (Ptr->xx<Ptr1->xx) { xx=Ptr1->xx; } else { xx=Ptr->xx; }
        if (Ptr->yy<Ptr1->yy) { yy=Ptr1->yy; } else { yy=Ptr->yy; }
        Ptr1->Geloescht=0;
        Ptr1->Bild=1;
        Ptr->x=x;
        Ptr->Bild=1;
        Ptr->Geloescht=0;
        Ptr->y=y;
        Ptr->xx=xx;
        Ptr->yy=yy;
        Ptr->dx=xx-x;
        Ptr->dy=yy-y;           
       } /* diff x,y */       
      }
     }
    }
   } 
  /* und loesche alle Bilder aus dem Orginal-Bild ! */
   for (Ptr=BitBlockListeStartPtr;Ptr!=NULL;Ptr=Ptr->NextPtr)
   { 
    if (Ptr->Bild==1) 
    { 
     Ptr->Geloescht=1;
     LoescheBildFeld(Ptr->x,Ptr->y,Ptr->dx,Ptr->dy);  
    } 
   } 
  }
  /* Jetzt werden alle erkannten Bloecke aus dem Bild kopiert */
  /* resturiere ArbeitsBild */
  x=Page_y*Page_Bytes_pro_Zeile; P1=ArbeitsPtr; P2=OrginalBildPuffer;
  for (t=0;t<x;t++) { *P1=*P2; P1++; P2++; }
  for (Ptr=BitBlockListeStartPtr;Ptr!=NULL;Ptr=Ptr->NextPtr)
  { 
   if ((Ptr->Bild==0) && (Ptr->Geloescht==0))
   {
    for (t=0;t<32;t++) Ptr->BildDaten[t]=0; /* loesche bild */ 
    offset_x=0; offset_y=0;
    if (Ptr->dx>Ptr->dy) { faktory=16/(Ptr->dx+0.0); offset_y=(16-(faktory*Ptr->dy))/2; } 
    else                 { faktory=16/(Ptr->dy+0.0); offset_x=(16-(faktory*Ptr->dx))/2; } 
    if ((Ptr->dx<16) && (Ptr->dy<16)) {  faktory=1.0; offset_y=(16-Ptr->dy)>>1; offset_x=(16-Ptr->dx)>>1; } 
    KopierePtr=Ptr;
    rek_x=Ptr->Startx;
    rek_y=Ptr->Starty;
    maxcount=0;
    rek_y_zeile=ArbeitsPtr+rek_y*Page_Bytes_pro_Zeile;
    KopiereRekursiv();
   }
  }
  free(ArbeitsPtr);
}   


/***************************************************************
Procedure        : void Finde_SegmentBloecke()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:    
         Es sollen in einem Bild (*OrginalBildPuffer) der Groesse
         Page_x,Page_y (Page_Bytes_pro_Zeile) alle "Segmente", d.h.
         alle rechteckigen Bloecke zusammengefasst werden die nach
         einem Verkleinerungszoom des Orginalbildes sich gebildet haben.
         Dadurch kann man einzelne Text-Bloecke gut herausfinden.  
         Diese gefundenen Bloecke werden in eine Liste eingetragen.
         Der StartPointer der Liste ist: SegmentBitBlockListeStartPtr
         Die Elemente der Liste haben den Typ: BitBlockListe.
         Der Zeiger auf das naechste Segment ist: NextPtrInSegment
         Es sollen weiterhin alle Segmente in Spalten sortiert werden.
         Dabei gilt: Spalten von links nach rechts, Segmente in den
         Spalten vun oben nach unten.

****************************************************************/
void Finde_SegmentBloecke()
{
 long t,x,y,dx,dy,y1,xx,yy,y_zeile;
 struct BitBlockListe *Ptr,*P1,*P2,*P3,*P4,*P0;
 unsigned char *HilfsPuffer,*P,*Pc1;
 long SegmentNummer,x11,y11,k,min,max,Anzahl,Summe;
 float deltax,deltay;

 /* Init der lokalen Variablen */
 t=0; x=0; y=0; dx=0; dy=0; xx=0; yy=0; y_zeile=0;
 SegmentNummer=0; x11=0; y11=0; k=0; min=0; max=0;
 Summe=0; Anzahl=0; y1=0;
 P1=NULL; P2=NULL; P3=NULL; P4=NULL; P0=NULL; 
 P=NULL; HilfsPuffer=NULL; deltax=0.0; deltay=0.0;
 
 /* lege platz fuer segment-kopie an ! */
 k=Page_Bytes_pro_Zeile*Page_y; 
 HilfsPuffer=calloc(1,k);
 if (HilfsPuffer==NULL) { MemoryError(); }
 Pc1=HilfsPuffer;
 for (t=0;t<k;t++) { *Pc1=0; Pc1++; }
 dx=0;
 dy=0;
 Anzahl=0;
 
 /* kopiere jetzt schwarz bloecke rein ! */
   for (Ptr=BitBlockListeStartPtr;Ptr!=NULL;Ptr=Ptr->NextPtr)
   { 
    if ((Ptr->Bild==0) && (Ptr->Geloescht==0))  
    { 
     dx=dx+Ptr->dx;
     dy=dy+Ptr->dy;
     Anzahl++;
    } 
   } 
 x11=0;
 y11=0;
 if (Anzahl!=0)
 {
  x11=((dx+0.0)/Anzahl)*2;
  y11=((dy+0.0)/Anzahl)*3;
 } 
 for (Ptr=BitBlockListeStartPtr;Ptr!=NULL;Ptr=Ptr->NextPtr)
   { 
    if ((Ptr->Bild==0) && (Ptr->Geloescht==0))  
    { 
    if (x11>Ptr->dx) { x=x11; } else { x=Ptr->dx*1.5; }
    if (y11>Ptr->dy) { y=y11; } else { y=Ptr->dy*1.5; } 
     SetzeBildFeld(HilfsPuffer,Page_Bytes_pro_Zeile,Ptr->x,Ptr->y,x,y); 
    } 
   } 

 /* Jetzt wird nach den Bloecken im schwarzen bloecke Bild gesucht */
 y_zeile=0-Page_Bytes_pro_Zeile;
 for (y=0;y<Page_y;y+=1)
 {
  y_zeile=y_zeile+Page_Bytes_pro_Zeile;
  for (x=0;x<Page_x;x+=1)
  {
   /* Punkt gesetzt ? */
   if (Bits[(x&7)]&(*(HilfsPuffer+y_zeile+(x >> 3))))
   { 
    /* Pixel im ArbeitsBild gesetzt ! */
    Min_x=x; Max_x=x;
    Min_y=y; Max_y=y;
    maxcount=0;
    ArbeitsPuffer=HilfsPuffer;
    rek_x=x; rek_y=y; rek_y_zeile=y_zeile+ArbeitsPuffer;
    SucheBitBloecke();
    dx=Max_x-Min_x+1;
    dy=Max_y-Min_y+1;
    if ((dx>3) && (dy>3))
    {
     /* erstelle Datenelement */
     Ptr=calloc(1,sizeof(struct BitBlockListe));    
     if (Ptr==NULL) { MemoryError(); }
     Ptr->Bytes_pro_Zeile=0;
     Ptr->x=Min_x;
     Ptr->y=Min_y;
     Ptr->Startx=x;
     Ptr->Starty=y;
     Ptr->Bild=0;
     Ptr->SegmentNummer=-1;
     Ptr->SpaltenNummer=-1;
     Ptr->XSpiegel=0;
     Ptr->YSpiegel=0;
     Ptr->Untere_Zeile=-1;
     Ptr->Obere_Zeile=-1;
     Ptr->Geloescht=0;
     Ptr->dx=dx;
     Ptr->dy=dy; 
     Ptr->xx=Min_x+dx;
     Ptr->yy=Min_y+dy;       
     Ptr->NextPtrInZeile=NULL;
     Ptr->NextSegmentBlockPtr=NULL;
     Ptr->NextPtrInSegment=NULL;
     Ptr->NextPtr=SegmentBitBlockListeStartPtr;
     SegmentBitBlockListeStartPtr=Ptr;
    } /* min. 2 pixel */
    x=x+dx-1;
   }  /* punkt gesetzt */ 
  }  /* x loop */
 }  /* y loop */
 free(HilfsPuffer);
 /* Wenn nun wegen max-rekursion abgebrochen wurde, muessen */
 /* wir noch zusammenhaengende Segmente zusammenfuegen !    */
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
 {
  if (P1->Geloescht==0)
  {
   for (P2=SegmentBitBlockListeStartPtr;P2!=NULL;P2=P2->NextPtr)                
   {
    if ((P2->Geloescht==0) && (P1!=P2))
    {
     if (P1->x>P2->x) { x=P1->x; } else { x=P2->x; }
     if (P1->y>P2->y) { y=P1->y; } else { y=P2->y; }
     if (P1->xx<P2->xx) { xx=P1->xx; } else { xx=P2->xx; }
     if (P1->yy<P2->yy) { yy=P1->yy; } else { yy=P2->yy; }
     if (((xx-x)>=0) && ((yy-y)>=0))
     { /* verschmelzen ! */
      if (P1->x>P2->x) { x=P2->x; } else { x=P1->x; }
      if (P1->y>P2->y) { y=P2->y; } else { y=P1->y; }
      if (P1->xx<P2->xx) { xx=P2->xx; } else { xx=P1->xx; }
      if (P1->yy<P2->yy) { yy=P2->yy; } else { yy=P1->yy; }
      P2->Geloescht=1;
      P1->x=x;
      P1->y=y;
      P1->xx=xx;
      P1->yy=yy;
      P1->dx=xx-x;
      P1->dy=yy-y;           
     } /* diff x,y */
    } /* p1!=p2 */  
   } /* for P2 */
  } /* if */
  if (((P1->xx-P1->x)<2) && ((P1->yy-P1->y)<3)) P1->Geloescht=1;
 } /* for P1 */

 /* Berechne nun die Spalten / Segmentreihenfolge */
 min=9999999; max=0; Summe=0; Anzahl=0; P2=NULL; 
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
 {
  if (P1->Geloescht==0)
  {
   if (P1->x<min) { min=P1->x; P2=P1; }
   if (P1->xx>max) { max=P1->xx; }
   Anzahl++;
   Summe=Summe+P1->xx-P1->x; 
  } /* if not killed */
 } /* for */
 if(Anzahl!=0)
 { 
  float Schnitt,aha;
  long SpaltenAnzahl,x1,SpaltenNummer;  
  Schnitt=Summe/(Anzahl+0.0);
  SpaltenNummer=0;
  SpaltenAnzahl=(0.5+((max-min) / Schnitt));
  aha=((max-min) / SpaltenAnzahl)*0.7 ;
  
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
  {
    
    if ((P1->dx<aha) )
    { 
     dx=P1->dx+x11;
     xx=dx+P1->x;
     P1->dx=dx; 
     P1->xx=xx;    
   }
  }


 min=9999999; max=0; Summe=0; Anzahl=0; P2=NULL; 
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
 {
  if (P1->Geloescht==0)
  {
   if (P1->x<min) { min=P1->x; P2=P1; }
   if (P1->xx>max) { max=P1->xx; }
   Anzahl++;
   Summe=Summe+P1->xx-P1->x; 
  } /* if not killed */
 } /* for */
  
 Schnitt=Summe/(Anzahl+0.0);
 SpaltenNummer=0;
 SpaltenAnzahl=(0.5+((max-min) / Schnitt));


 /* */
  for (SpaltenNummer=0;SpaltenNummer<=SpaltenAnzahl;)
  {
   x1=min+Schnitt;
   for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
   {
    if ((P1->Geloescht==0) && (P1->SpaltenNummer==-1) && (P1->x<x1))
    {
     P1->SpaltenNummer=SpaltenNummer;
    } /* if not killed */
   } /* for */
   SpaltenNummer++; 
   min=max+1;
   for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
   {
    if ((P1->Geloescht==0) && (P1->SpaltenNummer==-1) && (P1->x<min)) 
    {
     min=P1->x; 
    } /* if */
   } /* for */
  } /* for spaltennummer */
  P3=NULL; P4=NULL;
  SegmentNummer=0;
  min=9999999;
  for (SpaltenNummer=0;SpaltenNummer<=SpaltenAnzahl;SpaltenNummer++)
  {
   P2=SegmentBitBlockListeStartPtr;
   for (;P2!=NULL;)
   {
    P2=NULL; min=88888888;
    for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextPtr)
    {
     if ((P1->Geloescht==0) && (P1->SpaltenNummer==SpaltenNummer) && (P1->SegmentNummer==-1))
     {
      if (P1->y<min) { min=P1->y; P2=P1; }
     } /* if */
    }  /* for */
    if (P2!=NULL)
    {
     if (P3==NULL) P4=P2; /* start */
     if (P3!=NULL) P3->NextSegmentBlockPtr=P2;
     P3=P2;
     P2->SegmentNummer=SegmentNummer;  
     SegmentNummer++;
    }
   } /* P2!=NULL */ 
  } /* for spaltennummer */
  P0=NULL; 
  if (P4!=NULL)
  {
   for (P1=P4;P1!=NULL;P1=P1->NextSegmentBlockPtr)
   {
    if (P1->NextSegmentBlockPtr!=NULL)
    {
     P2=P1->NextSegmentBlockPtr;
     if (P1->y>P2->y) { y=P1->y; } else { y=P2->y; }
     if (P1->yy<P2->yy) { yy=P1->yy; } else { yy=P2->yy; }
     if ((yy-y)>=0)
     { /* aha, auf der gleichen ebene ! */
      if (P1->x>P2->x) 
      { /* falsch, es muss zuerst P1 kommen ! */
       P3=P2->NextSegmentBlockPtr;
       if (P0!=NULL) { P0->NextSegmentBlockPtr=P2; } else { P4=P2; }
       P2->NextSegmentBlockPtr=P1;  
       P1->NextSegmentBlockPtr=P3;
       P1=P4;
      }
     }
    } /* if */
    P0=P1;
   } /* for */
  } /* if NULL */
  /* Korrigiere die Kordinaten, da diese gezoomt sind ! */
  SegmentBitBlockListeStartPtr=P4;
/*  for (P1=P4;P1!=NULL;P1=P1->NextSegmentBlockPtr)
  {
   P1->NextPtrInSegment=NULL;
   P1->x=P1->x/deltax;
   P1->y=P1->y/deltay;
   P1->dx=P1->dx/deltax;
   P1->dy=P1->dy/deltay;
  }
*/
 }

  /* Wenn der Groessenanpassung muessen */
 /* wir noch zusammenhaengende Segmente zusammenfuegen !    */
 if (1)
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextSegmentBlockPtr)
 {
  if (P1->Geloescht==0)
  {
   for (P2=P1->NextSegmentBlockPtr;P2!=NULL;P2=P2->NextSegmentBlockPtr)                
   {
    if ((P2->Geloescht==0) && (P1!=P2))
    {
     if (P1->x>P2->x) { x=P1->x; } else { x=P2->x; }
     if (P1->y>P2->y) { y=P1->y; } else { y=P2->y; }
     if (P1->xx<P2->xx) { xx=P1->xx; } else { xx=P2->xx; }
     if (P1->yy<P2->yy) { yy=P1->yy; } else { yy=P2->yy; }
     if (((xx-x)>=0) && ((yy-y)>=0) && (P1->x<P2->x))
     { /* verschmelzen ! */
      if (P1->x>P2->x) { x=P2->x; } else { x=P1->x; }
      if (P1->y>P2->y) { y=P2->y; } else { y=P1->y; }
      if (P1->xx<P2->xx) { xx=P2->xx; } else { xx=P1->xx; }
      if (P1->yy<P2->yy) { yy=P2->yy; } else { yy=P1->yy; }
      P2->Geloescht=1;
      P1->x=x;
      P1->y=y;
      P1->xx=xx;
      P1->yy=yy;
      P1->dx=xx-x;
      P1->dy=yy-y; 
      break;          
     } /* diff x,y */
    } /* p1!=p2 */  
   } /* for P2 */
  } /* if */
  if (((P1->xx-P1->x)<2) && ((P1->yy-P1->y)<3)) P1->Geloescht=1;
 } /* for P1 */


}


/***************************************************************
Procedure        : void Bloecke_in_Segmente()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Zugriffe/Global  : SegmentBitBlockListeStartPtr sollte auf die 
                   Segment-liste zeigen
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)
                   

Aufgabe:    
          Es sollen alle BitBloecke in einem Segment dem Segment
          als verkette Liste zugeordnet werden !
              
****************************************************************/

void Bloecke_in_Segmente()
{
  long x,y,xx,yy,Summe,Anzahl; 
  struct BitBlockListe *P1,*P2;
  /* init der lokalen Variabeln */
  x=0; y=0; xx=0; yy=0; P1=NULL; P2=NULL;
  /* gehe alle Segmente durch */
  for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextSegmentBlockPtr)
  {
   Summe=0; Anzahl=0;
   /* lebt dieses Segment noch ? */
   if (P1->Geloescht==0)
   {
    P1->NextPtrInSegment=NULL;
    x=P1->x;
    y=P1->y;
    xx=x+P1->dx;
    yy=y+P1->dy;
    /* gehe alle BitBloecke durch */
    for (P2=BitBlockListeStartPtr;P2!=NULL;P2=P2->NextPtr)
    {
     /* lebt dieser Bitblock noch ? */
     if ((P2->Geloescht==0) && (P2->Bild==0))
     {     
      if ((P2->x>=x) && (P2->x<=xx) &&  (P2->y>=y) && (P2->y<=yy))
      { /* element ist in diesem Segment ! */
       Anzahl++; Summe=Summe+P2->dx;
       P2->NextPtrInSegment=P1->NextPtrInSegment;
       P1->NextPtrInSegment=P2;
      }
     } 
    }
    if (Anzahl!=0) P1->ddx=(Summe+0.0)/Anzahl;   
   }
  } 
}

/***************************************************************
Procedure        : void FindeZeilen()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Zugriffe/Global  : SegmentBitBlockListeStartPtr sollte auf die 
                   Segment-liste zeigen
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)
                   

Aufgabe:    
          Es sollen in Segmenten Zeilen erkannt werden und
          in diesen Zeilen sollen alle zugehoerigen Bitbloecke
          in eine Liste der x-Position nach eingetragen werden.
          (kleines x steht am weitesten links ! (wie im Bild) )
          In den Zeilen kann es eine UNTERE und OBERE Linie geben.
          Diese UNTERE-Linie existiert genau dann, wenn z.b.
          ein p,q,g,y unter den normalen Zeichen nach unten stehen.
          Eine OBERE Linie existiert dann, wenn ausser denn
          Kleinbuchstaben a,c,e,o,u,m,n... auch noch ein Grossbuchstabe
          oder ein "langer" Kleinbuchstabe wie z.b. h,b,t,... vorkommt.
              
****************************************************************/
void FindeZeilen()
{
 long t,y,yy,x,xx,Zeile,flag,flag1,Starty,Endey,Schnitt,Anzahl,Summe,i;
 long uu,Starty1,aha,frau,merkey,merkey1,y1,y11,min,max,max1,min1;
 double Winkel,Winkel1,toll;
       

 struct BitBlockListe *P1,*P2,*P3,*P4,*P5,*P6,*P7,*P8,*LastPtr;

 /* init der lokalen variablen */
 i=0; t=0; x=0; y=0; xx=0; yy=0; flag=0; flag1=0; Starty=0; Endey=0;
 min=0; min1=0; max=0; max1=0; Winkel=0.0; Winkel1=0.0; toll=0.0;
 Anzahl=0; Summe=0; frau=0; uu=0; aha=0; Starty1=0; merkey1=-1; merkey=-1;
 P1=NULL; P2=NULL; P3=NULL; P4=NULL; P5=NULL; P6=NULL; P7=NULL; P8=NULL;
 /* gehe alle Segmente durch */   
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextSegmentBlockPtr)  
 {
 /* berechne durchschnittsgroesse eines Zeichens in einem Segment */
 Anzahl=0; aha=0; frau=1; Summe=0; Schnitt=0; P1->NextZeileInSegment=NULL;
 for (P2=P1->NextPtrInSegment;P2!=NULL;P2=P2->NextPtrInSegment) { Summe=Summe+P2->dy; Anzahl++; }   
 /* Wenn es BitBloecke gab , dann berechne durchschnittl. Groesse. */
 if (Anzahl!=0) { Schnitt=(Summe/Anzahl)*2; Summe=Schnitt; Anzahl=Schnitt*0.3; }
 /* durchlaufe jetzt jede y-zeile im Segment durch */ 
 Zeile=0; flag=1; flag1=1; yy=P1->y+P1->dy+2; Starty1=P1->y;  
 LastPtr=NULL;
 for (y=(P1->y-1);y<=yy;y++)
 {
  flag=1; 
 /* durchlaufe alle Elemente eines Segments */
  for (P2=P1->NextPtrInSegment;P2!=NULL;P2=P2->NextPtrInSegment)
  {
   /* ist Element in der Zeile ? */
   if (P2->dy<Schnitt) if ((P2->y<=y) && ((P2->y+P2->dy))>=y) { flag=0;  break;} 
   if (P2->dy>=Schnitt) { Schnitt=Schnitt*2; }
  }
  /* erkenne Zeilenstart/Ende --> ist etwas kryptisch geraten :-() */
  if ((frau==1) && (flag==0)) { frau=0; Starty1=y; }
  Schnitt=Summe;
  if ((aha==1) && (flag==0) && (flag1==0)) { aha=0; Starty1=Starty; }
  if ((flag==0) && (flag1==1)) { uu=0; Starty=y; if (Anzahl>=P2->dy) { y=y+Anzahl; } aha=1; }
  /* Zeilenende gefunden -> erstelle Zeilenelement */
  if ((flag==1) && (flag1==0))
  {
   Endey=y;
   P4=(struct BitBlockListe *)calloc(1,sizeof(struct BitBlockListe)); 
   if (P4==NULL) { MemoryError(); }
   P4->NextZeileInSegment=NULL;
   P4->NextSegmentBlockPtr=NULL;
   P4->NextPtr=NULL;
   P4->NextPtrInZeile=NULL;
   P4->y=Starty1;
   P4->dy=Endey-Starty1;
   P4->Geloescht=0;
   P4->Bild=0;
   P4->Untere_Zeile=-1;
   P4->Obere_Zeile=-1;
   P4->x=P1->x;
   P4->dx=P1->dx;
   P4->xx=P4->dx+P4->x;
   P4->yy=P4->dy+P4->y;
   if (LastPtr==NULL)
   {
    P4->NextZeileInSegment=NULL;
    P1->NextZeileInSegment=P4;     
    LastPtr=P4;
   } else
   {
    P4->NextZeileInSegment=NULL;
    LastPtr->NextZeileInSegment=P4;     
    LastPtr=P4;
   }
   /* Durchlaufe alle Elemnte in dem Segment */
   for (P3=P1->NextPtrInSegment;P3!=NULL;P3=P3->NextPtrInSegment)
   {
    P6=NULL;
    /* Element in der Zeile ? */
    if ((P4->y<=P3->y) && ((P4->yy)>=P3->y))
    {
     /* Wenn dies das ALLERERSTE Elment in der Liste ist -> eintragen */
     if (P4->NextPtrInZeile==NULL)
     {
      P4->NextPtrInZeile=P3; P3->NextPtrInZeile=NULL;    
      } else
      { /* es gibt also schon mid. 1 Element in der Liste ! */
       i=0;
       P6=NULL;
       /* durchlaufe alle schon einsortierten Elemente */
       for (P5=P4->NextPtrInZeile;P5!=NULL;P5=P5->NextPtrInZeile)
       {
        /* schon vorbei ? (zu grosser x-Wert ?)*/
        if ((P5->x) > (P3->x))
        {
         /* und beim vorigem Element einsortieren */
         if (P6!=NULL)
         {
          P3->NextPtrInZeile=P6->NextPtrInZeile;
          P6->NextPtrInZeile=P3; 
         } else
         {
          P3->NextPtrInZeile=P4->NextPtrInZeile;
          P4->NextPtrInZeile=P3; 
         }
         i=1;
          break;
        } /* if groesser */       
        /* vorheriges Element merken */
        P6=P5;
       }  /* for ... */
       if (i==0) 
       { /* dies muss also letztes element sein ! */           
        P6->NextPtrInZeile=P3; P3->NextPtrInZeile=NULL; 
        P6=NULL;
       } 
      } /* if erster */
     } /* if in zeile */
    }  /* for zeilen */
    merkey1=-1; merkey=-1;
    /* Hier berechnen wir nun die UNTERE und OBERE Linie einer Zeile */
    /* alle Elemente einer Zeile eines Segmentes durchlaufen */
    for (P6=P4->NextPtrInZeile;P6!=NULL;P6=P6->NextPtrInZeile)
    {
     P7=P6->NextPtrInZeile;
     if (P7!=NULL)
     { /* es existiert ein Nachbar ! */
      if ((P6->x!=P7->x)) 
      {
       toll=MAXWINKEL;
       max =(0.5*P4->dy )+P4->y;
       min =(0.15*P4->dy)+P4->y;
       min1=(0.5*P4->dy )+P4->y;
       max1=(0.85*P4->dy)+P4->y;
       Winkel =atan((double) ((P6->y-P7->y  )                   / (0.0+P6->x-P7->x))) ;
       Winkel1=atan((double) (((P6->y+P6->dy) - (P7->y+P7->dy)) / (0.0+P6->x-P7->x))) ;
       if (Winkel<0)  Winkel =-Winkel; 
       if (Winkel1<0) Winkel1=-Winkel1;  
       if ((toll>Winkel) && (min<P6->y) && (max>P6->y))
       { /* gleiche hoehe ! */
        y1=P6->y; if (y1>P7->y) { y1=P7->y; }      
        if (merkey<y1) merkey=y1;                  
        P4->Obere_Zeile =merkey;
       } /* grad ok */
       if ((toll>Winkel1) && (min1<(P6->y+P6->dy)) && (max1>(P6->y+P6->dy)))
       { /* gleiche hoehe ! */
        y11=P6->y+P6->dy; if (y11>(P7->y+P7->dy)) { y11=P7->y+P7->dy; }      
        if (merkey1<y11) merkey1=y11;                  
        P4->Untere_Zeile=merkey1;
       } /* grad ok */
      } /* hoehe */
      P8=P7->NextPtrInZeile;
      if (P8!=NULL)
      { /* es existiert ein Nachbar ! */
       if ((P6->x!=P8->x)) 
       {
        toll=MAXWINKEL;
        max=(0.5*P4->dy)+P4->y;
        min=(0.15*P4->dy)+P4->y;
        Winkel=atan((double) ((P6->y-P8->y) / (0.0+P6->x-P8->x))) ;
        if (Winkel<0) Winkel=-Winkel;     
        if ((toll>Winkel) && (min<P6->y) && (max>P6->y))
        { /* gleiche hoehe ! */
         y1=P6->y; if (y1>P8->y) { y1=P8->y; }      
         if (merkey<y1) merkey=y1;                  
         P4->Obere_Zeile =merkey;
        } /* grad ok */
       } /* hoehe */
      } /* exist  */
     } /* exist  */
    }  
   } 
   flag1=flag;
  } /* for alle y-zeilen */
 } /* for segmente */
} /* proc */


/***************************************************************
Procedure        : void VerschmelzeBitBloeckeInZeile()

Uebergabe        : Nichts
Rueckgabe        : Nichts
Zugriffe/Global  : SegmentBitBlockListeStartPtr sollte auf den Anfang
                   der Segment-liste stehen !
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe: 

        Es soll in allen Zeilen ueberprueft werden, ob
        maximal 99 BitBloecke uebereinander liegen
        (-> Schnittflache mit x , keine Schnittflaeche mit y).
        Wenn ja, dann sollen alle uebereinanderliegenden Bloecke
        geloescht werden (indem ein Flag gesetzt wird) und diese 
        Bloecke zu einem neuen Block zusammengesetzt werden. Dazu
        werden in allen gefundenen Bloecken die jeweiligen 
        start(x,y)-sequenzen verwendet und ein neues bereits auf 16x16 
        Bild transformiert.
        
        Wichtig: Es duerfen nicht einfach die rechteckigen Bloecke kopiert werden,
                 da kursive Zeichen sonst mitkopiert werden ! 
****************************************************************/
void VerschmelzeBitBloeckeInZeile()
{
 struct BitBlockListe *P1,*P2,*P3,*P4;
 struct BitBlockListe *Pcat[100];
 long dx,dy,x,y,xx,yy,min_x,max_x,max_y,min_y;
 long t,i,Flag;
 char *Pc1,*Pc2,*ArbeitsPtr;
 /* setze alle Variablen auf def. Werte */
 t=0; i=0; P1=NULL; P2=NULL; P3=NULL; P4=NULL;
 dx=0;dy=0;Flag=0;x=0;y=0;xx=0;yy=0;min_x=0;min_y=0;max_y=0;max_x=0;
 Pc1=NULL; Pc2=NULL; ArbeitsPtr=NULL; for (i=0;i<99;i++) Pcat[i]=NULL;
 /* kopiere Arbeitspuffer , da dieser bei rekursivem kopieren spaeter zerstoert wird */
 i=Page_y*Page_Bytes_pro_Zeile; 
 ArbeitsPtr=malloc(i);
 if (ArbeitsPtr==NULL) { MemoryError(); }
 Pc1=ArbeitsPtr;
 Pc2=OrginalBildPuffer;
 for (t=0;t<i;t++) { *Pc1=*Pc2; Pc1++; Pc2++; } 
 /* gehe jetzt alle Segmente durch */                        
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextSegmentBlockPtr)  
 {
  /* gehe jetzt alle Zeilen eines Segments durch */
  for (P2=P1->NextZeileInSegment;P2!=NULL;P2=P2->NextZeileInSegment)
  { 
   Flag=0; min_x=99999; min_y=99999; max_x=-99999; max_y=-99999;
   /* gehe jetzt alle Bitbloecke einer Zeile durch */
   for (P3=P2->NextPtrInZeile;P3!=NULL;P3=P3->NextPtrInZeile)
   {
    /* lebt dieses Bitblock noch ? */
    if (P3->Geloescht==0)
    {
     min_x=P3->x; min_y=P3->y;
     max_x=P3->x+P3->dx; max_y=P3->y+P3->dy;
     /* ok , schaue die naechsten Bitbloecke an (die weiter rechts) */
     for (P4=P3->NextPtrInZeile;P4!=NULL;P4=P4->NextPtrInZeile)
     {  
      if ((P4->x)>(P3->x+P3->dx)) break;    /* schon zu weit rechts ? (keine x-Schnittflaeche moeglich !) */
      if ((P4->Geloescht==0) && (P4!=P3))
      {
       /* Berechne eventuelle x,y-ueberschneidungen */
       if (P4->x>P3->x) { x=P4->x; } else { x=P3->x; }
       if (P4->y>P3->y) { y=P4->y; } else { y=P3->y; }
       if (P4->xx<P3->xx) { xx=P4->xx; } else { xx=P3->xx; }
       if (P4->yy<P3->yy) { yy=P4->yy; } else { yy=P3->yy; }
       /* X-ueberschneidung und keine y-ueberschneidung ?*/
       if (((xx-x)>0) && ((yy-y)<0))
       {
        /* ja, also block loeschen und min/max platzbedarf des neuen Blocks berechnen */
        if ((P4->x+P4->dx)>max_x) max_x=P4->x+P4->dx;
        if ((P4->y+P4->dy)>max_y) max_y=P4->y+P4->dy;
        if (P4->x<min_x) min_x=P4->x;
        if (P4->y<min_y) min_y=P4->y;
        Pcat[Flag+1]=P4;   /* merke den geloeschten Block !*/
        Flag++;  /* merke die Anzahl der geloschten Bloecke ! */
        P4->Geloescht=1; 
       }
      } /* if killed */
     } /* for elemente */
     /* gab es den Fall , dass Bloecke uebereinander lagen ?*/
     if (Flag!=0)
     { 
      Pcat[0]=P3;  /* Merke "untersten" Block !*/
      dx=max_x-min_x; /* Groesse des Blocks berechnen */
      dy=max_y-min_y;
      P3->x=min_x;
      P3->y=min_y;
      P3->xx=max_x;
      P3->yy=max_y;
      P3->dx=max_x-min_x;
      P3->dy=max_y-min_y;
      /* berechne den Zoom-faktor fuer die 16x16 zoom. */
      offset_x=0; offset_y=0;
      if (dx>dy) { faktory=16/(dx+0.0); offset_y=(16-(faktory*dy))/2; } 
      else      { faktory=16/(dy+0.0); offset_x=(16-(faktory*dx))/2; } 
      if ((dx<16) && (dy<16)) {  faktory=1.0; offset_y=(16-dy)>>1; }
       /* leere das 16x16 Bild */ 
      for (t=0;t<32;t++) P3->BildDaten[t]=0; /* loesche bild */ 
      if (Flag>99) Flag=99;
      KopierePtr=P3;
      /* gehe alle geloschten Bloecke durch und zoome sie in das 16x16 Feld */
      for (i=0;i<=Flag;i++) 
      { /* Cat alle diese Elemente */
       rek_x=Pcat[i]->Startx;
       rek_y=Pcat[i]->Starty;
       P3->Elemente++;
       Pcat[i]->Geloescht=1;
       maxcount=0;
       rek_y_zeile=ArbeitsPtr+rek_y*Page_Bytes_pro_Zeile;
       KopiereRekursiv(); 
      }
      P3->Geloescht=0;
      Flag=0;
     }
    } /* if killed */
   } /* for elemente */
  } /* for zeilen */
 } /* for segmente */
 free(ArbeitsPtr);
}


/***************************************************************
Procedure        : void SucheBitBloecke()

Uebergabe        : Nichts
Rueckgabe        : Nichts (-> Global: Min_x,Max_x,Min_y,Max_y)
Zugriffe/Global  : maxcount , Value1 , Value, rek_y, rek_x, rek_y_zeile, HilfsPtr
                   Page_x , Page_y
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:            
          Aus Performance-Gruenden und Stacksize minimierung wurden absichtlich 
          auf alle lokalen Variablen/Parameter verzichtet !
          Diese Routine soll in einem Bild mit der Groesse:
          Page_x,Page_y nach zusammenhaengenden Punkten suchen.
          Dabei wird diese Funktion rekursiv aufgerufen. Damit es
          zu keinem Stack-overflow kommt (bei einem sehr grossen, total
          schwarzen Block wird mit der Variable maxcount die aktuelle
          Stacktiefe gezaehlt, bei maxcount > MAXSTACK wird die Rekursion
          abgebrochen -> Dies ergibt zwar spaeter kein zusammenhaengendes Bild,
          wird aber (speziell deswegen) spaeter wieder "repariert !".
          Die Variable rek_y_Zeile zeigt auf das Ur-Bild. Dieses Ur-Bild wird
          VERNICHTET !
          
****************************************************************/
void SucheBitBloecke()
{
 maxcount++;
 
 if (maxcount>MAXSTACK) { maxcount--; return; }   /* notfall-rek. abbruch */
 /* sind wir am Bild-Rand ? */
 if ((rek_x<0) || (Page_x<rek_x) || (rek_y<0) || (Page_y<rek_y)) { maxcount--; return; }
 /* berechne Punkt-Position als Byteadresse im Bild */
 HilfsPtr=(rek_y_zeile+(rek_x >> 3));
 /* hole Byte aus dem Bild */  
 Value=*HilfsPtr;
 /* ist dieser x-Punkt gesetzt ? */
 Value1=Value &  (~(128>>(rek_x&7)));
 if ( Value!=Value1 )
 {
  /* Punkt war gesetzt, loesche diesen um Endlos-Rekursion zu vermeiden */
  *HilfsPtr=Value1; 
  /* suche nach maximalen umrandung */
  if (Min_x>rek_x) Min_x=rek_x; if (Min_y>rek_y) Min_y=rek_y;
  if (Max_x<rek_x) Max_x=rek_x; if (Max_y<rek_y) Max_y=rek_y;
  /* suche jetzt in alle vier richtungen weiter ... unten,oben,rechts,links */
  rek_x--;
  SucheBitBloecke();
  rek_x+=2;
  SucheBitBloecke();
  rek_x--;
  rek_y--;
  rek_y_zeile-=Page_Bytes_pro_Zeile;
  SucheBitBloecke();
  rek_y+=2;
  rek_y_zeile=rek_y_zeile+Page_Bytes_pro_Zeile+Page_Bytes_pro_Zeile; 
  SucheBitBloecke();
  rek_y--;
  rek_y_zeile-=Page_Bytes_pro_Zeile;
 }
 /* und wier steigen aus der rekursion wieder aus */
 maxcount--;
}

/***************************************************************
Procedure        : void SucheSegmenteBitBloecke()

Uebergabe        : Nichts
Rueckgabe        : Nichts (-> Global: Min_x,Max_x,Min_y,Max_y)
Zugriffe/Global  : maxcount , Value1 , Value, rek_y, rek_x, rek_y_zeile, HilfsPtr
                   Page_x , Page_y
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:            
          Aus Performance-Gruenden und Stacksize minimierung wurden absichtlich 
          auf alle lokalen Variablen/Parameter verzichtet !
          Es sollen alle zusammenhaengende Punkte in einem Bild der Groesse:
          Segment_x,Segment_Y gefunden werden. 

          Die Variable rek_y_Zeile zeigt auf das Ur-Bild. Dieses Ur-Bild wird
          VERNICHTET !
          
****************************************************************/
void SucheSegmenteBitBloecke()
{
 maxcount++;
 /* schon maximaler Stacktiefe angelangt ? */
 if (maxcount>MAXSTACK) { maxcount--; return; }   /* notfall-rek. abbruch */
 /* am Bildrand ? */
 if ((rek_x<0) || (Segment_X<rek_x) || (rek_y<0) || (Segment_Y<rek_y)) { maxcount--; return; }
 /* Berechne Byte-positon des Punktes im Bild */
 HilfsPtr=(rek_y_zeile+(rek_x >> 3));
 /* hole dieses Byte */
 Value=*HilfsPtr;
 Value1=Value & (~(128>>(rek_x&7)));
 /* Punkt gesetzt ? */
 if ( Value!=Value1 )
 {
  /* loesche diesen Punkt */
  *HilfsPtr=Value1;     
  /* berechne Ausdehung des Blockes */
  if (Min_x>rek_x) Min_x=rek_x; if (Min_y>rek_y) Min_y=rek_y;
  if (Max_x<rek_x) Max_x=rek_x; if (Max_y<rek_y) Max_y=rek_y;
  /* und in alle vier richtungen weiter gehen (links,rechts,unten,oben) */
  rek_x--;
  SucheSegmenteBitBloecke();
  rek_x+=2;
  SucheSegmenteBitBloecke();
  rek_x--;
  rek_y--;
  rek_y_zeile-=Segment_Bytes;
  SucheSegmenteBitBloecke(); 
  rek_y_zeile=rek_y_zeile+Segment_Bytes+Segment_Bytes;
  rek_y+=2;
  SucheSegmenteBitBloecke();
  rek_y--;
  rek_y_zeile-=Segment_Bytes; 
}
  maxcount--;
}



/***************************************************************
Procedure        : BerechneWsaWerte()

Uebergabe        : Zeiger auf ein BitBlock.
Rueckgabe        : Nichts 
Autor            : Martin Bauer
Letzte Aenderung : 3.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:            
          Es sollen die WSA-Werte berechnet werden. Dazu
          werden die was-Kordinaten abgefahren und die gesetzten
          Punkte im Bild gezaehlt. Diese Werte werden dann
          den 128 Achsen zugeordnet.
****************************************************************/
void BerechneWsaWerte(Ptr)
struct BitBlockListe *Ptr;
{
 int B;
 int Achse,t,Punkt,x; 
 unsigned char A;
 long c;

 t=0; c=0;
 for (Achse=0;Achse<128;Achse++)
 {
  B=0;
  for (Punkt=0;Punkt<48;Punkt+=2)
  {
   x=Wsa[t+Punkt];
   if (x==-1) break; /* schon das ende erreicht ? */    
   A=*(((Wsa[t+Punkt+1])<<1)+Ptr->BildDaten+(x>>3));
   if (A & (128>>(x&7)))
   {
    B++;
   }
  } /* for Punkt */
  Ptr->WsaAchsenWerte[Achse]=B;
  c=c+Quadrat[B+100];
 t+=48;
 } /* for Achse */

 c=sqrt(c);
 Ptr->Klasse=c/6;

} /* proc */



/***************************************************************
Procedure        : void VergleicheAufSymetrieUndWsa()

Uebergabe        : Nichts
Rueckgabe        : Nichts 
Autor            : Martin Bauer
Letzte Aenderung : 1.6.1995

Getestet         : JA , Resultat: fehlerfrei (1.6.1995)

Aufgabe:            
          Es sollen alle BitBloecke auf (X/Y)-Achsensymetrie
          uberprueft werden. Die Achsen beginnen hierbai in der
          Mitte des Bildes (-> gezoomtes Bild !). Desweiteren sollen
          die Wsa-werte berechnte werden.
           
****************************************************************/
void VergleicheAufSymetrieUndWsa()
{
 struct BitBlockListe *P1,*P2,*P3;  
 unsigned char wert1,wert2,wert3,wert4,hwert3,hwert2;
 long t,j,k,i,y1,y2;
 for (P1=SegmentBitBlockListeStartPtr;P1!=NULL;P1=P1->NextSegmentBlockPtr)  
 {  
  if (P1->Geloescht==0)
  {
   for (P2=P1->NextZeileInSegment;P2!=NULL;P2=P2->NextZeileInSegment)
   {     
    if (P2->Geloescht==0)
    {
     y1=P2->Obere_Zeile-((P2->Obere_Zeile-P2->y)*0.3);
     y2=P2->Untere_Zeile+(((P2->y+P2->dy)-P2->Untere_Zeile)*0.3);    
     for (P3=P2->NextPtrInZeile;P3!=NULL;P3=P3->NextPtrInZeile)
     {
      /* Ueberpruefe ob unter/uebergrosses Zeichen */
     
      P3->Obere_Zeile=-1; P3->Untere_Zeile=-1;
      if (P2->Obere_Zeile>=0)  if (P3->y<y1)          P3->Obere_Zeile=y1;
      if (P2->Untere_Zeile>=0) if ((P3->y+P3->dy)>y2) P3->Untere_Zeile=y2;
      /* Berechne die Wsa-Werte */
      BerechneWsaWerte(P3);
      if (P3->Geloescht==0)  
      {
       i=0; j=0; k=0;
       for (t=0;t<16;t++)         
       {
        wert1=P3->BildDaten[   (t<<1)];
        wert2=P3->BildDaten[1 +(t<<1)];
        wert3=P3->BildDaten[30-(t<<1)];
        wert4=P3->BildDaten[31-(t<<1)];
        hwert3=ByteIstGespiegelt[wert3];
        hwert2=ByteIstGespiegelt[wert2];
        i=i+ ((PunkteInByte[(wert1 & ( ~wert3)) | ((~wert1) &  wert3)]<<1) + (PunkteInByte[(wert2 & (~wert4 )) | ((~wert2) &  wert4)]<<1));
        k=k+ ((PunkteInByte[(wert1 & (~hwert2)) | ((~wert1) & hwert2)]<<1) + (PunkteInByte[(wert4 & (~hwert3)) | ((~wert4) & hwert3)]<<1));
        j=j+ (PunkteInByte[wert4]+  PunkteInByte[wert3]+ PunkteInByte[wert2]+ PunkteInByte[wert1]);
       }
       if (j==0) P3->Geloescht=1;
       if (j!=0) if (((i/(j+0.0))<0.15) || (i<9)) P3->XSpiegel=1;
       if (j!=0) if (((k/(j+0.0))<0.15) || (k<9)) P3->YSpiegel=1;
      }
     }
    }
   }
  }
 }
}

void SpeichereGelerntesZeichen(Ptr)
struct LearndListe  *Ptr;
{
 FILE *file;
 char *a;

 a=(char *)&(Ptr->Daten);
 file=fopen("LERNED.DAT","a+b");
 if (file==NULL) return;
 fwrite(a,1,sizeof(struct LearndDaten),file);
 fclose(file);
}

void LadeGelernteZeichen()
{
 long a;
 FILE *file;
 struct LearndListe *Ptr;
 
 file=fopen("LERNED.DAT","a+b");
 if (file==NULL) return;
 fseek(file,0,0);
 for (;;)
 {
  Ptr=(struct  LearndListe *)calloc(1,sizeof(struct  LearndListe));
  if (Ptr==NULL) MemoryError();
  Ptr->NextPtr=NULL; 
  a=fread(&(Ptr->Daten),1,sizeof(struct LearndDaten),file);
  if (a==sizeof(struct LearndDaten))
  {
   Ptr->NextPtr=ZeichenListeStart[Ptr->Daten.Klasse][Ptr->Daten.Elemente][Ptr->Daten.Flag];
                ZeichenListeStart[Ptr->Daten.Klasse][Ptr->Daten.Elemente][Ptr->Daten.Flag]=Ptr;
  } else
  {
   free(Ptr);
   break;
  }
 }
 fclose(file);
}

struct LearndListe  *Analy(Start,min,ErgPtr,ElementP)
 struct LearndListe  *Start;
 long *min;
 struct LearndListe  *ErgPtr;
 struct BitBlockListe *ElementP;
 {
 struct LearndListe  *P1,*P2;
 long wert,t;
 
 for (P1=Start;P1!=NULL;P1=P1->NextPtr)
 {
  P1->JumpPtr=P1->NextPtr;
  P1->Wert=Quadrat[100+P1->Daten.WsaAchsenWerte[0]-ElementP->WsaAchsenWerte[0]];
 }
 
 for (t=1;t<128;t++)
 { 
  P2=NULL;
  for (P1=Start;P1!=NULL;P1=P1->JumpPtr)
  {
   wert=Quadrat[100+P1->Daten.WsaAchsenWerte[t]-ElementP->WsaAchsenWerte[t]];
   P1->Wert+=wert; 
   if ((P2!=NULL) && (wert>MAXDIFFERENZ))
   {
    P2->JumpPtr=P1->JumpPtr;
   }
   P2=P1; 
  }
 }
 P1=Start;
 for (;P1!=NULL;P1=P1->JumpPtr)
 {
  if (P1->Wert<(*min)) { *min=P1->Wert; ErgPtr=P1; }
 }


 return(ErgPtr);
}

struct LearndListe  *AnalysiereAlleWerteEinesSegments(ElementP,Wert,F)
 struct BitBlockListe *ElementP;
 long *Wert;
 short int *F;
{
 long min;
 struct LearndListe *ErgPtr;
 unsigned short int Flag;
 
 
 Flag=0;
 if (ElementP->Elemente>15) ElementP->Elemente=15;
 if (ElementP->XSpiegel)       Flag  = 1;
 if (ElementP->YSpiegel)       Flag |= 2;
 if (ElementP->Untere_Zeile>=0) Flag |= 4;
 if (ElementP->Obere_Zeile>=0)  Flag |= 8;
  min=999999; ErgPtr=NULL;


                         ErgPtr=Analy(ZeichenListeStart[1+ElementP->Klasse][ElementP->Elemente][Flag],&min,NULL  ,ElementP);
                         ErgPtr=Analy(ZeichenListeStart[  ElementP->Klasse][ElementP->Elemente][Flag],&min,ErgPtr,ElementP);
 if (ElementP->Klasse>0) ErgPtr=Analy(ZeichenListeStart[ElementP->Klasse-1][ElementP->Elemente][Flag],&min,ErgPtr,ElementP);


 min=sqrt(min); 
 *Wert=min;
 *F=Flag;
 return(ErgPtr);
}


void WoerterbuchUndSemantikCheck()
{
 FILE *file,*file_o;
 unsigned char Puffer[100],Puffer1[100]; 
 char Text[500];
 long position,laenge,t,l,NOC,posi,last,max;
 NOC=1;
 position=0;
 posi=0; last=0; laenge=1;
 file=fopen("tmp.dat","rb");
 strcpy(Text,"tmp1.dat");
 if (ParaAktuell.UseEditor==0) { strcpy(Text,ParaAktuell.Dateiname); } 
 file_o=fopen(Text,"w");
 fseek(file,0,2);
 max=ftell(file);
 fseek(file,0,0);
 if (file==NULL) return;
 for (;;)
 {
  fseek(file,position,0);
  if (position>max) 
{ fclose(file); fclose(file_o); 

 if (ParaAktuell.UseEditor!=0)
 {
  printf("hello\n");
  sprintf(Text,"%s tmp1.dat &",ParaAktuell.Editor);
  system(Text);
 }

return;  }
  strcpy(Puffer1,Puffer);
  fread(Puffer,1,60,file);
  Puffer[59]=0;
  last=laenge-1;
  for (t=0;t<60;t++) 
  { 
   if (Puffer[t]==1) { fclose(file); fclose(file_o); 
 if (ParaAktuell.UseEditor!=0)
 {
  
  sprintf(Text,"%s tmp1.dat &",ParaAktuell.Editor);
  system(Text);
 }

return; }
   if (Puffer[t]<32) { Puffer[t]=0; break; } 
  }
  laenge=strlen(Puffer);
  if (laenge>0)
  {    
   /* Ab hier kommen die Semantik-checks ! */   
   if (0==SucheElement(Puffer))
   { /* Tja , nicht im woerterbuch , also Semantik-check */
    l=0;
    for (t=0;t<laenge;t++)
    {
     if ((
             ((Puffer[t]<'A') &&  (!((Puffer[t]>='0') && (Puffer[t]<='9'))) ) 
          || ((Puffer[t]>'Z') && (Puffer[t]<'a') )))  { l=1; laenge=t+1;Puffer[t+1]=0;}
    } 
    position+=laenge;
    if (laenge!=1)
    { 
     if (l) { Puffer[laenge-1]=0; laenge--; position--; }
     if (0==SucheElement(Puffer))
     { /* Waaa, nicht im Woerterbuch , also Semantik-Check !*/
      if (SematischerCheck(Puffer,strlen(Puffer))) 
      { 
       if (NOC==0) { fprintf(file_o," %s",Puffer); posi++; } else { fprintf(file_o,"%s",Puffer); }
       posi=posi+strlen(Puffer);
       NOC=0;
       if (posi>MAXANZAHL) { fprintf(file_o,"\n"); NOC=1; posi=0; }
      } else 
      {
       if (NOC==0) { fprintf(file_o," %s",Puffer); posi++; } else { fprintf(file_o,"%s",Puffer); }
       posi=posi+strlen(Puffer);
       NOC=0;
       if (posi>MAXANZAHL) { fprintf(file_o,"\n"); NOC=1;  posi=0; }
      }
     } else
     { /* Gefunden :=) */
      if (NOC==0) { fprintf(file_o," %s",Puffer); } else { fprintf(file_o,"%s",Puffer); }    
      NOC=0;
      if (posi>MAXANZAHL) { fprintf(file_o,"\n"); NOC=1;  posi=0;  }
     }
    } else
    { /* nur ein buchstabe ! :=)  */
      posi++;
      switch (Puffer[0])
      {
       case '0':  fprintf(file_o,"0"); break;
       case '1':  fprintf(file_o,"1"); break;            
       case '2':  fprintf(file_o,"2"); break;
       case '3':  fprintf(file_o,"3"); break;
       case '4':  fprintf(file_o,"4"); break;            
       case '5':  fprintf(file_o,"5"); break;
       case '6':  fprintf(file_o,"6"); break;
       case '7':  fprintf(file_o,"7"); break;            
       case '8':  fprintf(file_o,"8"); break;
       case '9':  fprintf(file_o,"9"); break;
       case 'ß':  fprintf(file_o,"8"); break;
       case 'B':  fprintf(file_o,"8"); break;
       case 'I':  fprintf(file_o,"1"); break;
       case 'l':  fprintf(file_o,"1"); break;            
       case 'Z':  fprintf(file_o,"7"); break;
       case 'G':  fprintf(file_o,"6"); break;
       case 'S':  fprintf(file_o,"5"); break;
       case '.':  if ((Puffer1[last]>='0') && (Puffer1[last]<='9'))  { fprintf(file_o,"."); posi++; } else { fprintf(file_o,".\n"); posi=0; NOC=1; } break;            
       case ',':  fprintf(file_o,","); break;
       case ';':  fprintf(file_o,";"); break;
       case ':':  fprintf(file_o,":"); break;            
       case '-':  fprintf(file_o,"-"); break;
       case '*':  fprintf(file_o,"*"); NOC=1; break;
       case '+':  fprintf(file_o,"+"); break;            
       case '/':  fprintf(file_o,"/"); break;
       case '(':  fprintf(file_o," ("); posi++; NOC=1; break;
       case ')':  fprintf(file_o,")"); break;            
       case '{':  fprintf(file_o," {"); posi++; NOC=1; break;
       case '}':  fprintf(file_o,"}"); break;
       case '[':  fprintf(file_o," ["); posi++; NOC=1; break;            
       case ']':  fprintf(file_o,"]"); break;
       case '&':  fprintf(file_o,"&"); posi++; break;
       case '$':  fprintf(file_o,"$"); break;
       case '¶':  fprintf(file_o,"¶"); break;
       case '!':  fprintf(file_o,"!\n"); posi=0; NOC=1; break;            
       case '?':  fprintf(file_o,"?\n"); posi=0;NOC=1; break;
       default :
       if (NOC==0) { fprintf(file_o," %s",Puffer); } else { fprintf(file_o,"%s",Puffer); }
       posi++;
       NOC=0;
       if (posi>MAXANZAHL) { fprintf(file_o,"\n"); NOC=1; posi=0;  }
      } /* switch */
    } /*t == 1*/
   }else
   {
    position+=laenge;
    posi=posi+laenge;
    if (NOC==0) { posi++; fprintf(file_o," %s",Puffer); } else { fprintf(file_o,"%s",Puffer); }
    NOC=0;
    if (posi>MAXANZAHL) { fprintf(file_o,"\n"); NOC=1; posi=0;  }
   }
  } else
  {
   position++;
  }   
 } 
}





/***************************************************************
Procedure        : leselong()

Typ              : Wegwerf-Routine !  

Aufgabe:         Wandle Intel-long in ein normales int um .   
               
****************************************************************/
unsigned long leselong(Ptr)
unsigned char *Ptr;
{
 unsigned long r;
 r=0;
 r=((unsigned char)*(Ptr+0))+(((unsigned char)*(Ptr+1))<<8);
 return(r);
}

