replaced libart with new polygon code
[swftools.git] / lib / gocr / ocr0n.c
1 /* ocr-engine numbers only */
2 /*
3 This is a Optical-Character-Recognition program
4 Copyright (C) 2000-2007 Joerg Schulenburg
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20  see README for EMAIL-address
21
22    OCR engine (c) Joerg Schulenburg
23    first engine: rule based --- numbers 0..9
24
25 */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 /* #include "pgm2asc.h" */
30 #include "ocr0.h"
31 #include "ocr1.h"
32 #include "gocr.h"
33
34 /* only for debugging and development */
35 #define IFV if(JOB->cfg.verbose&4)
36 #define MM {IFV fprintf(stderr,"\nDBG %c L%04d (%d,%d): ",(char)c_ask,__LINE__,box1->x0,box1->y0);}
37
38 /* the old debug mode (0.40) was only for a special char, for another char
39  *   code must be recompiled with C_ASK='char'
40  * new debug mode (0.41) explains why char is declined or accepted as ABC...
41  *   the output can be filtered by external scripts
42  *   ToDo: we could reduce output to filter string
43  */
44 #ifndef DO_DEBUG       /* can be defined outside */
45 #define DO_DEBUG 0     /* 0 is the default */
46 #endif
47
48 /* this macro is for debugging output: "if char is declined, why?" */
49 #if DO_DEBUG   /* 0=Work mode, 1=debugging mode */
50 // Setac: output, that char is choosen with a probability
51 // Break: output, why the char is not choosen
52 // MSG: debugging functions for char C_ASK, mostly messages
53 // DBG: definitions usefull only for debugging
54 #define Setac(box1,ac,ad)  { MM;IFV fprintf(stderr,"setac %d",ad);setac(box1,ac,ad); }
55 #define Break              { MM;IFV fprintf(stderr,"break"); break; }
56 #define MSG(x)             { MM;IFV x }
57 #define DBG(x) x
58 #else
59 #define Setac(box1,ac,ad)  setac(box1,ac,ad)
60 #define Break break
61 #define MSG(x)
62 #define DBG(x)
63 #endif
64
65 /* extern "C"{ */
66
67 // OCR engine ;)
68 wchar_t ocr0n(ocr0_shared_t *sdata){
69    struct box *box1=sdata->box1;
70    pix  *bp=sdata->bp;
71    int  d,x,y,x0=box1->x0,x1=box1->x1,y0=box1->y0,y1=box1->y1;
72    int  dx=x1-x0+1,dy=y1-y0+1,cs=sdata->cs;     // size
73    int  xa,xb,ya,yb, /* tmp-vars */
74         i1,i2,i3,i4,i,j;
75    int (*aa)[4]=sdata->aa;    /* corner-points, (x,y,dist^2,vector_idx) */
76    wchar_t bc=UNKNOWN;                          // best char
77    int  ad=0;           // propability 0..100
78    int hchar=sdata->hchar;      // char is higher than 'e'
79    int gchar=sdata->gchar;      // char has ink lower than m3
80    int dots=box1->dots;
81    // --- test 5 near S ---------------------------------------------------
82    for(ad=d=100;dx>2 && dy>4;){     // min 3x4
83       DBG( char c_ask='5'; )
84       if (sdata->holes.num > 1) Break; /* be tolerant */
85       if( num_cross(  dx/2,  dx/2,0,dy-1,bp,cs)!=3
86       &&  num_cross(5*dx/8,3*dx/8,0,dy-1,bp,cs)!=3 ) Break;
87
88       i1=loop(bp,dx-1,dy-1,dx,cs,0,LE);
89       i2=loop(bp,dx-1,dy-2,dx,cs,0,LE);
90       if (i2-i1 >= dx/4) Break; // ~{  5x7font
91
92       // get the upper and lower hole koords, y around dy/4 ???
93       x=5*dx/8;
94       y  =loop(bp,x,0,dy,cs,0,DO); if(y>dy/8) Break;
95       y +=loop(bp,x,y,dy,cs,1,DO); if(y>dy/4) Break;
96       i1 =loop(bp,x,y,dy,cs,0,DO)+y; if(i1>5*dy/8) Break;
97       i3=y=(y+i1)/2; // upper end can be shifted to the right for italic
98       x  =loop(bp,0,y,dx,cs,0,RI); if(x>4*dx/8) Break;
99       x +=loop(bp,x,y,dx,cs,1,RI); if(x>5*dx/8) Break;
100       i1 =loop(bp,x,y,dx,cs,0,RI); i1=(i1+2*x)/2; // upper center (i1,i3)
101       y=11*dy/16;
102       x  =loop(bp,dx-1  ,y,dx,cs,0,LE); if(x>dx/4) Break;
103       x +=loop(bp,dx-1-x,y,dx,cs,1,LE); if(x>dx/2) Break;
104       i2 =loop(bp,dx-1-x,y,dx,cs,0,LE); i2=dx-1-(i2+2*x)/2; // lower center x
105
106       MSG( fprintf(stderr,"i1,i3=%d,%d i2=%d (upper+lower center)",i1,i3,i2);)
107
108       y  =loop(bp,i1,0,dy,cs,0,DO);
109       y +=loop(bp,i1,y,dy,cs,1,DO);
110       y  =(3*y+i3)/4;
111       if( num_cross( i1, dx-1, y, y,bp,cs)>0 ){ /* S or serif5 ? */
112         y  =loop(bp,i1  ,i3,dy,cs,0,DO);
113         i  =loop(bp,i1-1,i3,dy,cs,0,DO);
114         if (y>i ) ad=99*ad/100; /* looks like S */
115         y  =loop(bp,i1  ,i3,dy,cs,0,UP);
116         i  =loop(bp,i1+1,i3,dy,cs,0,UP);
117         if (i<y ) ad=99*ad/100; /* looks like S */
118         x  =loop(bp,dx-1,0,dx,cs,0,LE);
119         i  =loop(bp,dx-1,1,dx,cs,0,LE);
120         if (x>i ) ad=99*ad/100; /* looks like S */
121         if( num_cross(   0, dx/2, dy-1, dy-1,bp,cs)>1 
122          && num_cross( dx/2,dx-1,    0,    0,bp,cs)>1 ) ad=98*ad/100; /* serifs */
123         if (loop(bp,0,dy-1,dx,cs,0,RI)==0) ad=98*ad/100; /* S or 7segment */
124         ad=99*ad/100;
125       }
126
127       for(y=dy/5;y<3*dy/4;y++) // right gap?
128       if( num_cross(i1,dx-1,y,y,bp,cs)==0 ) break;
129       if( y==3*dy/4 ) Break;
130
131       for(y=dy/4;y<=11*dy/16;y++) // left gap?
132       if( num_cross(0,i2,y,y,bp,cs)==0 ) break;
133       if( y>11*dy/16 ) Break;
134
135       // if( num_hole( x0, x1, y0, y1, box1->p,cs,NULL) > 0 ) break;
136       if (sdata->holes.num>0) Break;
137
138       // sS5 \sl z  left upper v-bow ?
139       for(x=dx,i=y=dy/4;y<dy/2;y++){
140         j=loop(bp,0,y,dx,cs,0,RI); if(j<x) { x=j; i=y; }
141       } y=i;
142       i1=loop(bp,0,   dy/16     ,dx,cs,0,RI);
143       i2=loop(bp,0,(y+dy/16)/2  ,dx,cs,0,RI);
144       i =loop(bp,0,(y+dy/16)/2+1,dx,cs,0,RI); if( i>i2 ) i2=i;
145       i3=loop(bp,0, y  ,dx,cs,0,RI);
146       i =loop(bp,0, y-1,dx,cs,0,RI); if( i<i3 ) i3=i;
147       if( 2*i2+1+dx/16 < i1+i3 ) Break;
148       
149       if( dy>=20 && dx<16 ) /* tall S */
150       if(  loop(bp,0,   dy/5     ,dx,cs,0,RI)
151          ==loop(bp,0,   dy/4     ,dx,cs,0,RI)
152          &&
153            loop(bp,0,   dy/10    ,dx,cs,0,RI)
154           >loop(bp,0,   dy/4     ,dx,cs,0,RI)
155          &&
156            loop(bp,0,       1    ,dx,cs,0,RI)
157           >loop(bp,0,   dy/4     ,dx,cs,0,RI)+1
158          &&
159            loop(bp,dx-1,    0    ,dx,cs,0,LE)
160           >loop(bp,dx-1,    1    ,dx,cs,0,LE) ) Break;
161
162       if( dy>=30 && dx>15 ) /* large S */
163       if(   loop(bp,dx/4,3*dy/10,dy,cs,1,DO)>0 ) // check start
164       if(   loop(bp,dx-2,3*dy/4 ,dy,cs,1,UP)>0 ) // check end
165       if( num_cross(dx/4,dx-2,3*dy/10,3*dy/4,bp,cs)==1 ) Break; // connected?
166
167       if( dy>17 && dx>9 ) /* S */
168       if(   loop(bp,   0,dy/2   ,dx,cs,0,RI)<dx/2
169         ||  loop(bp,   0,dy/2-1 ,dx,cs,0,RI)<dx/2 )
170       if(   loop(bp,dx/4,3*dy/10,dy,cs,1,DO)>0 ) // check start
171       if(   loop(bp,dx-2,2*dy/3 ,dy,cs,1,UP)>0 ) // check end
172       if(   loop(bp,   0,      dy/16,dx,cs,0,RI)
173         >=  loop(bp,dx-1, dy-1-dy/16,dx,cs,0,LE) ) ad=ad*98/100;
174       if(   loop(bp,dx-1,      dy/16,dx,cs,0,LE)
175         >=  loop(bp,   0, dy-1-dy/16,dx,cs,0,RI)
176        &&   loop(bp,dx-1,      dy/16,dx,cs,0,LE)
177         >=  loop(bp,   0,       dy-1,dx,cs,0,RI) ) ad=ad*98/100;
178
179       if ( gchar) ad=99*ad/100;
180       if (!hchar) ad=99*ad/100;
181       Setac(box1,(wchar_t)'5',ad);
182       if (ad==100) return '5';
183       break;
184       
185    }
186    // --- test 1 ---------------------------------------------------
187    for(ad=d=100;dy>4 && dy>dx && 2*dy>box1->m3-box1->m2;){     // min 3x4
188       DBG( char c_ask='1'; )
189       if( dots==1 ) Break;
190       if (sdata->holes.num > 1) Break; /* be tolerant */
191
192       if( num_cross(0, dx-1, 0  , 0  ,bp,cs) != 1
193        && num_cross(0, dx-1, 1  , 1  ,bp,cs) != 1 ) Break;
194       if( num_cross(0, dx-1,dy/2,dy/2,bp,cs) != 1 ) Break;
195       if( num_cross(0, dx-1,dy-1,dy-1,bp,cs) != 1
196        && num_cross(0, dx-1,dy-2,dy-2,bp,cs) != 1 ) Break;
197       /*  5x7  micr
198       
199                ooo
200           .$.  ooo
201           $@.   oo
202           .$.   oo
203           .@.  ooooo
204           .$.  ooooo
205           $@$  ooooo
206           
207        */
208
209       i4=0; // human font
210       if( num_cross(0, dx-1,3*dy/4,3*dy/4,bp,cs) != 2 ) { // except ocr-a
211         for( y=1; y<dy/2; y++ ){
212           if( num_cross(0, dx-1, y  , y  ,bp,cs) == 2 ) break;
213         } if (y>=dy/2) ad=98*ad/100;
214         for( i=dy/8,y=7*dy/16;y<dy-1 && i;y++ ){
215           if( num_cross(0, dx-1, y  , y  ,bp,cs) != 1 ) i--;
216         } if( dy>8 && !i ) Break;
217       } else {  // ocr-a-1
218        /*  @@@..
219            ..@..
220            ..@..
221            ..@..
222            ..@.@
223            ..@.@
224            @@@@@  */
225         i=  loop(bp,dx/2,0,dy,cs,0,DO);
226         if (loop(bp,dx/2,i,dy,cs,1,DO)<dy-1) Break;
227         i=  loop(bp,dx  -1,dy-1-dy/16,dx,cs,0,LE);
228         if (loop(bp,dx-i-1,dy-1-dy/16,dx,cs,1,LE)<dx-1) Break;
229         i=  loop(bp,0,dy/16,dx,cs,0,RI);
230         if (loop(bp,i,dy/16,dx,cs,1,RI)<dx/2) Break;
231         i4=1;
232       }
233
234       if( num_cross(0, dx-1, 0  , 0  ,bp,cs) > 1
235        && num_cross(0, dx-1, 1  , 1  ,bp,cs) > 1 ) Break; // ~/it_7
236
237       // calculate upper and lower mass center (without lower serif)
238
239       x =loop(bp,0,7*dy/8-1,dx,cs,0,RI);   i2=x;
240       x+=loop(bp,x,7*dy/8-1,dx,cs,1,RI)-1; i2=(i2+x)/2;
241
242       i1=loop(bp,dx-1  ,1+0* dy/4,dx,cs,0,LE); i1=dx-1-i1-(x-i2)/2;
243
244       x =(i1-i2+4)/8; i1+=x; i2-=x;
245       
246       if( get_line2(i1,0,i2,dy-1,bp,cs,100)<95 ) { // dont work for ocr-a-1
247         i1=loop(bp,dx-1  ,1+0* dy/4,dx,cs,0,LE); i1=dx-1-i1;
248         if( get_line2(i1,0,i2,dy-1,bp,cs,100)<95 ) Break;
249       }
250       // upper and lower width
251       x =loop(bp,(i1+i2)/2,dy/2,dx,cs,1,RI); i=x; i3=0;
252       for(y=0;y<7*dy/8;y++)
253         if( loop(bp,i1+y*(i2-i1)/dy, y,dx,cs,1,RI)-i > 1+dx/8 ) break;
254       if(y<7*dy/8) ad=98*ad/100; // serif or ocr-a-1 ?
255       if(y<6*dy/8) ad=99*ad/100; /*  MICR E-13B font Jan07 */
256       if(y<4*dy/8) Break;
257 // out_x(box1); printf(" i12=%d %d\n",i1,i2);
258       x =loop(bp,i2,dy-1,dx,cs,1,LE); j=x;
259       x =loop(bp,i2,dy-2,dx,cs,1,LE); if(x>j)j=x; i=j;
260       x =loop(bp,i2,dy-1,dx,cs,1,RI); j=x;
261       x =loop(bp,i2,dy-2,dx,cs,1,RI); if(x>j)j=x;
262       if(abs(i-j)>1+dx/8) i3|=1;
263       if(i3) Break;        
264 //       out_x(box1);printf(" 11 i=%d j=%d i2=%d dx=%d\n",i,j,i1,dx);
265       // get most left upper point (i,j)
266       for(i=dx,j=y=0;y<7*dy/16;y++){
267         x =loop(bp,0,y,dx,cs,0,RI); if(x<i) { i=x;j=y; }
268       }  
269       if ( i1-i<7*dx/16 ) ad=ad*98/100;
270       if ( i1-i<6*dx/16 ) ad=ad*98/100; // 4*dx/8 => 7*dx/16  MICR E-13B font
271       if ( i1-i<4*dx/16 ) Break;
272       x =loop(bp,0,dy/2,dx,cs,0,RI); // right distance
273       j =loop(bp,x,dy/2,dx,cs,1,RI); // thickness
274       if( j>x+(dy+16)/32 ) ad=98*ad/100; // ~l but  MICR E-13B font
275       x =loop(bp,0,0,dx,cs,0,RI); // straight line ???
276       j =loop(bp,0,1,dx,cs,0,RI);               if( j>x ) Break; // ~l
277       if( x==j ) j =loop(bp,0,dy/8,dx,cs,0,RI); if( j>x && !i4) Break;
278       if( x==j ) if(loop(bp,0,dy/4,dx,cs,0,RI)>x) {  // ~l
279         // check micr-1 first before taken as 'l'
280         if (loop(bp,dx-1,dy/8,dx,cs,0,LE)<=dx/4
281          && loop(bp,0,3*dy/4,dx,cs,1,RI)<dx-1) ad=97*ad/100;
282       }
283       x=j;
284 //      j =loop(bp,0,2,dx,cs,0,RI); if( j>=x ) Break; x=j; // ~l
285 //      j =loop(bp,0,   0,dx,cs,0,DO); if( !j  ) Break; // ~7
286       if( !hchar ) // ~ right part of n
287       if( loop(bp,dx-1,   1,dx,cs,0,LE)-dy/6
288         > loop(bp,dx-1,dy/4,dx,cs,0,LE)
289        || get_bw(x1+1,x1+2,y0,y0+dy/8,box1->p,cs,1)==1 ) Break; // Mai00
290       if( loop(bp,dx-1,3*dy/4,dx,cs,0,LE) > dx/2
291        && get_bw(x1-dx/4,x1,y1-1,y1,box1->p,cs,1)==1 ) Break; // ~z Jun00
292
293       i=loop(bp,  dx/8,0,dy,cs,0,DO);
294       for (y=dy,x=dx/2;x<3*dx/4;x++){ /* get upper end */
295         j=loop(bp,x,0,dy,cs,0,DO); if (j<y) { y=j; }
296       }
297       if(y<dy/2 && y+dy/16>=i) ad=97*ad/100; // ~\tt l ??? ocr-a_1
298
299       if(   loop(bp,   0,  dy/8,dx,cs,0,RI)
300        -(dx-loop(bp,dx-1,7*dy/8,dx,cs,0,LE)) > dx/4 ) Break; // ~/
301
302       i=    loop(bp,   0,      0,dy,cs,0,DO); // horizontal line?
303       if(dy>=12 && i>dy/8 && i<dy/2){
304         if(   loop(bp,dx-1,3*dy/16,dx,cs,0,LE)-dx/8
305              >loop(bp,dx-1,      i,dx,cs,0,LE) 
306          ||   loop(bp,dx-1,3*dy/16,dx,cs,0,LE)-dx/8
307              >loop(bp,dx-1,    i+1,dx,cs,0,LE)       ) Break; // ~t,~f
308         i= loop(bp,   0,dy-1-dy/32,dx,cs,0,RI);
309         x= loop(bp,   0,dy-2-dy/32,dx,cs,0,RI); if (i<x) x=i;
310         if( x-loop(bp,   0,    3*dy/4,dx,cs,0,RI)>dx/8 
311          &&   loop(bp,dx-1,    3*dy/4,dx,cs,0,LE)-dx/8
312              >loop(bp,dx-1,dy-1-dy/32,dx,cs,0,LE)    ) Break; // ~t
313         if(   loop(bp,   0,i-1,dx,cs,0,RI)>1 && dx<6) {
314           ad=99*ad/100; 
315           if ( loop(bp,dx-1,i-1,dx,cs,0,LE)>1 ) Break; // ~t
316         }
317       }
318
319       if (dx>8){
320         if (loop(bp,0,3*dy/4,dx,cs,0,RI)-
321             loop(bp,0,dy/2-1,dx,cs,0,RI)>dx/4) ad=95*ad/100; // ~3
322         if (loop(bp,dx-1,dy/2-1,dx,cs,0,LE)-
323             loop(bp,dx-1,3*dy/4,dx,cs,0,LE)>dx/8) ad=95*ad/100; // ~3
324         if (loop(bp,dx-1, dy/16,dx,cs,0,LE)-
325             loop(bp,dx-1,  dy/4,dx,cs,0,LE)>dx/8) ad=95*ad/100; // ~23
326       }
327       /* font 5x9 "2" recognized as "1" */
328       i=loop(bp,dx-1-dx/8,dy-1,dy,cs,0,UP);
329       if (i<=dy/4) {
330         i+=loop(bp,dx-1-dx/8,dy-1-i,dy,cs,1,UP);
331         if (i<=dy/4) {
332           i=loop(bp,dx-1-dx/8,dy-1-i,dy,cs,0,LE);
333           if (2*i>=dx && loop(bp,dx/4,0,dy,cs,0,DO)<dy/2) {
334             if (dx<17) ad=98*ad/100;
335             if (dx<9)  ad=97*ad/100;
336           }
337         }
338       }
339       
340       // looking for  ###
341       //              ..# pattern (its important, we dont want perp. lines as 1)
342       // ToDo: better check that we have exact one on top
343       for (i2=0,i=dx,y=0;y<dy/2;y++) {
344         j=loop(bp,0,y,dx,cs,0,RI); if (j<i) i=j;
345         if (j>i+dx/8) { break; }
346       } if (y>=dy/2) ad=95*ad/100;  // Feb07 care plates, right black border
347       
348       if (sdata->holes.num > 0) Break; // mini holes should be filtered
349       if (!box1->m3 && ad>98) ad=98; else {
350         if (!hchar) ad=99*ad/100;
351         if (box1->y0>box1->m2) ad=98*ad/100;
352         if (box1->y1<(1*box1->m2+3*box1->m3)/4) ad=98*ad/100;
353         if (box1->y1-box1->y0<(box1->m3-box1->m1)/2) ad=98*ad/100;
354         if ( gchar) ad=99*ad/100;
355       }
356
357       Setac(box1,(wchar_t)'1',ad);
358       break;
359    }
360    // --- test 2 old pixelbased - remove! -----------------------------
361 #ifdef Old_pixel_based  
362    for(ad=d=100;dx>2 && dy>4;){     // min 3x4
363       DBG( char c_ask='2'; )
364       if (sdata->holes.num > 1) Break; /* be tolerant */
365       if( get_bw(x0+dx/2, x0+dx/2 , y1-dy/5, y1     ,box1->p,cs,1) != 1 ) Break;
366       if( get_bw(x0+dx/2, x0+dx/2 , y0     , y0+dy/5,box1->p,cs,1) != 1 ) Break;
367       if( get_bw(x0+dx/8, x1-dx/3 , y1-dy/3, y1-dy/3,box1->p,cs,1) != 1 ) Break;
368
369       if( get_bw(x1-dx/3, x1      , y0+dy/3 , y0+dy/3,box1->p,cs,1) != 1 ) Break;
370       if( get_bw(x0     , x0+dx/ 8, y1-dy/16, y1     ,box1->p,cs,1) != 1 ) Break;
371       if( num_cross(x0, x1-dx/8, y0+dy/2, y0+dy/2,box1->p,cs) != 1 ) Break;
372       if( get_bw(x0, x0+dx/9 , y0       , y0       ,box1->p,cs,1) == 1
373        && get_bw(x0, x0+dx/2 ,y0+3*dy/16,y0+3*dy/16,box1->p,cs,1) == 1 ) Break;
374       if( get_bw(x0, x0+dx/9 , y0       , y0       ,box1->p,cs,1)
375        != get_bw(x1-dx/9, x1 , y0       , y0       ,box1->p,cs,1) )
376       { if (dx<6 && dy<9) ad=99*ad/100; else Break; }
377       // out_x(box1);
378
379       for( x=x0+dx/4;x<x1-dx/6;x++ )            // C
380       if( num_cross( x, x, y0, y0+dy/2,box1->p,cs) == 2 ) break;
381       if( x>=x1-dx/6 ) Break;
382
383       for( x=x0+dx/4;x<x1-dx/6;x++ )            // C, but acr-a
384       if( num_cross( x, x, y0+3*dy/8,y1,box1->p,cs) == 2 ) break;
385       if( x>=x1-dx/6 ) Break;
386
387       for(i=1,y=y0;y<y0+dy/2;y++ )
388       if( num_cross( x0, x1, y, y,box1->p,cs) == 2 ) i=0;
389       if( i ) ad=99*ad/100; // ToDo: ocr-a-2 should have 100%
390
391       for(i=1,y=y0+dy/5;y<y0+3*dy/4;y++ )
392       if( get_bw( x0, x0+dx/3, y, y,box1->p,cs,1) == 0 ) i=0;
393       if( i ) Break;
394
395       x=x1-dx/3,y=y1; /* center bottom */
396       turmite(box1->p,&x,&y,x0,x1,y0,y1,cs,UP,ST); if( y<y1-dy/5 ) Break;
397       turmite(box1->p,&x,&y,x0,x1,y0,y1,cs,ST,UP); if( y<y1-dy/4 ) ad=99*ad/100;
398                                                    if( y<y1-dy/3 ) Break;
399       turmite(box1->p,&x,&y,x0,x1,y0,y1,cs,UP,ST); if( y<y0+dy/3 ) Break; y++;
400       turmite(box1->p,&x,&y,x0,x1,y0,y1,cs,RI,ST);
401       if( x<x1 ){ x--; // hmm thick font and serifs
402         turmite(box1->p,&x,&y,x0,x1,y0,y1,cs,UP,ST); if( y<y0+dy/2 ) Break; y++;
403         turmite(box1->p,&x,&y,x0,x1,y0,y1,cs,RI,ST);
404         if( x<x1 ) Break;
405       }
406
407       // test ob rechte Kante ansteigend
408       for(x=0,y=dy/18;y<=dy/3;y++){ // rechts abfallende Kante/Rund?
409         i=loop(box1->p,x1,y0+y,dx,cs,0,LE); // use p (not b) for broken chars
410         if( i<x ) break;        // rund
411         if( i>x ) x=i;
412       }
413       if (y>dy/3 ) Break;       // z
414
415       // hole is only allowed in beauty fonts
416       // if( num_hole( x0, x1,      y0,      y1,box1->p,cs,NULL) > 0 ) // there is no hole
417       // if( num_hole( x0, x0+dx/2, y0, y0+dy/2,box1->p,cs,NULL) == 0 ) // except in some beauty fonts
418       if (sdata->holes.num>0)
419       if (sdata->holes.hole[0].x1 >= dx/2 || sdata->holes.hole[0].y1 >= dy/2)
420         Break;
421
422       i1=loop(bp,dx-1-dx/16,0,dy,cs,0,DO);  // Jul00
423       i2=loop(bp,     dx/ 2,0,dy,cs,0,DO); if( i2+dy/32>=i1 ) Break; // ~z
424       i1=loop(bp,dx-1,dy-3*dy/16,dx,cs,0,LE);
425       i2=loop(bp,   0,dy-3*dy/16,dx,cs,0,RI); if( i2>i1 ) ad=98*ad/100; // ~i
426       if (dots) ad=98*ad/100; // i
427       if (loop(bp,dx-1,dy-1-dy/16,dx,cs,0,LE)>dx/4) ad=96*ad/100; // \it i
428
429       if ((!hchar) && box1->m4!=0) ad=80*ad/100;
430       Setac(box1,(wchar_t)'2',ad);
431       if (ad==100) return '2';
432       break;
433    }
434 #endif
435    // --- test 2 new edge based v0.44 --------------------------------------
436    for(ad=d=100;dx>2 && dy>4;){     // min 3x4
437      // rewritten for vectors 0.42
438       int ld, i1, i2, i3, i4, i5, i6, i7;  // line derivation + corners
439       DBG( wchar_t c_ask='2'; )
440       if (sdata->holes.num > 0) Break; /* no hole */
441       /* half distance to the center */
442       d=2*sq(128/4);
443       /* now we check for the lower ends, must be near to the corner */
444       if (aa[1][2]>d/4) Break;  /* [2] = distance, ~7... */
445       if (aa[2][2]>d/2) Break;  /* [2] = distance, ~r... */
446       if (aa[0][2]>d/1) Break;  /* [2] = distance, ~d... */
447       if (aa[3][2]>d/1) Break;  /* [2] = distance, ~bhk... */
448       /* searching for 4 notches between neighbouring ends */
449
450 /*
451     type A       B  
452                     
453         1OOO    OO  
454            2   1  2 <- 6 
455    7->  OOOO     O  
456         O       O   <- 5
457         3OO4   3OO4    
458 */
459
460       /* get a point on the inner low left side of the J */
461       i =box1->num_frame_vectors[0] - 1;
462       /* rightmost point on upper left side */
463       i2=nearest_frame_vector(box1, aa[0][3], aa[1][3], x1+dx, y0+dy/4);
464       /* upper leftmost vector */
465       i1=nearest_frame_vector(box1, aa[0][3], i2, x0-dx, (y0+y1)/2);
466       i3=aa[1][3];
467       /* low leftmost vector */
468       i5=nearest_frame_vector(box1, aa[2][3], aa[3][3], x0, y1);
469       /* low mostright vector */     
470       i4=nearest_frame_vector(box1, aa[1][3], i5, x1+dx, y1);
471       /* next local max_x-point after i5 */
472       i6=i5;
473       for (i=i5;i!=aa[0][3];i=(i+1)%box1->num_frame_vectors[0]) {
474         if (box1->frame_vector[ i][0]
475            >box1->frame_vector[i6][0]) i6=i; // get next maximum
476         if (box1->frame_vector[ i][0]<x0+dx/3
477          && box1->frame_vector[ i][1]<y0+dy/3
478          && box1->frame_vector[i6][0]>x0+dx/2) break; // 5
479       }
480       /* which type? ToDo: have a more sure algorithm */     
481       i7=nearest_frame_vector(box1, i2, i3, x0-dx/8, (y0+y1)/2);
482       if (box1->frame_vector[i7][0]<=x0+  dx/4
483        && box1->frame_vector[i7][1]<=y0+2*dy/3) {
484         MSG(fprintf(stderr,"7-segment-type");)
485       } else { /* regular-book-type */
486         if (aa[3][0]>=x1-dx/8
487          && aa[3][1]<=y0+dy/8) ad=99*ad/100;
488         if (aa[0][0]<=x0+dx/8
489          && aa[0][1]<=y0+dy/8) ad=99*ad/100;
490         if (aa[3][2]<=aa[1][2]) ad=97*ad/100;
491       }
492       // ToDo: output no=(x,y)
493       MSG(fprintf(stderr,"i1-7 %d %d %d %d %d %d %d",i1,i2,i3,i4,i5,i6,i7);)
494       if (i5==i6) Break; // ~+
495       
496       if (box1->frame_vector[i5][1]
497          -box1->frame_vector[i6][1]<dy/4) Break; // ~5
498       if (box1->frame_vector[i1][1]>y0+dy/2) Break; // not to low
499       if (box1->frame_vector[i1][0]>x0+dx/8) Break;
500       if (box1->frame_vector[i2][1]>(y0+  y1)/2) Break; 
501       if (box1->frame_vector[i2][1]>(5*y0+3*y1)/8) ad=99*ad/100; 
502       if (box1->frame_vector[i2][0]<(x0+x1+1)/2) Break;  // fat tiny fonts?
503       if (box1->frame_vector[i2][0]<(x0+2*x1)/3) ad=99*ad/100;
504       if (box1->frame_vector[i3][0]>(3*x0+x1)/4) Break;
505       if (box1->frame_vector[i3][0]>(7*x0+x1)/8) ad=99*ad/100;
506       if (box1->frame_vector[i3][1]<(y0+3*y1)/4) Break;
507       if (box1->frame_vector[i3][1]>(y0+7*y1)/8) ad=99*ad/100;
508       /* check lower leftmost point from right side */
509       if (box1->frame_vector[i5][0]>(x0+2*x1)/3) Break;
510       if (box1->frame_vector[i5][0]>(x0+  x1)/2) ad=98*ad/100;
511       if (box1->frame_vector[i5][0]>(2*x0+x1)/3) ad=99*ad/100;
512       if (box1->frame_vector[i5][1]<(3*y0+5*y1)/8) Break;
513       if (box1->frame_vector[i5][1]<(y0+3*y1)/4) ad=99*ad/100;
514       if (box1->frame_vector[i6][1]>(y0+2*y1)/3) Break;
515       if (box1->frame_vector[i6][1]>(y0+  y1)/2) ad=99*ad/100;
516       if (box1->frame_vector[i6][0]<(x0+3*x1)/4) Break;
517       if (box1->frame_vector[i6][0]<(x0+7*x1)/8) ad=99*ad/100;
518  
519       /* check for zZ */
520      
521       /* check if lower left and right points are joined directly */
522       ld=line_deviation(box1, i3, i4);
523       MSG(fprintf(stderr," i1-i2 %d %d dist= %d/%d",i1,i2,ld,2*sq(1024/4));)
524       if (ld >2*sq(1024/4)) Break;
525       if (ld >  sq(1024/4)) ad=99*ad/100;
526
527       if (box1->m3) {
528         if(!hchar){ ad=99*ad/100; }
529         if( gchar){ ad=99*ad/100; }
530       } else { if (ad==100) ad=99; } /* not 100% sure */
531       Setac(box1,'2',ad);
532       if (ad==100) return '2';
533       break;
534    }
535    // --- test 3 -------
536    for(ad=d=100;dx>3 && dy>4;){ // dy<=dx nicht perfekt! besser mittleres
537                                 // min-suchen fuer m
538       DBG( char c_ask='3'; )
539       if (sdata->holes.num > 1) Break; /* be tolerant */
540       // if( get_bw(x0+dx/2,x0+dx/2,y0,y0+dy/4,box1->p,cs,1) == 0 ) Break; // ~4
541       // if( get_bw(x0+dx/2,x0+dx/2,y1-dy/8,y1,box1->p,cs,1) == 0 ) Break; // ~4
542       // if( num_cross(x0+dx/2,x0+dx/2,y0     ,y1,box1->p,cs) < 2 ) Break;
543       // if( num_cross(x0+dx/4,x0+dx/4,y1-dy/2,y1,box1->p,cs) == 0 ) Break;
544       if( get_bw(dx/2,dx/2,        0,dy/6,bp,cs,1) == 0 ) Break; // ~4
545       if( get_bw(dx/2,dx-1,     dy/6,dy/6,bp,cs,1) == 0 ) Break; // ~j
546       if( get_bw(dx/2,dx/2,dy-1-dy/8,dy-1,bp,cs,1) == 0 ) Break; // ~4
547       if( num_cross(dx/2,dx/2,0        ,dy-1,bp,cs) < 2          // normal
548        && num_cross(dx/3,dx/3,0        ,dy-1,bp,cs) < 2 ) Break; // fat LCD
549       if( num_cross(dx/4,dx/4,dy-1-dy/2,dy-1,bp,cs) == 0 ) Break;
550       if( loop(bp,dx/2,  0   ,dy,cs,0,DO)>dy/4 ) Break;
551       if( loop(bp,dx/2,  dy-1,dy,cs,0,UP)>dy/4 ) Break;
552       if( loop(bp,dx-1,  dy/3,dy,cs,0,LE)>dy/4 /* 3 with upper bow */
553        && loop(bp,dx-1,  dy/8,dy,cs,0,LE)>dy/4 /* 3 with horizontal line */
554        && loop(bp,dx/4,  dy/8,dy,cs,1,RI)<dy/2 ) Break;
555       if( loop(bp,dx-1,2*dy/3,dy,cs,0,LE)>dy/4 ) Break;
556       if( loop(bp,dx-1,3*dy/4,dy,cs,0,LE)>dy/2 ) Break; // ~2 Feb06
557       if( loop(bp,dx-1,7*dy/8,dy,cs,0,LE)>dy/2 ) Break; // ~2 Feb06
558       // search upper right half circle
559       for( i3=x=0,i1=y=dy/5;y<dy/2;y++ ){
560         i=loop(bp,0,y,dx,cs,0,RI);
561         if (i>x) { i3=x=i; i1=y; }
562       } i3--; if (i3<dx/3 && i3+1+loop(bp,i3+1,i1,dx,cs,1,RI)<3*dx/4) Break;
563       if (loop(bp,dx-1,i1,dx,cs,0,LE)>1+dx/8) ad=ad*99/100; // ~1 with a pixel
564       // search lower right half circle
565       for( i4=x=0,i2=y=dy-1-dy/8;y>=dy/2;y-- ){
566         i=loop(bp,0,y,dx,cs,0,RI);
567         if( i>x ) { i4=x=i;i2=y; }
568       } i4--; if(i4<dx/3 && i4+1+loop(bp,i4+1,i2,dx,cs,1,RI)<3*dx/4) Break;
569       if (loop(bp,dx-1,i2,dx,cs,0,LE)>1+dx/8) ad=ad*99/100; // ~1 with a pixel
570
571       for( x=xa=0,ya=y=dy/4;y<3*dy/4;y++ ){  // right gap, not on LCD-font
572         i=loop(bp,dx-1,y,dx,cs,0,LE);
573         if (i>=xa) { xa=i;ya=y;x=xa+loop(bp,dx-1-xa,y,dx,cs,1,LE); }
574       } if (dy>3*dx) if (xa<2 && x-xa<dx/2) Break; // ~]
575       if (xa>1+dx/8 // noLCD
576         && xa<=loop(bp,dx-1,i2,dx,cs,0,LE)) ad=ad*99/100; // ~1 with a pixel
577       if (xa>1+dx/8 // noLCD
578         && xa<=loop(bp,dx-1,i1,dx,cs,0,LE)) ad=ad*99/100; // ~1 with a pixel
579       
580
581       if( get_bw(i3,i3,i1,i2  ,bp,cs,1) != 1 ) Break;
582       if( get_bw(i4,i4,i1,i2  ,bp,cs,1) != 1 ) Break;
583       if( get_bw(i3,i3,0 ,i1  ,bp,cs,1) != 1 ) Break;
584       if( get_bw(i4,i4,i1,dy-1,bp,cs,1) != 1 ) Break;  // m like
585       // hole is only allowed in beauty fonts
586       // if( num_hole( x0, x1,      y0,      y1,box1->p,cs,NULL) > 0 ) // there is no hole
587       // if( num_hole( x0, x0+dx/2, y0, y0+dy/2,box1->p,cs,NULL) == 0 ) // except in some beauty fonts
588       if (sdata->holes.num>0)
589       if (sdata->holes.hole[0].x1 >= dx/2 || sdata->holes.hole[0].y1 >= dy/2)
590         Break;
591       Setac(box1,(wchar_t)'3',ad);
592       if (ad==100) return '3';
593       break;
594    }
595    // --- test 4 --------------------------------------------------- 25Nov06
596    for(ad=d=100;dy>3 && dx>2;){     // min 3x4 ~<gA',
597      // rewritten for vectors 0.42
598       int ld, i1, i2, i3, i4, i5, i6, i7;  // line derivation + corners
599       DBG( wchar_t c_ask='4'; )
600       if (sdata->holes.num > 1) Break; /* no or one hole */
601       /* half distance to the center */
602       d=2*sq(128/4);
603       /* now we check for the lower left end, must be far away */
604       if (aa[1][2]<d/8) Break;  /* [2] = distance, ~ABDEF... */
605       /* searching for 4 notches between neighbouring ends */
606
607 /*
608     type A       B      C      D
609                      
610         1  5   1        1      1     
611         O  O   O       O5     O5
612         2OO3   O 5    O O    O O   <- 7 6
613            O   2O3O  2OO3   2OO3O
614            4     4      4      4
615 */
616
617       /* Warning: aa0 can be left upper or left lower point for type B */
618       /* get a point on the inner low left side of the J */
619       i =box1->num_frame_vectors[0] - 1;
620       /* leftmost upper point */
621       i1=nearest_frame_vector(box1, 0, i, x0, y0-dy);
622       /* lowest from leftmost vector can be very low (20/23) */
623       i2=nearest_frame_vector(box1, 0, i, x0-2*dx, (y0+7*y1)/8);
624       /* lowest vector */
625       i4=nearest_frame_vector(box1, 0, i, (x0+2*x1)/3, y1+dy);
626       /* right center crossing point */     
627       i3=nearest_frame_vector(box1, i2, i4, x1, (3*y0+y1)/4);
628       /* get a point on the outer right side below top serif */
629       /* next local max_y-point after i4 */
630       i5=i4;
631       for (i=i4;i!=i2;i=(i+1)%box1->num_frame_vectors[0]) {
632         if (box1->frame_vector[ i][1]
633            <box1->frame_vector[i5][1]) i5=i; // get next maximum
634         if (box1->frame_vector[ i][1]
635            >box1->frame_vector[i5][1]+1) break; // break after maximum
636         if (box1->frame_vector[ i][0]<x0+dx/4) break; // type A B
637       }
638       if (box1->num_frames>1) {  // type C D
639         i = box1->num_frame_vectors[0] - 1; // end outer loop
640         j = box1->num_frame_vectors[1] - 1; // end inner loop
641         i6=nearest_frame_vector(box1, i+1, j, x1, y1);
642         i7=nearest_frame_vector(box1, i+1, j, x0, y1);
643         if (box1->frame_vector[i1][0]
644            -box1->frame_vector[i2][0]<dx/4+1) ad=96*ad/100; // ~4x6q
645         i =nearest_frame_vector(box1, i+1, j, x0, y0); // top left
646         MSG(fprintf(stderr,"triangle type top-left i=%d",i);)
647         if (box1->frame_vector[i ][0]-x0<dx/4+1
648          && box1->frame_vector[i ][1]-y0<dy/4+1
649          && dx>7) ad=97*ad/100; // q
650         
651       } else {  // type A B
652         i6=nearest_frame_vector(box1, i5, i1, (x0+3*x1)/4, y1-dy/8);
653         i7=nearest_frame_vector(box1, i5, i1,  x0        , y1-dy/8);
654         MSG(fprintf(stderr,"open type");)
655       }
656       // ToDo: output no=(x,y)
657       MSG(fprintf(stderr,"i1-7 %d %d %d %d %d %d %d",i1,i2,i3,i4,i5,i6,i7);)
658       if (i5==i6) Break; // ~+
659       
660       if (box1->frame_vector[i1][1]>y0+dy/8) Break; // not to low
661       if (box1->frame_vector[i2][1]
662          -box1->frame_vector[i1][1]<dy/2) Break;
663       if (box1->frame_vector[i3][0]
664          -box1->frame_vector[i2][0]<dx/4) Break;
665       if (abs(box1->frame_vector[i3][1]
666              -box1->frame_vector[i2][1])>dy/4) Break;
667       if (box1->frame_vector[i2][0]>x0+dx/8) Break;
668       if (box1->frame_vector[i2][1]>y1-dy/8) Break;
669       if (box1->frame_vector[i4][1]
670          -box1->frame_vector[i2][1]<dy/8) Break;
671       if (box1->frame_vector[i4][1]
672          -box1->frame_vector[i2][1]<dy/6) ad=99*ad/100;
673       /* min. distance of the horizontal bar to the ground */
674       if (box1->frame_vector[i4][1]
675          -box1->frame_vector[i3][1]<1+dy/16) Break;
676       if (box1->frame_vector[i4][1]
677          -box1->frame_vector[i3][1]<dy/6) ad=99*ad/100; /* tall chars */
678       if (box1->frame_vector[i4][1]
679          -box1->frame_vector[i3][1]<dy/8) ad=99*ad/100;
680       if (box1->frame_vector[i4][1]<y1-1-dy/8) Break;
681       if (box1->frame_vector[i3][0]<x0+dx/4) Break;
682       if (box1->frame_vector[i3][0]<x0+dx/2) ad=98*ad/100;
683       /* on very tall chars the i3 point can be near to the groundline */
684       if (box1->frame_vector[i3][1]>y1-1) Break;
685       if (box1->frame_vector[i3][1]>y1-dy/16) Break;
686       if (box1->frame_vector[i3][1]>=y1) Break; // ~5x5#
687       if (box1->frame_vector[i5][0]<x0+dx/3) Break;
688       /* upper end of right vertical line */
689       if (box1->frame_vector[i5][1]>y0+2*dy/3) Break;
690       if (box1->frame_vector[i6][1]
691          -box1->frame_vector[i5][1]<1+dy/16) Break;
692       if (box1->frame_vector[i6][0]<x0+dx/3) Break;
693       if (box1->frame_vector[i7][0]>x0+dx/2) Break;
694       if (box1->frame_vector[i7][0]>x0+dx/3) ad=ad*99/100;
695       if (box1->frame_vector[i6][1]<y0+dy/3) Break;
696       if (box1->frame_vector[i6][0]<x0+dx/2) ad=96*ad/100; // ~ 42
697       if (box1->frame_vector[i6][0]<aa[2][0]-dx/2
698                      && aa[2][1]>=y1-1-dy/8) ad=96*ad/100; // ~ 42
699       if (box1->frame_vector[i7][1]<y0+dy/3) Break;
700       if (abs(box1->frame_vector[i3][1]
701              -box1->frame_vector[i2][1])>dy/4) Break;
702       
703       /* check if upper left and lower left points are joined directly */
704       ld=line_deviation(box1, i1, i2);
705       MSG(fprintf(stderr," i1-i2 %d %d dist= %d/%d",i1,i2,ld,2*sq(1024/4));)
706       if (ld >2*sq(1024/4)) Break;
707       /* check if lower right and upper right points are joined directly */
708       ld=line_deviation(box1, i2, i3);
709       MSG(fprintf(stderr," i2-i3 %d %d dist= %d/%d",i2,i3,ld,2*sq(1024/4));)
710       if (ld >  sq(1024/4)) Break;
711       /* check if lower right and upper right points are joined directly */
712       ld=line_deviation(box1, i3, i4);
713       MSG(fprintf(stderr," i3-i4 %d %d dist= %d/%d",i3,i4,ld,2*sq(1024/4));)
714       if (ld >  sq(1024/4)) Break;
715       /* check if lower right and upper right points are joined directly */
716       ld=line_deviation(box1, i6, i7);
717       MSG(fprintf(stderr," i6-i7 %d %d dist= %d/%d",i6,i7,ld,2*sq(1024/4));)
718       if (ld >2*sq(1024/4)) Break;
719
720       // 4 exists as gchar and ~gchar
721       if(!hchar){ ad=99*ad/100; }
722       Setac(box1,'4',ad);
723       break;
724    }
725 #ifdef Old_pixel_based 
726    // --- old test 4 pixelbased ------- remove!
727    for(ad=d=100;dx>3 && dy>5;){     // dy>dx, min 4x6 font
728       DBG( char c_ask='4'; )
729       if (sdata->holes.num > 2) Break; /* be tolerant */
730       if (sdata->holes.num > 1) ad=97*ad/100;
731       // upper raising or vertical line
732       if( loop(bp,0   ,3*dy/16,dx,cs,0,RI)
733         < loop(bp,0   ,2*dy/4 ,dx,cs,0,RI)-dx/8 ) Break;
734       // search for a vertical line on lower end
735       for (y=0;y<dy/4;y++)
736        if( loop(bp,0   ,dy-1-y,dx,cs,0,RI)
737          + loop(bp,dx-1,dy-1-y,dx,cs,0,LE) >= dx/2 ) break;
738       if (y>=dy/4) Break;
739       if( loop(bp,0   ,dy-1-dy/8,dx,cs,0,RI) <  dx/4 ) Break;
740       // --- follow line from (1,0) to (0,.7)
741       y=0; x=loop(bp,0,0,dx,cs,0,RI);
742       if (x<=dx/4) {  // ocr-a-4
743         i=loop(bp,0,dy/4,dx,cs,0,RI); if (i>dx/4) Break;
744         i=loop(bp,i,dy/4,dx,cs,1,RI); if (i>dx/2) Break;
745         j=loop(bp,i,dy/4,dy,cs,0,DO)+dy/4; if (j>7*dy/8) Break;
746       }
747       turmite(bp,&x,&y,0,dx-1,0,dy-1,cs,DO,LE); if( x>=0 ) Break;
748
749       y=loop(bp,0,0,dy,cs,0,DO);
750       if( (y+loop(bp,0,y,dy,cs,1,DO)) < dy/2 ) Break;
751       if( get_bw(x0   , x0+3*dx/8, y1-dy/7, y1-dy/7,box1->p,cs,1) == 1 ) Break;
752       if( get_bw(x0+dx/2, x1     , y1-dy/3, y1-dy/3,box1->p,cs,1) != 1 ) Break;
753       if( get_bw(x0+dx/2, x0+dx/2, y0+dy/3, y1-dy/5,box1->p,cs,1) != 1 ) Break;
754       i=loop(bp,bp->x-1,  bp->y/4,dx,cs,0,LE);
755       if( i > loop(bp,bp->x-1,2*bp->y/4,dx,cs,0,LE)+1
756        && i > loop(bp,bp->x-1,3*bp->y/8,dx,cs,0,LE)+1 ) Break;
757       if (loop(bp,0,0,dx,cs,0,RI)>dx/4) {
758         for(i=dx/8+1,x=0;x<dx && i;x++){
759           if( num_cross(x ,x   ,0  ,dy-1, bp,cs) == 2 ) i--;
760         } if( i ) Break;
761       }
762       for(i=dy/6+1,y=dy/4;y<dy && i;y++){
763         if( num_cross(0 ,dx-1,y  ,y   , bp,cs) == 2 ) i--;
764       } if( dy>15 && i ) Break;
765       for(i=dy/10+1,y=dy-1-dy/4;y<dy && i;y++){
766         if( num_cross(0   ,dx-1,y ,y  , bp,cs) == 1 )
767         if( num_cross(dx/2,dx-1,y ,y  , bp,cs) == 1 ) i--;
768       } if( i ) Break;
769       // i4 = num_hole ( x0, x1, y0, y1,box1->p,cs,NULL);
770       // ToDo:
771       //   - get start and endpoint of left edge of left vert. line
772       //       and check if that is an streight line
773       //   - check the right edge of the inner hole (if there) too
774       i4 = sdata->holes.num;
775       if (sdata->holes.num >0) { // ~q
776         i = loop(bp,0,dy/16,dx,cs,0,RI);
777         if (i < dx/3) Break;
778         if (i < dx/2) ad=98*ad/100; // hole?
779         if ( loop(bp,     0,dy-1,dy,cs,0,UP)
780             -loop(bp,dx/8+1,dy-1,dy,cs,0,UP)>dy/16) ad=97*ad/100;
781       }
782       // thickness of left vertical line
783       for (j=y=0;y<dy/6;y++) {
784         i=loop(bp,dx-1  ,y,dx,cs,0,LE);
785         i=loop(bp,dx-1-i,y,dx,cs,1,LE); if (i>j) j=i;
786       }
787       if (j>=dx/2) ad=98*ad/100; // ~q handwritten a (or very thinn 4)
788       // ToDo: check y of masscenter of the hole q4
789       
790       if( i4 ) if( dx > 15 )
791       if( loop(bp,  dx/2,   0,dy,cs,0,DO)<dy/16
792        && loop(bp,  dx/4,   0,dy,cs,0,DO)<dy/8
793        && loop(bp,3*dx/4,   0,dy,cs,0,DO)<dy/8
794        && loop(bp,  dx/4,dy-1,dy,cs,0,UP)<dy/8
795        && loop(bp,  dx/2,dy-1,dy,cs,0,UP)<dy/8
796        && loop(bp,3*dx/4,dy-1,dy,cs,0,UP)<dy/4 ) Break; // ~9
797
798       i =loop(bp,dx-1  ,dy-1,dx,cs,0,LE); // ~9
799       i+=loop(bp,dx-1-i,dy-1,dx,cs,1,LE);
800       if( i>3*dx/4
801        && i-loop(bp,dx-1,dy-1-dy/8,dx,cs,0,LE)>dx/4 ) Break;
802        
803       i =loop(bp,dx-1-dx/4,dy-1,dx,cs,0,UP);
804       if (i>  dy/2) ad=97*ad/100;
805       if (i>3*dy/4) ad=97*ad/100;  /* handwritten n */
806
807       if( num_cross(0 ,dx-1,dy/16 ,dy/16  , bp,cs) == 2 // ~9
808        && loop(bp,dx-1,dy/16        ,dx,cs,0,LE)>
809           loop(bp,dx-1,dy/16+1+dy/32,dx,cs,0,LE) ) Break;
810       if (         !hchar) ad=99*ad/100;
811       if (gchar && !hchar) ad=98*ad/100; // ~q
812       Setac(box1,(wchar_t)'4',ad);      
813       if (ad>99) bc='4';
814       break;
815    }
816 #endif
817    // --- test 6 ------- ocr-a-6 looks like a b  :(
818    for(ad=d=100;dx>3 && dy>4;){     // dy>dx
819       DBG( char c_ask='6'; )
820       if (sdata->holes.num > 2) Break; /* be tolerant */
821       if( loop(bp,   0,  dy/4,dx,cs,0,RI)>dx/2          // ocr-a=6
822        && loop(bp,dx-1,     0,dy,cs,0,DO)>dy/4 ) Break; // italic-6
823       if( loop(bp,   0,  dy/2,dx,cs,0,RI)>dx/4 ) Break;
824       if( loop(bp,   0,3*dy/4,dx,cs,0,RI)>dx/4 ) Break;
825       if( loop(bp,dx-1,3*dy/4,dx,cs,0,LE)>dx/2 ) Break;
826       if( num_cross(x0+  dx/2,x0+  dx/2,y0     ,y1     ,box1->p,cs) != 3
827        && num_cross(x0+5*dx/8,x0+5*dx/8,y0     ,y1     ,box1->p,cs) != 3 ) {
828         if( num_cross(x0+  dx/2,x0+  dx/2,y0+dy/4,y1     ,box1->p,cs) != 2
829          && num_cross(x0+5*dx/8,x0+5*dx/8,y0+dy/4,y1     ,box1->p,cs) != 2 ) Break;
830         // here we have the problem to decide between ocr-a-6 and b
831         if ( loop(box1->p,(x0+x1)/2,y0,dy,cs,0,DO)<dy/2 ) Break;
832         ad=99*ad/100;
833       } else {
834         if (loop(box1->p,x0+dx/2,y0,dx,cs,0,DO)>dy/8
835          && loop(box1->p,x1-dx/4,y0,dx,cs,0,DO)>dy/8 ) Break;
836       }
837       if( num_cross(x0     ,x1     ,y1-dy/4,y1-dy/4,box1->p,cs) != 2 ) Break;
838       for( y=y0+dy/6;y<y0+dy/2;y++ ){
839         x =loop(box1->p,x1    ,y  ,dx,cs,0,LE); if( x>dx/2 ) break;
840         x+=loop(box1->p,x1-x+1,y-1,dx,cs,0,LE); if( x>dx/2 ) break;
841       } if( y>=y0+dy/2 ) Break;
842       if (loop(box1->p,x0,y1-dy/3,dx,cs,0,RI)>dx/4 ) Break;
843       if (loop(box1->p,x1,y1-dy/3,dx,cs,0,LE)>dx/4 ) Break;
844
845       if (sdata->holes.num != 1) Break;
846       if (sdata->holes.hole[0].y1 < dy/2) ad=95*ad/100; // whats good for?
847       if (sdata->holes.hole[0].y0 < dy/4) Break;
848 //      if( num_hole ( x0, x1, y0, y0+dy/2,box1->p,cs,NULL) >  0 ) ad=95*ad/100; 
849 //      if( num_hole ( x0, x1, y0+dy/4, y1,box1->p,cs,NULL) != 1 ) Break; 
850 //      if( num_hole ( x0, x1, y0     , y1,box1->p,cs,NULL) != 1 ) Break; 
851 //    out_x(box1); printf(" x0 y0 %d %d\n",x0,y0);
852       /* check left vertical bow */
853       i1=loop(bp,0,dy/8     ,dx,cs,0,RI);
854       i3=loop(bp,0,dy-1-dy/8,dx,cs,0,RI);
855       i2=loop(bp,0,dy/2     ,dx,cs,0,RI); 
856       if(i1+i3-2*i2<-2-dx/16 && i1+i2+i3>0) Break;  // convex from left
857       if(i1+i3-2*i2<1        && i1+i2+i3>0) ad=99*ad/100;  // 7-segment-font
858       for( x=dx,y=0;y<dy/4;y++ ){       // ~ b (serife?)
859         i1=loop(bp, 0,y,dx,cs,0,RI);
860         i2=loop(bp,i1,y,dx,cs,1,RI);
861         if (i2+i1>dx/2 && i2>dx/4) break; /* its a 6 (example: 7-segment) */
862         if (i1<x) x=i1; else if (i1>x) break; /* may be serifen b */
863       } if (y<dy/4 && i1+i2<=dx/2) Break;
864       // ~& (with open upper loop)
865       for( i=0,y=dy/2;y<dy;y++){
866         if( num_cross(dx/2,dx-1,y,y,bp,cs) > 1 ) i++; if( i>dy/8 ) break;
867       } if( y<dy ) Break;
868       if ( gchar) ad=99*ad/100;
869       if (!hchar) ad=98*ad/100;
870       if ( box1->dots ) ad=98*ad/100;
871       Setac(box1,(wchar_t)'6',ad);
872       bc='6';
873       break;
874    }
875    // --- test 7 ---------------------------------------------------
876    for(ad=d=100;dx>2 && dy>4;){     // dx>1 dy>2*dx
877       DBG( char c_ask='7'; )
878       if (sdata->holes.num > 1) Break; /* be tolerant */
879       if( loop(bp,dx/2,0,dy,cs,0,DO)>dy/8 ) Break;
880       if( num_cross(0,dx-1,3*dy/4,3*dy/4,bp,cs) != 1 ) Break; // preselect
881       for( yb=xb=y=0;y<dy/2;y++){ // upper h-line and gap
882         j=loop(bp,0,y,dx,cs,0,RI);if(xb>0 && j>dx/4) break; // gap after h-line
883         j=loop(bp,j,y,dx,cs,1,RI);if(j>xb){ xb=j;yb=y; }  // h-line
884       } if( xb<dx/4 || y==dy/2 ) Break;
885       j=loop(bp,0,dy/2,dx,cs,0,RI);
886       j=loop(bp,j,dy/2,dx,cs,1,RI); if(xb<2*j) Break; // minimum thickness
887       for(x=0,y+=dy/16;y<dy;y++){       // one v-line?
888         if( num_cross(0,dx-1,y,y,bp,cs) != 1 ) break;
889         j=loop(bp,dx-1,y,dx,cs,0,LE); if( j<x ) break; if( j-1>x ) x=j-1;
890       } if( y<dy || x<dx/3 ) {
891          MSG( fprintf(stderr,"xy= %d %d",x,y); )
892          Break;
893       }
894       j =loop(bp,dx-1,0,dy,cs,0,DO);  // ~T
895       j+=loop(bp,dx-1,j,dy,cs,1,DO)+dy/16;
896       i =loop(bp,dx-1,j,dx,cs,0,LE); if(j<dy/2) {
897        if (i>j) Break;
898        j=loop(bp,   0,j,dx,cs,0,RI);
899        if(j>dx/4 && j<=i+dx/16) Break; // tall T
900       }
901
902       MSG( fprintf(stderr,"7: ad= %d",ad); )
903       if(   loop(bp,   0,3*dy/8,dx,cs,0,RI)
904           <=loop(bp,dx-1,3*dy/8,dx,cs,0,LE)+dx/8 ) ad=ad*98/100; // l
905       MSG( fprintf(stderr,"7: ad= %d",ad); )
906       if( num_cross(0,dx-1,dy/4,dy/4,bp,cs) == 1
907        && loop(bp,0,dy/4,dx,cs,0,RI) < dx/2 ) ad=ad*96/100; // J
908       MSG( fprintf(stderr,"7: ad= %d",ad); )
909
910       if (box1->m3 &&   dy<box1->m3-box1->m2) ad=99*ad/100; // too small
911       if (box1->m3 && 2*dy<box1->m3-box1->m2) ad=96*ad/100; // too small
912       if (dy>3*dx) ad=99*ad/100; // )
913       if ( gchar)  ad=99*ad/100; // J
914       if (!hchar)  ad=99*ad/100;
915       Setac(box1,(wchar_t)'7',ad);
916       break;
917    }
918    // --- test 8 ---------------------------------------------------
919    // last change: May15th,2000 JS
920    for(ad=d=100;dx>2 && dy>4;){     //    or we need large height
921       DBG( char c_ask='8'; )
922       if (sdata->holes.num != 2) Break;
923       if( num_cross(x0,x1,y0  +dy/4,y0  +dy/4,box1->p,cs) != 2 ) Break; // ~gr (glued)
924       if( num_cross(x0,x1,y1  -dy/4,y1  -dy/4,box1->p,cs) != 2
925        && num_cross(x0,x1,y1-3*dy/8,y1-3*dy/8,box1->p,cs) != 2 ) Break;
926       if( get_bw(x0,x0+dx/4,y1-dy/4,y1-dy/4,box1->p,cs,1) == 0 ) Break; // ~9
927       if( get_bw(x0,x0+dx/2,y0+dy/4,y0+dy/4,box1->p,cs,1) == 0 ) Break;
928       if( get_bw(x0+dx/2,x0+dx/2,y0+dy/4,y1-dy/4,box1->p,cs,1) == 0 ) Break; // ~0
929 // MSG( printf(" x0 y0 %d %d\n",x0,y0); )
930       for( i2=i1=x=0,i=y=y0+dy/3;y<=y1-dy/3;y++){       // check left middle nick
931         j=loop(box1->p,x0,y,dx,cs,0,RI);
932         if (j>x || (abs(j-x)<=dx/8  /* care about MICR E-13B font */
933                  && (i1=loop(box1->p,x0+j,y,dx,cs,1,RI))>dx/2)) {
934            if (j>x) x=j; i=y; if (i1>i2) i2=i1; }
935       } if(i>=y1-dy/3 || (x<dx/8 && i2<=dx/2) || x>dx/2) Break; // no gB
936       if (x< dx/4) ad=99*ad/100; // no B
937       if (x<=dx/8) ad=98*ad/100; // no B
938       j =   loop(box1->p,x1,y1-  dy/4,dx,cs,0,LE);
939       if( j>loop(box1->p,x1,y1-  dy/5,dx,cs,0,LE)
940        && j>loop(box1->p,x1,y1-2*dy/5,dx,cs,0,LE) ) Break;      // &
941       // check for upper hole
942       for (j=0;j<sdata->holes.num;j++) {
943         if (sdata->holes.hole[j].y1 < i-y0+1   ) break;
944         if (sdata->holes.hole[j].y1 < i-y0+dy/8) break;
945       } if (j==sdata->holes.num) Break;  // not found
946       // if( num_hole(x0,x1,y0,i+1   ,box1->p,cs,NULL)!=1 )
947       // if( num_hole(x0,x1,y0,i+dy/8,box1->p,cs,NULL)!=1 ) Break;      // upper hole
948       // check for lower hole
949       for (j=0;j<sdata->holes.num;j++) {
950         if (sdata->holes.hole[j].y0 > i-y0-1   ) break;
951       } if (j==sdata->holes.num) Break;  // not found
952       // if( num_hole(x0,x1,i-1,y1,box1->p,cs,NULL)!=1 ) Break; 
953       i1=i;  // left middle nick
954       /* find the middle right nick */
955       for( x=0,i2=i=y=y0+dy/3;y<=y1-dy/3;y++){
956         j=loop(box1->p,x1,y,dx,cs,0,LE); if( j>=x ) i2=y;
957         /* we care also for 7-segment and unusual fonts */
958         if (j>x || (abs(j-x)<=(dx+4)/8
959                  && loop(box1->p,x1-j,y,dx,cs,1,LE)>dx/2)){
960            if (j>x) x=j; i=y; }
961         // MSG(fprintf(stderr," yjix %d %d %d %d %d %d",y-y0,j,i-y0,x,loop(box1->p,x1-j,y,dx,cs,1,LE),dx/2);)
962       }
963       if( i>y0+dy/2+dy/10 ) Break;
964       // if( x<dx/8 ) Break;
965       if( x>dx/2 ) Break;
966       MSG(fprintf(stderr,"center bar at y= %d %d x=%d+%d i1=%d",i-y0,i2-y0,x,j,i1);)
967       if( num_cross(x0,x1, i      , i      ,box1->p,cs) != 1
968        && num_cross(x0,x1, i+1    , i+1    ,box1->p,cs) != 1
969        && num_cross(x0,x1,(i+i2)/2,(i+i2)/2,box1->p,cs) != 1 ) Break; // no g
970       if(abs(i1-i)>(dy+5)/10) ad=99*ad/100; // y-distance right-left-nick
971       if(abs(i1-i)>(dy+4)/8)  ad=99*ad/100; // y-distance right-left-nick
972       if(abs(i1-i)>(dy+2)/4) Break;
973       // ~B ff
974       for(i=dx,y=0;y<dy/8+2;y++){
975         j=loop(bp,0,y,dx,cs,0,RI); if( j<i ) i=j; if( j>i+dx/16 ) break;
976       } if( y<dy/8+2 ) Break;
977       for(i=dx,y=0;y<dy/8+2;y++){
978         j=loop(bp,0,dy-1-y,dx,cs,0,RI);
979         if( j<i ) i=j; if( j>i+dx/16 ) break;
980       } if( y<dy/8+2 ) Break;
981       if(  dy>16 && num_cross(0,dx-1,dy-1,dy-1,bp,cs) > 1
982         && loop(bp,0,dy-1,dx,cs,0,RI) <dx/8+1 ) Break; // no fat serif S
983       for( i=0,y=dy/2;y<dy;y++){
984         if( num_cross(0,dx-1,y,y,bp,cs) > 2 ) i++; if( i>dy/8 ) break;
985       } if( y<dy ) Break;
986       if ( loop(bp,dx-1,0,dx,cs,0,LE)==0 ) ad=99*ad/100;
987       if (num_cross(   0,dx-1,dy-1,dy-1,bp,cs) > 1) ad=98*ad/100; // &
988       if (num_cross(dx-1,dx-1,dy/2,dy-1,bp,cs) > 1) ad=98*ad/100; // &
989       if (num_cross(   0,dx-1,   0,   0,bp,cs) > 1) ad=98*ad/100;
990       if (dy>15)
991       if (num_cross(   0,dx-1,   1,   1,bp,cs) > 1) ad=98*ad/100;
992       /* if m1..4 is unsure ignore hchar and gchar ~ga */
993       if (!hchar) {
994         if ((box1->m2-box1->y0)*8>=dy) ad=98*ad/100;
995         else                           ad=99*ad/100;
996       }
997       if ( gchar
998          && (box1->y1-box1->m3)*8>=dy) ad=99*ad/100;
999       Setac(box1,(wchar_t)'8',ad);
1000       break;
1001    }
1002    // --- test 9 \it g ---------------------------------------------------
1003    /* 
1004     *    lcd  micr
1005     *    ooo  ooo
1006     *    o o  o o
1007     *    ooo  ooo
1008     *      o    o
1009     *    ooo    o
1010     */
1011    for(ad=d=100;dx>2 && dy>4;){     // dx>1 dy>2*dx
1012       DBG( char c_ask='9'; )
1013       if (sdata->holes.num > 1) Break;
1014       if( num_cross(x0+  dx/2,x0+  dx/2,y0,y1-dy/4,box1->p,cs) != 2 // pre select
1015        && num_cross(x0+  dx/2,x0+  dx/2,y0,     y1,box1->p,cs) != 3 // pre select
1016        && num_cross(x0+3*dx/8,x0+3*dx/8,y0,y1,box1->p,cs) != 3
1017        && num_cross(x0+  dx/4,x1  -dx/4,y0,y1,box1->p,cs) != 3 ) Break;
1018       if( num_cross(x0+  dx/2,x0  +dx/2,y0,y0+dy/4,box1->p,cs) < 1 ) Break;
1019       if( num_cross(x0+  dx/2,x1, y0+dy/2 ,y0+dy/2,box1->p,cs) < 1 ) Break;
1020       if( num_cross(x0,x1, y0+  dy/4 ,y0+  dy/4,box1->p,cs) != 2 
1021        && num_cross(x0,x1, y0+3*dy/8 ,y0+3*dy/8,box1->p,cs) != 2 ) Break;
1022       if( num_cross(x1-dx/8,x1,y0+dy/4,y0+dy/4,box1->p,cs) == 0) ad=ad*97/100; // ~4
1023       for( x=0,i=y=y0+dy/2;y<=y1-dy/4;y++){     // find notch (suche kerbe)
1024         j=loop(box1->p,x0,y,dx,cs,0,RI); 
1025         if( j>x ) { x=j; i=y; }  
1026       } if (x<1 || x<dx/8) Break; y=i;
1027  //      fprintf(stderr," debug 9: %d %d\n",x,i-y0);
1028       if( x<dx/2 ) {  /* big bow? */
1029         j=loop(box1->p,x0+x-1,y,dy/8+1,cs,0,DO)/2; y=i=y+j;
1030         j=loop(box1->p,x0+x-1,y,dx/2  ,cs,0,RI);   x+=j;
1031         if (x<dx/2) Break;
1032       }
1033       // check for the right lower bow
1034       MSG( fprintf(stderr,"bow-y0= %d",i-y0); )
1035       if (dx>5)
1036       if( num_cross(x0+dx/2,x1,i,y1     ,box1->p,cs) != 1  /* fails on 5x8 */
1037        && num_cross(x0+dx/2,x1,i,y1-dy/8,box1->p,cs) != 1 ) Break;
1038       if( num_cross(x0+dx/2,x0+dx/2,i,y1,box1->p,cs)  > 1 ) Break;
1039       if( num_cross(x0+dx/2,x1     ,i, i,box1->p,cs) != 1 ) Break;
1040
1041       if (sdata->holes.num < 1) { /* happens for 5x7 font */
1042         if (dx<8) ad=98*ad/100; else Break; }
1043       else {
1044         if (sdata->holes.hole[0].y1 >= i+1) Break;
1045         if (sdata->holes.hole[0].y0 >  i-1) Break;
1046         if (sdata->holes.num > 1)
1047         if (sdata->holes.hole[1].y0 >  i-1) Break;
1048       // if( num_hole(x0,x1,y0,i+1,box1->p,cs,NULL)!=1 ) Break;
1049       // if( num_hole(x0,x1,i-1,y1,box1->p,cs,NULL)!=0 ) Break;
1050       }
1051       if( loop(box1->p,x0,y1  ,dy,cs,0,RI)>dx/3 &&
1052           loop(box1->p,x0,y1-1,dy,cs,0,RI)>dx/3
1053          && (box1->m3==0 || (box1->m3!=0 && (!hchar || gchar)))) ad=98*ad/100; // no q OR ocr-a-9
1054       for( x=0,i=y=y0+dy/3;y<=y1-dy/3;y++){     // suche kerbe
1055         j=loop(box1->p,x1,y,dx,cs,0,LE); 
1056         if( j>x ) { x=j; i=y; }
1057       } if( x>dx/2 ) Break;             // no g
1058       i1=loop(bp,dx-1,dy/8     ,dx,cs,0,LE); if(i1>dx/2) Break;
1059       i3=loop(bp,dx-1,dy-1-dy/8,dx,cs,0,LE);
1060       i2=loop(bp,dx-1,dy/2     ,dx,cs,0,LE); if(i1+i3-2*i2<-1-dx/16) Break; // konvex
1061       i1=loop(bp,dx-1,dy/4     ,dx,cs,0,LE); if(i1>dx/2) Break;
1062       i3=loop(bp,dx-1,dy-1-dy/8,dx,cs,0,LE);
1063       for(y=dy/4;y<dy-1-dy/4;y++){
1064         i2=loop(bp,dx-1,y,dx,cs,0,LE);
1065         if(i1+i3-2*i2<-1-dx/16) break;  // konvex from right ~g ~3
1066       } if(y<dy-1-dy/4) Break;
1067       x=loop(bp,dx  -1,6*dy/8,dx,cs,0,LE); if(x>0){
1068         x--; // robust
1069         y=loop(bp,dx-x-1,  dy-1,dy,cs,0,UP);
1070         if(y<dy/8) Break; // ~q (serif!)
1071       }
1072       if (box1->m3) {
1073         if( gchar) ad=99*ad/100;  /* unsure */
1074         if(!hchar) ad=99*ad/100;  /* unsure */
1075       } else { if (ad==100) ad=99; } /* not 100% sure */
1076       Setac(box1,(wchar_t)'9',ad);
1077       break;
1078    }
1079    // 0 is same as O !?
1080    // --- test 0 (with one big hole in it ) -----------------------------
1081    for(d=ad=100;dx>2 && dy>3;){     // min 3x4
1082       DBG( char c_ask='0'; )
1083       if (sdata->holes.num > 1) Break; /* be tolerant */
1084       if( get_bw(x0      , x0+dx/3,y0+dy/2 , y0+dy/2,box1->p,cs,1) != 1 ) Break;
1085       if( get_bw(x1-dx/3 , x1     ,y0+dy/2 , y0+dy/2,box1->p,cs,1) != 1 ) Break;
1086       /* could be an O, unless we find a dot in the center */
1087       if( get_bw(x0      , x1     ,y0+dy/2 , y0+dy/2,box1->p,cs,1) != 3 ) ad=99;
1088       if( get_bw(x0+dx/2 , x0+dx/2,y1-dy/3 , y1,box1->p,cs,1) != 1 ) Break;
1089       if( get_bw(x0+dx/2 , x0+dx/2,y0      , y0+dy/3,box1->p,cs,1) != 1 ) Break;
1090       /* accept 0 with dot in center, accept \/0 too ... */
1091       if( get_bw(x0+dx/2 , x0+dx/2,y0+dy/3 , y1-dy/3,box1->p,cs,1) != 0 ) Break;
1092
1093       if( num_cross(x0+dx/2,x0+dx/2,y0      , y1     ,box1->p,cs)  != 2 ) Break;
1094       if( num_cross(x0+dx/3,x1-dx/3,y0      , y0     ,box1->p,cs)  != 1 ) // AND
1095       if( num_cross(x0+dx/3,x1-dx/3,y0+1    , y0+1   ,box1->p,cs)  != 1 ) Break;
1096       if( num_cross(x0+dx/3,x1-dx/3,y1      , y1     ,box1->p,cs)  != 1 ) // against "rauschen"
1097       if( num_cross(x0+dx/3,x1-dx/3,y1-1    , y1-1   ,box1->p,cs)  != 1 ) Break;
1098       if( num_cross(x0     ,x0     ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 )
1099       if( num_cross(x0+1   ,x0+1   ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 ) Break;
1100       if( num_cross(x1     ,x1     ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 )
1101       if( num_cross(x1-1   ,x1-1   ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 ) Break;
1102       // if( num_hole(x0,x1,y0,y1,box1->p,cs,NULL) != 1 ) Break;
1103       if (sdata->holes.num != 1) Break;
1104       
1105       i= loop(bp,0   ,0   ,x1-x0,cs,0,RI)-
1106          loop(bp,0   ,2   ,x1-x0,cs,0,RI);
1107       if (i<0) Break;
1108       if (i==0) {
1109         if (loop(bp,dx-1,0   ,x1-x0,cs,0,LE)>
1110             loop(bp,dx-1,2   ,x1-x0,cs,0,LE)  ) ad=98*ad/100;
1111         ad=99*ad/100; /* LCD-type? */
1112       }
1113       
1114       x=loop(bp,dx-1,dy-1-dy/3,x1-x0,cs,0,LE);  // should be minimum
1115       for (y=dy-1-dy/3;y<dy;y++){
1116         i=loop(bp,dx-1,y,x1-x0,cs,0,LE);
1117         if (i<x-dx/16-1) break; if (i>x) x=i;
1118       }
1119       if( y<dy ) Break;
1120
1121       // ~D (but ocr-a-font)
1122       i=      loop(bp,   0,     dy/16,dx,cs,0,RI)
1123          +    loop(bp,   0,dy-1-dy/16,dx,cs,0,RI)
1124          -  2*loop(bp,   0,     dy/2 ,dx,cs,0,RI);
1125       j=      loop(bp,dx-1,     dy/16,dx,cs,0,LE)
1126          +    loop(bp,dx-1,dy-1-dy/16,dx,cs,0,LE)
1127          <= 2*loop(bp,dx-1,     dy/2 ,dx,cs,0,LE);
1128       if (i<-dx/8 || i+dx/8<j) Break; // not konvex
1129
1130       if( loop(bp,dx-1,     dy/16,dx,cs,0,LE)>dx/8 )
1131       if( loop(bp,0   ,     dy/16,dx,cs,0,RI)<dx/16 ) Break;
1132       if( loop(bp,dx-1,dy-1-dy/16,dx,cs,0,LE)>dx/8 )
1133       if( loop(bp,0   ,dy-1-dy/16,dx,cs,0,RI)<dx/16 ) Break;
1134       if( get_bw(x1-dx/32,x1,y0,y0+dy/32,box1->p,cs,1) == 0
1135        && get_bw(x1-dx/32,x1,y1-dy/32,y1,box1->p,cs,1) == 0
1136        && ( get_bw(x0,x0+dx/32,y0,y0+dy/32,box1->p,cs,1) == 1
1137          || get_bw(x0,x0+dx/32,y1-dy/32,y1,box1->p,cs,1) == 1 ) ) {
1138          if (dx<32) ad=ad*99/100; else Break; // ~D
1139       }
1140
1141       // search lowest inner white point
1142       for(y=dy,j=x=0;x<dx;x++) {
1143         i =loop(bp,x,dy-1  ,y1-y0,cs,0,UP);
1144         i+=loop(bp,x,dy-1-i,y1-y0,cs,1,UP);
1145         if (i<=y) { y=i; j=x; }
1146       } i=y;
1147       // italic a
1148       for(y=dy-1-i;y<dy-1;y++)
1149         if( num_cross(j,dx-1,y,y,bp,cs) > 1 ) ad=99*ad/100; // ~a \it a
1150
1151       if (loop(bp,   0,   0,x1-x0,cs,0,RI)>=dx/8) { // round, notLCD
1152         if (loop(bp,dx-1,dy-1,x1-x0,cs,0,LE)<dx/8) ad=98*ad/100; // \it a
1153         if (loop(bp,dx-1,   0,x1-x0,cs,0,LE)<dx/8) ad=98*ad/100; // \it a
1154       }
1155
1156       if (abs(loop(bp,dx/2,   0,dy,cs,0,DO)
1157              -loop(bp,dx/2,dy-1,dy,cs,0,UP))>dy/8
1158         || num_cross(0,dx-1,   0,   0,bp,cs) > 1
1159         || num_cross(0,dx-1,dy-1,dy-1,bp,cs) > 1
1160          ) ad=98*ad/100; // ~bq
1161
1162       if (box1->m3) {
1163        if (!hchar) ad=98*ad/100; else // ~o
1164        if ( gchar) ad=99*ad/100;      // wrong line detection?
1165       } else { if (ad==100) ad=99; } /* not 100% sure */
1166       if (ad>99) ad=99; /* we can never be sure having a O,
1167                            let context correction decide, see below! */
1168       Setac(box1,(wchar_t)'0',ad);
1169       break;
1170    } 
1171    // --- test 0 with a straight line in it -------------------
1172    for(ad=100;dx>4 && dy>5;){  /* v0.3.1+ */
1173       DBG( char c_ask='0'; )
1174       if (sdata->holes.num > 3) Break; /* be tolerant */
1175       if (sdata->holes.num < 1) Break;
1176       if (sdata->holes.num != 2) ad=95*ad/100;
1177       if( get_bw(x0      , x0+dx/2,y0+dy/2 , y0+dy/2,box1->p,cs,1) != 1 ) Break;
1178       if( get_bw(x1-dx/2 , x1     ,y0+dy/2 , y0+dy/2,box1->p,cs,1) != 1 ) Break;
1179       if( get_bw(x0+dx/2 , x0+dx/2,y1-dy/2 , y1,box1->p,cs,1) != 1 ) Break;
1180       if( get_bw(x0+dx/2 , x0+dx/2,y0      , y0+dy/2,box1->p,cs,1) != 1 ) Break;
1181       if( get_bw(x0+dx/2 , x0+dx/2,y0+dy/3 , y1-dy/3,box1->p,cs,1) != 1 ) Break;
1182       // out_x(box1); printf(" x0 y0 %d %d\n",x0,y0);
1183       if( num_cross(x0+dx/2,x0+dx/2,y0      , y1     ,box1->p,cs)  != 3 ) Break;
1184       if( num_cross(x0+dx/3,x1-dx/3,y0      , y0     ,box1->p,cs)  != 1 ) // AND
1185       if( num_cross(x0+dx/3,x1-dx/3,y0+1    , y0+1   ,box1->p,cs)  != 1 ) Break;
1186       if( num_cross(x0+dx/3,x1-dx/3,y1      , y1     ,box1->p,cs)  != 1 ) // against "rauschen"
1187       if( num_cross(x0+dx/3,x1-dx/3,y1-1    , y1-1   ,box1->p,cs)  != 1 ) Break;
1188       if( num_cross(x0     ,x0     ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 )
1189       if( num_cross(x0+1   ,x0+1   ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 ) Break;
1190       if( num_cross(x1     ,x1     ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 )
1191       if( num_cross(x1-1   ,x1-1   ,y0+dy/3 , y1-dy/3,box1->p,cs)  != 1 ) Break;
1192       // if( num_hole(x0,x1,y0,y1,box1->p,cs,NULL) != 2 ) Break;
1193       if (sdata->holes.num != 2) ad=85*ad/100;
1194       
1195       if( loop(bp,0   ,        0,x1-x0,cs,0,RI)<=
1196           loop(bp,0   ,  2+dy/32,x1-x0,cs,0,RI)  ) Break;
1197       x=  loop(bp,0   ,dy/2  ,x1-x0,cs,0,RI);
1198       i=  loop(bp,0   ,dy/2-1,x1-x0,cs,0,RI); if (i>x) x=i;
1199       i=  loop(bp,0   ,dy/2-2,x1-x0,cs,0,RI); if (i>x && dy>8) x=i;
1200       if( loop(bp,0   ,  dy/4,x1-x0,cs,0,RI)<x ) Break; // ~8
1201       x=  loop(bp,dx-1,dy/2  ,x1-x0,cs,0,LE);
1202       i=  loop(bp,dx-1,dy/2-1,x1-x0,cs,0,LE); if(i>x) x=i;
1203       i=  loop(bp,dx-1,dy/2-1,x1-x0,cs,0,LE); if(i>x && dy>8) x=i;
1204       if( loop(bp,dx-1,3*dy/4,x1-x0,cs,0,LE)<x) Break; // ~8
1205
1206       x=loop(bp,dx-1,dy-1-dy/3,x1-x0,cs,0,LE);  // should be minimum
1207       for( y=dy-1-dy/3;y<dy;y++ ){
1208         i=loop(bp,dx-1,y,x1-x0,cs,0,LE);
1209         if (i<x-dx/16) break; 
1210         if (i>x) x=i;
1211       }
1212       if( y<dy ) Break;
1213       
1214       /* test for straight line */
1215       y =loop(bp,dx/2,dy-1  ,y1-y0,cs,0,UP); if(y>dy/4) Break;
1216       y+=loop(bp,dx/2,dy-1-y,y1-y0,cs,1,UP); if(y>dy/3) Break; if (y>dy/4) ad=ad*99/100;
1217       y+=loop(bp,dx/2,dy-1-y,y1-y0,cs,0,UP); if(3*y>2*dy) Break;
1218       x =loop(bp,dx/2,dy-y,dx/2,cs,0,RI);    if(x==0) Break;
1219       // MM; fprintf(stderr," y=%d x=%d\n",y-1,x);
1220       if( loop(bp,dx/2+x-1-dx/16,dy-y,y1-y0,cs,0,UP)==0 ) Break;
1221        // $
1222       for(i=0,y=dy/4;y<dy-dy/4-1;y++)
1223       if( loop(bp,   0,y,dx-1,cs,0,RI) > dx/4
1224        || loop(bp,dx-1,y,dx-1,cs,0,LE) > dx/4 ) break;
1225       if( y<dy-dy/4-1 ) Break;
1226
1227       // ~D
1228       if(     loop(bp,0,     dy/16,dx,cs,0,RI)
1229          +    loop(bp,0,dy-1-dy/16,dx,cs,0,RI)
1230          <= 2*loop(bp,0,     dy/2 ,dx,cs,0,RI)+dx/8 ) Break; // not konvex
1231
1232       if( loop(bp,dx-1,     dy/16,dx,cs,0,LE)>dx/8 )
1233       if( loop(bp,0   ,     dy/16,dx,cs,0,RI)<dx/16 ) Break;
1234       if( loop(bp,dx-1,dy-1-dy/16,dx,cs,0,LE)>dx/8 )
1235       if( loop(bp,0   ,dy-1-dy/16,dx,cs,0,RI)<dx/16 ) Break;
1236       if( get_bw(x1-dx/32,x1,y0,y0+dy/32,box1->p,cs,1) == 0
1237        && get_bw(x1-dx/32,x1,y1-dy/32,y1,box1->p,cs,1) == 0
1238        && ( get_bw(x0,x0+dx/32,y0,y0+dy/32,box1->p,cs,1) == 1
1239          || get_bw(x0,x0+dx/32,y1-dy/32,y1,box1->p,cs,1) == 1 ) ) Break; // ~D
1240
1241       /* 5x9 font "9" is like "0" */
1242       if (dx<16)
1243       if ( num_cross(x0,x0,y0,y1,box1->p,cs)  != 1 ) ad=98*ad/100;
1244
1245        // italic a
1246       for(i=0,y=6*dy/8;y<dy-dy/16;y++)
1247       if( num_cross(0,dx-1,y,y,bp,cs) > 2 ) i++; else i--;
1248       if(i>0) ad=ad*98/100; // ~'a' \it a
1249       if( !hchar ) ad=90*ad/100;
1250       Setac(box1,(wchar_t)'0',ad);
1251       break;
1252    } 
1253    return box1->c;
1254 }