yagf-0.9.3.2/src/core/common.h0000664000175000017500000000221202263216100016500 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef COMMON_H #define COMMON_H #include template void deallocator(T_ buf[]) { delete[] buf; } typedef struct IntRect { quint32 x1, y1, x2, y2; } _IntRect; //#define IPRIT_MULTITHREADING #ifdef IPRIT_MULTITHREADING #include #include #endif #endif // COMMON_H yagf-0.9.3.2/src/core/binarize.h0000664000175000017500000000254302263216100017022 0ustar parallelsparallels#ifndef BINARIZE_H #define BINARIZE_H /* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky The original code is Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #if !defined(_qrcode_binarize_H) # define _qrcode_binarize_H (1) void qr_image_cross_masking_median_filter(unsigned char *_img, int _width,int _height); void qr_wiener_filter(unsigned char *_img,int _width,int _height); /*Binarizes a grayscale image.*/ void qr_binarize(unsigned char *_img,int _width,int _height); unsigned char *qr_binarize1(const unsigned char *_img,int _width,int _height); #endif #endif // BINARIZE_H yagf-0.9.3.2/src/core/binarize.cpp0000664000175000017500000005505402263216100017362 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky The original code is Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "util.h" //#include "image.h" #include "binarize.h" /*Binarization based on~\cite{GPP06}. @ARTICLE{GPP06, author="Basilios Gatos and Ioannis E. Pratikakis and Stavros J. Perantonis", title="Adaptive Degraded Document Image Binarization", journal="Pattern Recognition", volume=39, number=3, pages="317-327", month=Mar, year=2006 }*/ void wienerFilter(unsigned char *_img,int _width,int _height, int dimensions){ int bufSize = dimensions == 5 ? 8 : 4; unsigned *m_buf[bufSize]; unsigned *sn2_buf[bufSize]; unsigned char g; int x; int y; if(_width<=0||_height<=0)return; m_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*m_buf)); sn2_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*sn2_buf)); for(y=1;y<8;y++){ m_buf[y]=m_buf[y-1]+_width+4; sn2_buf[y]=sn2_buf[y-1]+_width+4; } for(y=-4;y<_height;y++){ unsigned *pm; unsigned *psn2; int i; int j; pm=m_buf[y+2&7]; psn2=sn2_buf[y+2&7]; for(x=-4;x<_width;x++){ unsigned m; unsigned m2; m=m2=0; if(y>=0&&y<_height-4&&x>=0&&x<_width-4)for(i=0;i<5;i++)for(j=0;j<5;j++){ g=_img[(y+i)*_width+x+j]; m+=g; m2+=g*g; } else for(i=0;i<5;i++)for(j=0;j<5;j++){ g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; m+=g; m2+=g*g; } pm[x+4]=m; psn2[x+4]=(m2*25-m*m); } pm=m_buf[y&7]; if(y>=0)for(x=0;x<_width;x++){ int sn2; sn2=sn2_buf[y&7][x+2]; if(sn2){ int vn3; int m; /*Gatos et al. give the expression mu+(s2-v2)*(g-mu)/s2 , which we reduce to mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , g-(v2/s2)*g+(v2/s2)*mu , g+(mu-g)*(v2/s2) . However, s2 is much noisier than v2, and dividing by it often gives extremely large adjustments, causing speckle near edges. Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ vn3=0; for(i=-2;i<3;i++){ psn2=sn2_buf[y+i&7]; for(j=0;j<5;j++)vn3+=psn2[x+j]; } m=m_buf[y&7][x+2]; vn3=vn3+1023>>10; sn2=25*sn2+1023>>10; if(vn3=0&&y<_height-4&&x>=0&&x<_width-4)for(i=0;i<5;i++)for(j=0;j<5;j++){ g=_img[(y+i)*_width+x+j]; m+=g; m2+=g*g; } else for(i=0;i<5;i++)for(j=0;j<5;j++){ g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; m+=g; m2+=g*g; } pm[x+4]=m; psn2[x+4]=(m2*25-m*m); } pm=m_buf[y&7]; if(y>=0)for(x=0;x<_width;x++){ int sn2; sn2=sn2_buf[y&7][x+2]; if(sn2){ int vn3; int m; /*Gatos et al. give the expression mu+(s2-v2)*(g-mu)/s2 , which we reduce to mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , g-(v2/s2)*g+(v2/s2)*mu , g+(mu-g)*(v2/s2) . However, s2 is much noisier than v2, and dividing by it often gives extremely large adjustments, causing speckle near edges. Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ vn3=0; for(i=-2;i<3;i++){ psn2=sn2_buf[y+i&7]; for(j=0;j<5;j++)vn3+=psn2[x+j]; } m=m_buf[y&7][x+2]; vn3=vn3+1023>>10; sn2=25*sn2+1023>>10; if(vn3=0&&y<_height-2&&x>=0&&x<_width-2)for(i=0;i<3;i++)for(j=0;j<3;j++){ g=_img[(y+i)*_width+x+j]; m+=g; m2+=g*g; } else for(i=0;i<3;i++)for(j=0;j<3;j++){ g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; m+=g; m2+=g*g; } pm[x+2]=m; psn2[x+2]=(m2*9-m*m); } pm=m_buf[y&3]; if(y>=0)for(x=0;x<_width;x++){ int sn2; sn2=sn2_buf[y&3][x+1]; if(sn2){ int m; int vn3; /*Gatos et al. give the expression mu+(s2-v2)*(g-mu)/s2 , which we reduce to mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , g-(v2/s2)*g+(v2/s2)*mu , g+(mu-g)*(v2/s2) . However, s2 is much noisier than v2, and dividing by it often gives extremely large adjustments, causing speckle near edges. Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ vn3=0; for(i=-1;i<2;i++){ psn2=sn2_buf[y+i&3]; for(j=0;j<3;j++)vn3+=psn2[x+j]; } m=m_buf[y&3][x+1]; vn3=vn3+31>>5; sn2=9*sn2+31>>5; if(vn30&&_height>0){ unsigned *col_sums; unsigned *col2_sums; int logwindw; int logwindh; int windw; int windh; int y0offs; int y1offs; unsigned g; unsigned g2; int x; int y; /*We keep the window size fairly large to ensure it doesn't fit completely inside the center of a finder pattern of a version 1 QR code at full resolution.*/ for(logwindw=4;logwindw<8&&(1<>3);logwindw++); for(logwindh=4;logwindh<8&&(1<>3);logwindh++); windw=1<>1);y++){ y1offs=QR_MINI(y,_height-1)*_width; for(x=0;x<_width;x++){ g=_img[y1offs+x]; col_sums[x]+=g; col2_sums[x]+=g*g; } } for(y=0;y<_height;y++){ unsigned m; unsigned m2; int x0; int x1; /*Initialize the sums over the window.*/ m=(col_sums[0]<>1);x++){ x1=QR_MINI(x,_width-1); m+=col_sums[x1]; m2+=col2_sums[x1]; } for(x=0;x<_width;x++){ int d; /*Perform the test against the threshold T = (m/n)*(1+k*(s/R-1)), where n=windw*windh, s=sqrt((m2-(m*m)/n)/n), and R=128. We don't actually compute the threshold directly, as that would require a square root. Instead we perform the equivalent test: (m/n)*(m/n)*(m2/n-(m/n)*(m/n))/16 > (((1/k)*g-((1-k)/k)*(m/n))*32)**2 R is split up across each side of the inequality to maximize the dynamic range available for the right hand side, which requires 31 bits in the worst case.*/ /*(m/n)*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g m*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g*n m*sqrt((m2-m*m/n)/n) > 5*g*n-4*m<<7 m*m*(m2*n-m*m) > (5*g*n-4*m<<7)**2*n*n || 5*g*n-4*m < 0 */ g=_img[y*_width+x]; d=(5*g<=0){ unsigned mm; unsigned mms2; unsigned d2; mm=(m>>logwindw)*(m>>logwindh); mms2=(m2-mm>>logwindw+logwindh)*(mm>>logwindw+logwindh)+15>>4; d2=d>>logwindw+logwindh-5; d2*=d2; if(d2>=mms2){ /*Update the background average.*/ b+=g; nb++; _mask[y*_width+x]=0; } else _mask[y*_width+x]=0xFF; } else _mask[y*_width+x]=0xFF; /*Update the window sums.*/ if(x+1<_width){ x0=QR_MAXI(0,x-(windw>>1)); x1=QR_MINI(x+(windw>>1),_width-1); m+=col_sums[x1]-col_sums[x0]; m2+=col2_sums[x1]-col2_sums[x0]; } } /*Update the column sums.*/ if(y+1<_height){ y0offs=QR_MAXI(0,y-(windh>>1))*_width; y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; for(x=0;x<_width;x++){ g=_img[y0offs+x]; col_sums[x]-=g; col2_sums[x]-=g*g; g=_img[y1offs+x]; col_sums[x]+=g; col2_sums[x]+=g*g; } } } free(col2_sums); free(col_sums); } *_b=b; *_nb=nb; } /*Interpolates a background image given the source and a conservative foreground mask. If the current window contains no foreground pixels, the average background value over the whole image is used. Note on dynamic range: we assume _width*_height<=0x8000000 (23 bits). Returns the average difference between the foreground and the interpolated background.*/ static void qr_interpolate_background(unsigned char *_dst, int *_delta,int *_ndelta,const unsigned char *_img,const unsigned char *_mask, int _width,int _height,unsigned _b,int _nb){ int delta; int ndelta; delta=ndelta=0; if(_width>0&&_height>0){ unsigned *col_sums; unsigned *ncol_sums; int logwindw; int logwindh; int windw; int windh; int y0offs; int y1offs; unsigned b; unsigned g; int x; int y; b=_nb>0?((_b<<1)+_nb)/(_nb<<1):0xFF; for(logwindw=4;logwindw<8&&(1<>4);logwindw++); for(logwindh=4;logwindh<8&&(1<>4);logwindh++); windw=1<>1);y++){ y1offs=QR_MINI(y,_height-1)*_width; for(x=0;x<_width;x++)if(!_mask[y1offs+x]){ col_sums[x]+=_img[y1offs+x]; ncol_sums[x]++; } } for(y=0;y<_height;y++){ unsigned n; unsigned m; int x0; int x1; /*Initialize the sums over the window.*/ m=(col_sums[0]<>1);x++){ x1=QR_MINI(x,_width-1); m+=col_sums[x1]; n+=ncol_sums[x1]; } for(x=0;x<_width;x++){ if(!_mask[y*_width+x])g=_img[y*_width+x]; else{ g=n>0?((m<<1)+n)/(n<<1):b; delta+=(int)g-_img[y*_width+x]; ndelta++; } _dst[y*_width+x]=(unsigned char)g; /*Update the window sums.*/ if(x+1<_width){ x0=QR_MAXI(0,x-(windw>>1)); x1=QR_MINI(x+(windw>>1),_width-1); m+=col_sums[x1]-col_sums[x0]; n+=ncol_sums[x1]-ncol_sums[x0]; } } /*Update the column sums.*/ if(y+1<_height){ y0offs=QR_MAXI(0,y-(windh>>1))*_width; y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; for(x=0;x<_width;x++){ if(!_mask[y0offs+x]){ col_sums[x]-=_img[y0offs+x]; ncol_sums[x]--; } if(!_mask[y1offs+x]){ col_sums[x]+=_img[y1offs+x]; ncol_sums[x]++; } } } } free(ncol_sums); free(col_sums); } *_delta=delta; *_ndelta=ndelta; } /*Parameters of the logistic sigmoid function that defines the threshold based on the background intensity. They should all be between 0 and 1.*/ #define QR_GATOS_Q (0.7) #define QR_GATOS_P1 (0.5) #define QR_GATOS_P2 (0.8) /*Compute the final binarization mask according to Gatos et al.'s method~\cite{GPP06}.*/ static void qr_gatos_mask(unsigned char *_mask,const unsigned char *_img, const unsigned char *_background,int _width,int _height, unsigned _b,int _nb,int _delta,int _ndelta){ unsigned thresh[256]; unsigned g; double delta; double b; int x; int y; /*Construct a lookup table for the thresholds. This bit uses floating point, but doesn't need to do much calculation, so emulation should be fine.*/ b=_nb>0?(_b+0.5)/_nb:0xFF; delta=_ndelta>0?(_delta+0.5)/_ndelta:0xFF; for(g=0;g<256;g++){ double d; d=QR_GATOS_Q*delta*(QR_GATOS_P2+(1-QR_GATOS_P2)/ (1+exp(2*(1+QR_GATOS_P1)/(1-QR_GATOS_P1)-4*g/(b*(1-QR_GATOS_P1))))); if(d<1)d=1; else if(d>0xFF)d=0xFF; thresh[g]=(unsigned)floor(d); } /*Apply the adaptive threshold.*/ for(y=0;y<_height;y++)for(x=0;x<_width;x++){ g=_background[y*_width+x]; /*_background[y*_width+x]=thresh[g];*/ _mask[y*_width+x]=(unsigned char)(-(g-_img[y*_width+x]>thresh[g])&0xFF); } /*{ FILE *fout; fout=fopen("thresh.png","wb"); image_write_png(_background,_width,_height,fout); fclose(fout); }*/ } /*Binarizes a grayscale image.*/ void qr_binarize(unsigned char *_img,int _width,int _height){ unsigned char *mask; unsigned char *background; unsigned b; int nb; int delta; int ndelta; /*qr_wiener_filter(_img,_width,_height); { FILE *fout; fout=fopen("wiener.png","wb"); image_write_png(_img,_width,_height,fout); fclose(fout); }*/ mask=(unsigned char *)malloc(_width*_height*sizeof(*mask)); qr_sauvola_mask(mask,&b,&nb,_img,_width,_height); /*{ FILE *fout; fout=fopen("foreground.png","wb"); image_write_png(mask,_width,_height,fout); fclose(fout); }*/ background=(unsigned char *)malloc(_width*_height*sizeof(*mask)); qr_interpolate_background(background,&delta,&ndelta, _img,mask,_width,_height,b,nb); /*{ FILE *fout; fout=fopen("background.png","wb"); image_write_png(background,_width,_height,fout); fclose(fout); }*/ qr_gatos_mask(_img,_img,background,_width,_height,b,nb,delta,ndelta); free(background); free(mask); } /*The above algorithms are computationally expensive, and do not work as well as the simple algorithm below. Sauvola by itself does an excellent job of classifying regions outside the QR code as background, which greatly reduces the chance of false alarms. However, it also tends to over-shrink isolated black dots inside the code, making them easy to miss with even slight mis-alignment. Since the Gatos method uses Sauvola as input to its background interpolation method, it cannot possibly mark any pixels as foreground which Sauvola classified as background, and thus suffers from the same problem. The following simple adaptive threshold method does not have this problem, though it produces essentially random noise outside the QR code region. QR codes are structured well enough that this does not seem to lead to any actual false alarms in practice, and it allows many more codes to be detected and decoded successfully than the Sauvola or Gatos binarization methods.*/ /*A simplified adaptive thresholder. This compares the current pixel value to the mean value of a (large) window surrounding it.*/ unsigned char *qr_binarize1(const unsigned char *_img,int _width,int _height){ unsigned char *mask = NULL; if(_width>0&&_height>0){ unsigned *col_sums; int logwindw; int logwindh; int windw; int windh; int y0offs; int y1offs; unsigned g; int x; int y; mask=(unsigned char *)malloc(_width*_height*sizeof(*mask)); /*We keep the window size fairly large to ensure it doesn't fit completely inside the center of a finder pattern of a version 1 QR code at full resolution.*/ for(logwindw=4;logwindw<8&&(1<>3);logwindw++); for(logwindh=4;logwindh<8&&(1<>3);logwindh++); windw=1<>1);y++){ y1offs=QR_MINI(y,_height-1)*_width; for(x=0;x<_width;x++){ g=_img[y1offs+x]; col_sums[x]+=g; } } for(y=0;y<_height;y++){ unsigned m; int x0; int x1; /*Initialize the sum over the window.*/ m=(col_sums[0]<>1);x++){ x1=QR_MINI(x,_width-1); m+=col_sums[x1]; } for(x=0;x<_width;x++){ /*Perform the test against the threshold T = (m/n)-D, where n=windw*windh and D=3.*/ g=_img[y*_width+x]; mask[y*_width+x]=-(g+3<>1)); x1=QR_MINI(x+(windw>>1),_width-1); m+=col_sums[x1]-col_sums[x0]; } } /*Update the column sums.*/ if(y+1<_height){ y0offs=QR_MAXI(0,y-(windh>>1))*_width; y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; for(x=0;x<_width;x++){ col_sums[x]-=_img[y0offs+x]; col_sums[x]+=_img[y1offs+x]; } } } free(col_sums); } #if defined(QR_DEBUG) { FILE *fout; fout=fopen("binary.png","wb"); image_write_png(_img,_width,_height,fout); fclose(fout); } #endif return(mask); } #if defined(TEST_BINARIZE) #include #include "image.c" int main(int _argc,char **_argv){ unsigned char *img; int width; int height; int x; int y; if(_argc<2){ fprintf(stderr,"usage: %s .png\n",_argv[0]); return EXIT_FAILURE; } /*width=1182; height=1181; img=(unsigned char *)malloc(width*height*sizeof(*img)); for(y=0;y This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef IMAGEPROCESSOR_H #define IMAGEPROCESSOR_H #include "qipgrayscaleimage.h" #include "qipblackandwhiteimage.h" #include #include class ImageProcessor : public QObject { Q_OBJECT public: explicit ImageProcessor(QObject *parent = 0); ~ImageProcessor(); QRect crop(); void loadImage(const QImage &image); QImage gsImage() const; void binarize(); void saveYGF(const QImage &image, const QString &fileName); QImage loadYGF(const QString &fileName); static void polishImage(QImage &image); static void polishImage2(QImage &image); static bool isTextHorizontal(QImage &image); static void cropAngles(QImage &image); signals: public slots: private: QIPGrayscaleImage img; private: }; #endif // IMAGEPROCESSOR_H yagf-0.9.3.2/src/core/qipblackandwhiteimage.h0000664000175000017500000001421402263216100021532 0ustar parallelsparallels/* This file is part of Libiprit by Andrei Borovsky . Libiprit is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Libiprit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Libiprit. If not, see . */ #ifndef QIPBLACKANDWHITEIMAGE_H #define QIPBLACKANDWHITEIMAGE_H #include "common.h" //#include "qipconnectedcomponents.h" #include #include #include /*! \class QIPBlackAndWhiteImage This class holds internal representations of black and white (binary) images and allows you to perform different operations on them. The only way to create an instance of the class is to call QIPGrayscaleImage::binarize() method. When you are done with transformations you can convert the data to QImage image (which will contain only black and white pixels) using the toImage() method. \sa QIPGrayscaleImage */ class QIPGrayscaleImage; class QIPBlackAndWhiteImage { QIPBlackAndWhiteImage(quint32 width, quint32 height); public: QIPBlackAndWhiteImage(); QIPBlackAndWhiteImage(const QIPBlackAndWhiteImage &I); /*! Converts binary image to QImage object. */ QImage toImage() const; /*! * Returns the part of the original black and white image bounded by the rectangle (x1,y1) (x2,y2) as a new instance of QIPBlackAndWhiteImage. * Note that the boundaries should not exceed those of the original image (this is not checked). * \sa toImage */ QIPBlackAndWhiteImage copy(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const; /*! * Returns the image's width. * \sa height */ quint32 width(); /*! * Returns the image's height. * \sa width */ quint32 height(); /*! * Returns an image obtained by morphologically dilating the original image with the structuring element defined by the structuringElement variable. * structuringElement is an array of quint8 values where 1 corresponds to white and 0 - to black. The array should form a square with an odd side size for example, the structureing element 3*3 might be declared as * \code quint8 se[9] = {1,0,1,0,0,0,1,0,1}; \endcode * The hotspot is in the center of the square. * dimensions is the size of the side of the square. Odd values like 3, 5, 7 should be used for squares 3*3, 5*5, and 7*7 respectively. * \sa erode */ QIPBlackAndWhiteImage dilate(quint8 *structuringElement, int dimensions) const; /*! * Returns an image obtained by morphologically eroding the original image with the structuring element defined by the structuringElement variable. * structuringElement is an array of quint8 values where 1 corresponds to white and 0 - to black. The array should form a square with an odd side size for example, the structureing element 3*3 might be declared as * \code quint8 se[9] = {1,0,1,0,0,0,1,0,1}; \endcode * The hotspot is in the center of the square. * dimensions is the size of the side of the square. Odd values like 3, 5, 7 should be used for squares 3*3, 5*5, and 7*7 respectively. * \sa dilate */ QIPBlackAndWhiteImage erode(quint8 *structuringElement, int dimensions) const; /*! * Returns an image obtained by morphologically opening the original image with the structuring element defined by the structuringElement variable. * structuringElement is an array of quint8 values where 1 corresponds to white and 0 - to black. The array should form a square with an odd side size for example, the structureing element 3*3 might be declared as * \code quint8 se[9] = {1,0,1,0,0,0,1,0,1}; \endcode * The hotspot is in the center of the square. * dimensions is the size of the side of the square. Odd values like 3, 5, 7 should be used for squares 3*3, 5*5, and 7*7 respectively. * \sa close */ QIPBlackAndWhiteImage open(quint8 *structuringElement, int dimensions) const; /*! * Returns an image obtained by morphologically closing the original image with the structuring element defined by the structuringElement variable. * structuringElement is an array of quint8 values where 1 corresponds to white and 0 - to black. The array should form a square with an odd side size for example, the structureing element 3*3 might be declared as * \code quint8 se[9] = {1,0,1,0,0,0,1,0,1}; \endcode * The hotspot is in the center of the square. * dimensions is the size of the side of the square. Odd values like 3, 5, 7 should be used for squares 3*3, 5*5, and 7*7 respectively. * \sa open */ QIPBlackAndWhiteImage close(quint8 *structuringElement, int dimensions) const; /*! * Returns an inverted image. */ QIPBlackAndWhiteImage inverse() const; /*! * Returns the image with black or white border frames cropped out. */ QIPBlackAndWhiteImage crop() const; QRect cropGrayScaleImage(const QIPGrayscaleImage &image); bool isNull() const; private: quint32 w, h; QSharedPointer data; private: void toImageInternal(uchar * image, const IntRect &rect, int imageWidth) const; quint8 * scanLine(quint32 y) const; inline quint8 pixel(quint32 x, quint32 y); inline void setPixel(quint32 x, quint32 y, quint8 value); bool compareElements(quint8 **se, quint8 **w, int dimensions) const; IntRect cropInternal(bool upperLeft) const; void copyInternal(quint8 * data, quint32 x1, quint32 x2, quint32 y1, quint32 y2) const; friend class QIPGrayscaleImage; friend class QIPConnectedComponents; }; #endif // QIPBLACKANDWHITEIMAGE_H yagf-0.9.3.2/src/core/qipblackandwhiteimage.cpp0000664000175000017500000003044102263216100022065 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "qipblackandwhiteimage.h" #include "common.h" #include "qipgrayscaleimage.h" static const QRgb black = qRgb(0,0,0); static const QRgb white = qRgb(255,255,255); QIPBlackAndWhiteImage::QIPBlackAndWhiteImage() : data(0) { w =0; h = 0; } QIPBlackAndWhiteImage::QIPBlackAndWhiteImage(quint32 width, quint32 height) : data(new quint8[width*height], deallocator) { w = width; h = height; // quint8 * ptr = new quint8[w*h]; // data = QSharedPointer(ptr); } QIPBlackAndWhiteImage::QIPBlackAndWhiteImage(const QIPBlackAndWhiteImage &I) : data(I.data.data(),deallocator) { w = I.w; h = I.h; } quint8 *QIPBlackAndWhiteImage::scanLine(quint32 y) const { return &(data.data()[y*w]); } quint8 QIPBlackAndWhiteImage::pixel(quint32 x, quint32 y) { return data.data()[x + y*w]; } void QIPBlackAndWhiteImage::setPixel(quint32 x, quint32 y, quint8 value) { data.data()[x+y*w] = value; } bool QIPBlackAndWhiteImage::compareElements(quint8 **se, quint8 **w, int dimensions) const { quint8 ise[dimensions][dimensions]; for (int i = 0; i < dimensions; i++) for (int j = 0; j < dimensions; j++) // for (int j = 0; j < dimensions-2; j++) if (se[i][j] == 0) if (w[i][j] != 0) return false; return true; } IntRect QIPBlackAndWhiteImage::cropInternal(bool upperLeft) const { IntRect result; if (upperLeft) { for (int y = 0; y < h; y++) { quint32 stepCount = 0; quint8 * line = scanLine(y); for (int x = 1; x < w; x++) { if (line[x] != line[x-1]) stepCount++; } if (stepCount > 4) { result.y1 = y > 2 ? y - 2 : y; result.y1 = y > 4 ? y - 4 : result.y1; break; } } for (int x = 0; x < w; x++) { quint32 bin[2] = {0,0}; quint32 min; for (int y = 0; y < h; y++) { quint8 * line = scanLine(y); bin[line[x]]++; } min = bin[0] < bin[1] ? bin[0] : bin[1]; if (min > 10) { result.x1 = x >4? x-4:x; break; } } //result.x1 = minL; } else { for (int y = h-1; y > 0; y--) { quint32 stepCount = 0; quint8 * line = scanLine(y); for (int x = 1; x < w; x++) { if (line[x] != line[x-1]) stepCount++; } if (stepCount > 4) { result.y2 = h- y > 2 ? y + 2 : y; result.y2 = h -y > 4 ? y + 4 : result.y2; //result.y2 = 500; break; } } for (int x = w-2; x > 0; x--) { quint32 bin[2] = {0,0}; quint32 min; for (int y = 0; y < h; y++) { quint8 * line = scanLine(y); bin[line[x]]++; } min = bin[0] < bin[1] ? bin[0] : bin[1]; if (min > 10) { result.x2 = x >4? x-4:x; break; } } } return result; } void QIPBlackAndWhiteImage::copyInternal(quint8 * data, quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { int wr = x2 - x1; for (int y = y1; y < y2; y++) memcpy(&(data[wr*(y-y1)]), &(scanLine(y)[x1]), wr); } QImage QIPBlackAndWhiteImage::toImage() const { QImage image(w, h, QImage::Format_ARGB32); #ifndef IPRIT_MULTITHREADING IntRect r = {0,0,image.width(),image.height()}; toImageInternal(image.scanLine(0),r, image.width()); #endif #ifdef IPRIT_MULTITHREADING IntRect r1 = {0,0,image.width(),image.height()/2}; IntRect r2 = {0,image.height()/2,image.width(),image.height()}; QFuture future1 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::toImageInternal,image.scanLine(0),r1, image.width()); QFuture future2 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::toImageInternal,image.scanLine(0),r2, image.width()); future1.waitForFinished(); future2.waitForFinished(); #endif return image; } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::copy(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { QIPBlackAndWhiteImage result(x2-x1, y2-y1); #ifndef IPRIT_MULTITHREADING copyInternal(result.data.data(), x1, x2, y1, y2); #endif #ifdef IPRIT_MULTITHREADING copyInternal(result.data.data(), x1, x2, y1, y2); QFuture future1 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::copyInternal, result.data.data(), x1, x2, y1, (y1+y2)/2); QFuture future2 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::copyInternal, result.data.data(), x1, x2, (y1+y2)/2, y2 - (y1+y2)/2); future1.waitForFinished(); future2.waitForFinished(); #endif return result; } quint32 QIPBlackAndWhiteImage::width() { return w; } quint32 QIPBlackAndWhiteImage::height() { return h; } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::dilate(quint8 *structuringElement, int dimensions) const { QIPBlackAndWhiteImage res(w, h); memset((void *)res.data.data(), 1, w*h); if ((dimensions >= w)|| (dimensions >= h)) return res; quint8 ** slines = new quint8 * [dimensions]; quint8 ** dlines = new quint8 * [dimensions]; quint8 ** elines = new quint8 * [dimensions]; for (int i = 0; i < dimensions; i++) elines[i] = &structuringElement[dimensions*i]; int dimensions_2 = dimensions/2; for (int y = dimensions_2; y < h - dimensions_2; y++) { for (int i = 0; i < dimensions; i++) { int ind = y + i - dimensions_2; slines[i] = scanLine(ind); dlines[i] = res.scanLine(ind); } for (int x = dimensions_2; x < w - dimensions_2; x++) { if (slines[dimensions_2][x] == 0) { //dlines [dimensions_2][x] = slines[dimensions_2][x]; int xs = x - dimensions_2; for (int i = 0; i < dimensions; i++) for (int j = 0; j < dimensions; j++) { dlines[i][xs+j] &= elines[i][j]; } } } } delete[] slines; delete[] elines; delete[] dlines; return res; } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::erode(quint8 *structuringElement, int dimensions) const { QIPBlackAndWhiteImage res(w, h); memset((void *)res.data.data(), 1, w*h); if ((dimensions >= w)|| (dimensions >= h)) return res; quint8 ** slines = new quint8 * [dimensions]; quint8 ** dlines = new quint8 * [dimensions]; quint8 ** elines = new quint8 * [dimensions]; quint8 * sums = new quint8 [dimensions]; for (int i = 0; i < dimensions; i++) elines[i] = &structuringElement[dimensions*i]; int seSumsTotal = 0; for (int i = 0; i < dimensions; i++) for (int j = 0; j < dimensions; j++) seSumsTotal += elines[i][j]; int dimensions_2 = dimensions/2; for (int y = dimensions_2; y < h - dimensions_2; y++) { for (int i = 0; i < dimensions; i++) { int ind = y + i - dimensions_2; slines[i] = scanLine(ind); dlines[i] = res.scanLine(ind); } //sums[0] = ; for (int x = 0; x < dimensions; x++) { sums[x] = 0; for (int i = 0; i < dimensions; i++) sums[x] += slines[i][x]; } int totalSum = 0; for (int i = 0; i < dimensions; i++) totalSum += sums[i]; if (totalSum <= seSumsTotal) { if (totalSum == 0) dlines[dimensions_2][dimensions_2] = 0; else { if (compareElements(elines, slines, dimensions)) dlines[dimensions_2][dimensions_2] = 0; } } for (int x = dimensions_2+1; x < w - dimensions_2; x++) { for (int i = 0; i < dimensions; i++) slines[i]++; //int xf = x + dimensions_2; totalSum -= sums[0]; for (int i = 1; i < dimensions; i++) sums[i-1] = sums[i]; sums[dimensions-1] = 0; for (int i = 0; i < dimensions; i++) sums[dimensions-1] += slines[i][dimensions-1]; totalSum += sums[dimensions-1]; if (totalSum <= seSumsTotal) { if (totalSum == 0) dlines[dimensions_2][x] = 0; else { if (compareElements(elines, slines, dimensions)) dlines[dimensions_2][x] = 0; } } } } delete[] slines; delete[] elines; delete[] dlines; delete[] sums; return res; } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::open(quint8 *structuringElement, int dimensions) const { return erode(structuringElement, dimensions).dilate(structuringElement, dimensions); } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::close(quint8 *structuringElement, int dimensions) const { return dilate(structuringElement, dimensions).erode(structuringElement, dimensions); } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::inverse() const { QIPBlackAndWhiteImage res(w, h); for (quint32 y = 0; y < h; y++) { quint8 * sline = scanLine(y); quint8 * dline = res.scanLine(y); for (quint32 x = 0; x < w; x++) dline[x] = 1 - sline[x]; } return res; } QIPBlackAndWhiteImage QIPBlackAndWhiteImage::crop() const { IntRect r1, r2; #ifndef IPRIT_MULTITHREADING r1 = cropInternal(true); r2 = cropInternal(false); #endif #ifdef IPRIT_MULTITHREADING QFuture future1 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::cropInternal, true); QFuture future2 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::cropInternal, false); future1.waitForFinished(); future2.waitForFinished(); r1 = future1.result(); r2 = future2.result(); #endif return copy(r1.x1, r2.x2, r1.y1, r2.y2); } QRect QIPBlackAndWhiteImage::cropGrayScaleImage(const QIPGrayscaleImage &image) { IntRect r1, r2; #ifndef IPRIT_MULTITHREADING r1 = cropInternal(true); r2 = cropInternal(false); #endif #ifdef IPRIT_MULTITHREADING QFuture future1 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::cropInternal, true); QFuture future2 = QtConcurrent::run(this, &QIPBlackAndWhiteImage::cropInternal, false); future1.waitForFinished(); future2.waitForFinished(); r1 = future1.result(); r2 = future2.result(); #endif if ((r2.x2-r1.x1 < 32)&&(r2.y2-r1.y1 < 32)) return QRect(0,0,0,0); // QIPGrayscaleImage tmp = image->copy(r1.x1, r2.x2, r1.y1, r2.y2); // delete *image; // (*image) = tmp; return QRect(r1.x1,r1.y1, r2.x2-r1.x1,r2.y2-r2.y1); } bool QIPBlackAndWhiteImage::isNull() const { return w*h == 0; } void QIPBlackAndWhiteImage::toImageInternal(uchar *image, const IntRect &rect, int imageWidth) const { int im4 =4*imageWidth; for (quint32 y = rect.y1; y < rect.y2; y++) { quint8 * line = scanLine(y); QRgb * lineOut = (QRgb *)&image[im4*y]; for (quint32 x = rect.x1; x < rect.x2; x++ ) lineOut[x] = (line[x] == 0 ? black : white); } } yagf-0.9.3.2/src/core/qipgrayscaleimage.h0000664000175000017500000002551702263216100020714 0ustar parallelsparallels/* This file is part of Libiprit by Andrei Borovsky . Libiprit is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Libiprit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Libiprit. If not, see . */ #ifndef QIPGRAYSCALEIMAGE_H #define QIPGRAYSCALEIMAGE_H #include "qipblackandwhiteimage.h" #include "common.h" #include #include #include /*! \typedef qreal QIPHistogram[256]; Array of 256 double values for storing image intensity histograms. */ typedef qreal QIPHistogram[256]; class QFile; /*! \class QIPGrayscaleImage This class holds internal representations of grayscale images and allows you to perform different grayscale operations and binarize images. You pass the data to the instance of this class when you create the instance. The colored QImage is grayscaled using one of the GrayscaleConversion types. If you want obtain the grayscaled QImage use the toImage() method. The binarize() method produces black and white (binary) images. \sa QIPBlackAndWhiteImage */ class QIPGrayscaleImage { public: QIPGrayscaleImage(); /*! These codes specify which type of conversion from RGB to 256-level grayscale is to be used. MaxEntropyChannel and MinValue suite best if you canvert to grayscale for the further text extarction. */ enum GrayscaleConversion { RGBDevideByThree = 0, /*!< gray(i) = (r(i)+g(i)+b(i))/3 */ MinMaxValue, /*!< gray(i) = (min(r(i), g(i), b(i)) + max(r(i), g(i), b(i)))/2 */ MinValue, /*!< gray(i) = min(r(i), g(i), b(i)) */ MaxValue, /*!< gray(i) = max(r(i), g(i), b(i)) */ MaxEntropyChannel, /*!< gray(i) = ch(i) where ch is either r, g or b depending on which has the maximum entropy. */ MinEntropyChannel, /*!< gray(i) = ch(i) where ch is either r, g or b depending on which has the minimum entropy. */ NoConnversion }; Q_DECLARE_FLAGS(GrayscaleTransformations, GrayscaleConversion) /*! This is the constructor for an QIPGrayscaleImage object that you should use to start your image processing. \param image the QImage object holding an image to be processed. \param conversionMethod the type of conversion from RGB to grayscale. \sa GrayscaleConversion */ explicit QIPGrayscaleImage(const QImage &image, GrayscaleConversion conversionMethod = RGBDevideByThree); /*! Makes QIPGrayscaleImage from QImage. \sa toImage */ static QIPGrayscaleImage fromImage(const QImage &image, GrayscaleConversion conversionMethod = RGBDevideByThree); /*! Returns true if the instance contains no data. */ bool isNull() const; QIPGrayscaleImage(const QIPGrayscaleImage &I); QIPGrayscaleImage(const QString &ygfFileName); ~QIPGrayscaleImage(); /*! Converts grayscaled image to QImage object. */ QImage toImage() const; /*! These flags specify which type of binariztion to use when calling binarize() method. \sa binarize() */ enum BinarizationMethod { OtsuBinarization, /*!< Otsu global binarization */ OtsuMABinarization, NiblackBinarization, /*!< Niblack adaptive binarization */ SauvolaBinarization, /*!< Sauvola adaptive binarization */ MaxEntropyBinarization, /*!< Maximum entropy global binarization */ BradleyBinarization, /*!< Bradley adaptive binarization */ IterativeBinarization, /*!< Global iterative binarization */ BernsenBinarization, /*!< Bernsen adaptive binarization */ GatosBinarization /*!< Ga'tos adaptive binarization */ }; Q_DECLARE_FLAGS(BinarizationMethods, BinarizationMethod) /*! Calculates image intensity histogram for the rectangle specified by (x1,y1) (x2,y2). the reault is returned in result variable. If x1, y1, x2, y2 are set to zero the histogram for the who;e image is calculated. \sa equalize \sa entropy */ void histogram(QIPHistogram &result, quint32 x1=0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0) const; /*! * Finds foreground/background threshold using Otsu method for the rectangle specified by (x1,y1) (x2,y2). the reault is returned in result variable. If x1, y1, x2, y2 are set to zero the result for the whole image is calculated. * Note that the values of x1, x2, y1, y2 should not exceed those of the original image (this is not checked). \sa simpleThreshold \sa maxEntropyThreshold */ quint8 otsuThreshold(quint32 x1 = 0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0) const; /*! * Finds foreground/background threshold as a mean intencity value for the rectangle specified by (x1,y1) (x2,y2). the reault is returned in result variable. If x1, y1, x2, y2 are set to zero the result for the whole image is calculated. * Note that the values of x1, x2, y1, y2 should not exceed those of the original image (this is not checked). \sa otsuThreshold \sa maxEntropyThreshold */ quint8 simpleThreshold(quint32 x1 = 0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0) const; /*! * Finds foreground/background threshold as a maximum entropy threshold for the rectangle specified by (x1,y1) (x2,y2). the reault is returned in result variable. If x1, y1, x2, y2 are set to zero the result for the whole image is calculated. * Note that the values of x1, x2, y1, y2 should not exceed those of the original image (this is not checked). \sa otsuThreshold \sa simpleThreshold */ quint8 maxEntropyThreshold(quint32 x1 = 0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0) const; /*! * Finds mean of distribution for the rectangle specified by (x1,y1) (x2,y2). the reault is returned in result variable. If x1, y1, x2, y2 are set to zero the result for the whole image is calculated. * Note that the values of x1, x2, y1, y2 should not exceed those of the original image (this is not checked). \sa otsuThreshold \sa simpleThreshold */ qreal meanOfDistribution(quint32 x1 = 0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0); /*! * Finds the entropy (-Sum(p(i)*Log(p(i)) for the rectangle specified by (x1,y1) (x2,y2). the reault is returned in result variable. If x1, y1, x2, y2 are set to zero the result for the whole image is calculated. * Note that the values of x1, x2, y1, y2 should not exceed those of the original image (this is not checked). */ qreal entropy(quint32 x1 = 0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0); /*! * Equalizes the image histogram (spreads colors). * \sa histogram */ void equalize(); qreal lpcEntropy(quint32 x1 = 0, quint32 x2 = 0, quint32 y1 = 0, quint32 y2 = 0); qreal variance(quint32 x1, quint32 x2, quint32 y1, quint32 y2); /*! * Returns the image's width. * \sa height */ quint32 width() const; /*! * Returns the image's height. * \sa width */ quint32 height() const; /*! * Returns the part of the original grayscale image bounded by the rectangle (x1,y1) (x2,y2) as a new instance of QIPGrayscaleImage. * Note that the boundaries should not exceed those of the original image (this is not checked). * \sa toImage */ QIPGrayscaleImage copy(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const; /*! Converts the grayscaled image to binary format using the specified method. */ QIPBlackAndWhiteImage binarize(BinarizationMethod method) const; /*! Sharpens the image. */ QIPGrayscaleImage sharpen() const; /*! Blurs the image. */ QIPGrayscaleImage blur() const; /*! Transforms the image so then edges appear as white lines on black. */ void isolateEdges(); /*! Inverts the intensity levels of the grayscale image. */ void invert(); void wienerFilter(); void blendImage(const QIPBlackAndWhiteImage &image); void darken(uint threshold); bool save(const QString &fileName, bool overwrite = false); static bool saveGrayscale(const QImage &image, const QString &fileName, bool overwrite = false); signals: public slots: protected: QIPGrayscaleImage(quint32 width, quint32 height); private: quint32 w, h; QSharedPointer data; enum { SharpenFilter, BlurFilter, EdgesFilter } FilterType; private: QPoint loadHeader(QFile * file); void toGSRGDBBy3(const QImage &input, int top, int left, int bottom, int right); void toImageInternal(uchar *image, const IntRect &rect, int imageWidth) const; void toGrayScale(const QImage &input); quint8 * scanLine(quint32 y) const; inline quint8 pixel(quint32 x, quint32 y) const; inline void setPixel(quint32 x, quint32 y, quint8 value); inline quint8 nextInColumn(quint32 x, quint32 &y); inline quint8 prevInColumn(quint32 x, quint32 &y); qreal cdf(QIPHistogram hist, quint8 x); quint8 predictor(quint8 *x); QIPGrayscaleImage applyFilter(int type) const; QIPBlackAndWhiteImage niblackSauvolaBinarize(bool sauvola) const; QIPBlackAndWhiteImage otsuBinarize() const; QIPBlackAndWhiteImage otsuBinarizeMA() const; QIPBlackAndWhiteImage gatosBinarize() const; QIPBlackAndWhiteImage maxEntropyBinarize() const; QIPBlackAndWhiteImage bradleyBinarize() const; QIPBlackAndWhiteImage iterativeBinarize() const; QIPBlackAndWhiteImage bernsenBinarize() const; quint8 CalculateIterativeThreshold() const; void integralImage(uint w, uint h, uint * image) const; void toGrayscaleMinMax(const QImage &input); void toGrayscaleMinOrMax(const QImage &input, bool min); void toGrayscaleMinOrMaxInternal(const QImage &input, const IntRect &rect, bool min); void toGrayscaleMinMaxInternal(const QImage &input, const IntRect &rect); void toGrayScaleByEntropyChannel(const QImage &input, bool maxEntropy); void histogramInternal(qreal *result, const IntRect &r) const; void copyInternal(const IntRect &r, uint * image) const; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QIPGrayscaleImage::BinarizationMethods) Q_DECLARE_OPERATORS_FOR_FLAGS(QIPGrayscaleImage::GrayscaleTransformations) #endif // QIPGRAYSCALEIMAGE_H yagf-0.9.3.2/src/core/imageprocessor.cpp0000664000175000017500000001607602263216100020602 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "imageprocessor.h" #include "qipgrayscaleimage.h" #include "qipblackandwhiteimage.h" #include #include #include const uint m_treshold = 0; ImageProcessor::ImageProcessor(QObject *parent) : QObject(parent) { //img = NULL; //bwimg = NULL; } ImageProcessor::~ImageProcessor() { //delete img; //delete bwimg; } QRect ImageProcessor::crop() { QIPBlackAndWhiteImage bwimg1 = QIPBlackAndWhiteImage(img.binarize(QIPGrayscaleImage::OtsuMABinarization)); //QPoint p = bwimg1.cropGrayScaleImage(&img); //delete bwimg; //bwimg = NULL; QRect r = bwimg1.cropGrayScaleImage(img); img = img.copy(r.x(), r.x()+r.width(), r.y(), r.y()+r.height()); return r; } void ImageProcessor::loadImage(const QImage &image) { img = QIPGrayscaleImage(image,QIPGrayscaleImage::MinValue); } QImage ImageProcessor::gsImage() const { return img.toImage(); } void ImageProcessor::binarize() { QIPBlackAndWhiteImage bwimg = img.binarize(QIPGrayscaleImage::GatosBinarization); img.blendImage(bwimg); //img->darken(235); } void ImageProcessor::saveYGF(const QImage &image, const QString &fileName) { //QIPGrayscaleImage gsi(image, QIPGrayscaleImage::MinValue); QIPGrayscaleImage::saveGrayscale(image, fileName); } QImage ImageProcessor::loadYGF(const QString &fileName) { QIPGrayscaleImage gsi(fileName); return gsi.toImage(); } void ImageProcessor::polishImage(QImage &image) { for(int y =1; y < image.height()-1; y++) { QRgb * line = (QRgb *) image.scanLine(y); QRgb * linep = (QRgb *) image.scanLine(y-1); QRgb * linen = (QRgb *) image.scanLine(y+1); //if for (int x = 1; x < image.width()-1; x++) { if (line[x]%255 > 64) { if ((line[x-1]%255 < 64)&&(line[x+1]%255 < 64)) line[x] = 0xFF404040; else if ((linep[x]%255 < 64)&&(linen[x]%255 < 64)) line[x] = 0xFF404040; else if ((linep[x-1]%255 < 64)&&(linen[x+1]%255 < 64)) line[x] = 0xFF404040; else if ((linep[x+1]%255 < 64)&&(linen[x-1]%255 < 64)) line[x] = 0xFF404040; } } } } void ImageProcessor::polishImage2(QImage &image) { /*for (int y = 0; y < image.height(); y++) { quint8 * line = image.scanLine(y); uint strokLen = 24; for (int x = 0; x < image.width()*4 - strokLen*4; x+=strokLen*2) { int strokes[8] = {0}; int xvals[8] = {0}; int curstrok = 0; for (int t = x+4; t < x+strokLen*4; t+=4) { if (abs(line[t]-line[t-4]) > strokes[curstrok]) { strokes[curstrok] = line[t]-line[t-4]; xvals[curstrok] = t; } else curstrok++; if (curstrok >= 8) break; } if (curstrok < 4) break; int start = -1; int stop = -1; int k = 0; while (k < curstrok -1) { if (((strokes[k] < 0)&&(strokes[k+1] < 0))||((strokes[k] > 0)&&(strokes[k+1] > 0))) { strokes[k] += strokes[k+1]; xvals[k] = xvals[k+1]; for (int l = k+1; l < curstrok-1; l++) strokes[l] = strokes[l+1]; curstrok--; } else k++; } if (curstrok < 4) break; for (int m = 1; m < curstrok-1; m++) { if (start < 0) { if (strokes[m-1] < -15) if (strokes[m] > 7) if (-strokes[m-1] > strokes[m]) start = xvals[m]; } else { if (strokes[m-1] < -7) if (strokes[m] > 15) if (strokes[m] > -strokes[m-1]) { stop = xvals[m]; break; } } if (stop > 0) { for (int p = start; p < stop; p +=4) line[p] = 60; } } } }*/ } bool ImageProcessor::isTextHorizontal(QImage &image) { return true; if (image.width() > image.height()) return true; QMap stripes; for (int y = 600; y < image.height() - 600; y++) { quint8 * line = image.scanLine(y); for (int x = 1200; x < image.width()*4 - 1200; x+=160) { if (stripes.contains(x)) { if (line[x] > 128) stripes.insert(x, stripes.value(x)+1); else if (stripes.value(x) < 600) stripes.insert(x, 0); } else stripes.insert(x, 0); } } int longCount = 0; int shortCount = 0; foreach (int count, stripes.values()) { if (count >= 600) longCount++; else shortCount++; } if (longCount*2 > shortCount) return false; if (longCount*3 > shortCount) return true; return true; } void ImageProcessor::cropAngles(QImage &image) { // int prevLen = 0; for (int y = 0; y < image.height(); y++) { QRgb * line = (QRgb *) image.scanLine(y); for (int x = 0; x < image.width(); x++) { if ((line[x]&0x00FFFFFF) == 0) line[x] =0xFFFFFFFF; else break; } for (int x = image.width()-1; x > 0; x--) { if ((line[x]&0x00FFFFFF) == 0) line[x] =0xFFFFFFFF; else break; } } for (int x = 0; x < image.width(); x++) { for (int y = 0; y < image.height(); y++) { QRgb * line = (QRgb *) image.scanLine(y); if ((line[x]&0x00FFFFFF) == 0) line[x] =0xFFFFFFFF; else break; } for (int y = image.height()-1; y > 0 ; y--) { QRgb * line = (QRgb *) image.scanLine(y); if ((line[x]&0x00FFFFFF) == 0) line[x] =0xFFFFFFFF; else break; } } } yagf-0.9.3.2/src/core/util.h0000664000175000017500000000426202263216100016174 0ustar parallelsparallels#ifndef UTIL_H #define UTIL_H /*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) You can redistribute this library and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.*/ #if !defined(_qrcode_util_H) # define _qrcode_util_H (1) #define QR_MAXI(_a,_b) ((_a)-((_a)-(_b)&-((_b)>(_a)))) #define QR_MINI(_a,_b) ((_a)+((_b)-(_a)&-((_b)<(_a)))) #define QR_SIGNI(_x) (((_x)>0)-((_x)<0)) #define QR_SIGNMASK(_x) (-((_x)<0)) /*Unlike copysign(), simply inverts the sign of _a if _b is negative.*/ #define QR_FLIPSIGNI(_a,_b) ((_a)+QR_SIGNMASK(_b)^QR_SIGNMASK(_b)) #define QR_COPYSIGNI(_a,_b) QR_FLIPSIGNI(abs(_a),_b) /*Divides a signed integer by a positive value with exact rounding.*/ #define QR_DIVROUND(_x,_y) (((_x)+QR_FLIPSIGNI(_y>>1,_x))/(_y)) #define QR_CLAMPI(_a,_b,_c) (QR_MAXI(_a,QR_MINI(_b,_c))) #define QR_CLAMP255(_x) ((unsigned char)((((_x)<0)-1)&((_x)|-((_x)>255)))) /*Swaps two integers _a and _b if _a>_b.*/ #define QR_SORT2I(_a,_b) \ do{ \ int t__; \ t__=QR_MINI(_a,_b)^(_a); \ (_a)^=t__; \ (_b)^=t__; \ } \ while(0) #define QR_ILOG0(_v) (!!((_v)&0x2)) #define QR_ILOG1(_v) (((_v)&0xC)?2+QR_ILOG0((_v)>>2):QR_ILOG0(_v)) #define QR_ILOG2(_v) (((_v)&0xF0)?4+QR_ILOG1((_v)>>4):QR_ILOG1(_v)) #define QR_ILOG3(_v) (((_v)&0xFF00)?8+QR_ILOG2((_v)>>8):QR_ILOG2(_v)) #define QR_ILOG4(_v) (((_v)&0xFFFF0000)?16+QR_ILOG3((_v)>>16):QR_ILOG3(_v)) /*Computes the integer logarithm of a (positive, 32-bit) constant.*/ #define QR_ILOG(_v) ((int)QR_ILOG4((unsigned)(_v))) /*Multiplies 32-bit numbers _a and _b, adds (possibly 64-bit) number _r, and takes bits [_s,_s+31] of the result.*/ #define QR_FIXMUL(_a,_b,_r,_s) ((int)((_a)*(long long)(_b)+(_r)>>(_s))) /*Multiplies 32-bit numbers _a and _b, adds (possibly 64-bit) number _r, and gives all 64 bits of the result.*/ #define QR_EXTMUL(_a,_b,_r) ((_a)*(long long)(_b)+(_r)) unsigned qr_isqrt(unsigned _val); unsigned qr_ihypot(int _x,int _y); int qr_ilog(unsigned _val); #endif #endif // UTIL_H yagf-0.9.3.2/src/core/util.cpp0000664000175000017500000000674602263216100016540 0ustar parallelsparallels/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) You can redistribute this library and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.*/ #include #include "util.h" /*Computes floor(sqrt(_val)) exactly.*/ unsigned qr_isqrt(unsigned _val){ unsigned g; unsigned b; int bshift; /*Uses the second method from http://www.azillionmonkeys.com/qed/sqroot.html The main idea is to search for the largest binary digit b such that (g+b)*(g+b) <= _val, and add it to the solution g.*/ g=0; b=0x8000; for(bshift=16;bshift-->0;){ unsigned t; t=(g<<1)+b<>=1; } return g; } /*Computes sqrt(_x*_x+_y*_y) using CORDIC. This implementation is valid for all 32-bit inputs and returns a result accurate to about 27 bits of precision. It has been tested for all postiive 16-bit inputs, where it returns correctly rounded results in 99.998% of cases and the maximum error is 0.500137134862598032 (for _x=48140, _y=63018). Very nearly all results less than (1<<16) are correctly rounded. All Pythagorean triples with a hypotenuse of less than ((1<<27)-1) evaluate correctly, and the total bias over all Pythagorean triples is -0.04579, with a relative RMS error of 7.2864E-10 and a relative peak error of 7.4387E-9.*/ unsigned qr_ihypot(int _x,int _y){ unsigned x; unsigned y; int mask; int shift; int u; int v; int i; x=_x=abs(_x); y=_y=abs(_y); mask=-(x>y)&(_x^_y); x^=mask; y^=mask; _y^=mask; shift=31-qr_ilog(y); shift=QR_MAXI(shift,0); x=(unsigned)((x<>32); _y=(int)((_y<>32); u=x; mask=-(_y<0); x+=_y+mask^mask; _y-=u+mask^mask; u=x+1>>1; v=_y+1>>1; mask=-(_y<0); x+=v+mask^mask; _y-=u+mask^mask; for(i=1;i<16;i++){ int r; u=x+1>>2; r=(1<<2*i)>>1; v=_y+r>>2*i; mask=-(_y<0); x+=v+mask^mask; _y=_y-(u+mask^mask)<<1; } return x+((1U<>1)>>shift; } #if defined(__GNUC__) && defined(HAVE_FEATURES_H) # include # if __GNUC_PREREQ(3,4) # include # if INT_MAX>=2147483647 # define QR_CLZ0 sizeof(unsigned)*CHAR_BIT # define QR_CLZ(_x) (__builtin_clz(_x)) # elif LONG_MAX>=2147483647L # define QR_CLZ0 sizeof(unsigned long)*CHAR_BIT # define QR_CLZ(_x) (__builtin_clzl(_x)) # endif # endif #endif int qr_ilog(unsigned _v){ #if defined(QR_CLZ) /*Note that __builtin_clz is not defined when _x==0, according to the gcc documentation (and that of the x86 BSR instruction that implements it), so we have to special-case it.*/ return QR_CLZ0-QR_CLZ(_v)&-!!_v; #else int ret; int m; m=!!(_v&0xFFFF0000)<<4; _v>>=m; ret=m; m=!!(_v&0xFF00)<<3; _v>>=m; ret|=m; m=!!(_v&0xF0)<<2; _v>>=m; ret|=m; m=!!(_v&0xC)<<1; _v>>=m; ret|=m; ret|=!!(_v&0x2); return ret + !!_v; #endif } #if defined(QR_TEST_SQRT) #include #include /*Exhaustively test the integer square root function.*/ int main(void){ unsigned u; u=0; do{ unsigned r; unsigned s; r=qr_isqrt(u); s=(int)sqrt(u); if(r!=s)printf("%u: %u!=%u\n",u,r,s); u++; } while(u); return 0; } #endif yagf-0.9.3.2/src/core/qipgrayscaleimage.cpp0000664000175000017500000011035202263216100021237 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "settings.h" #include "qipgrayscaleimage.h" #include "binarize.h" #include #include "common.h" #include #include const QString fheader = QString::fromUtf8("YGF1"); static const int kSharpen [3][3]= {{0,-1,0}, {-1,5,-1}, {0,-1,0}}; static const int wkSharpen = 1; static const int kBlur [3][3]= {{1,1,1}, {1,1,1}, {1,1,1}}; static const int wkBlur = 5; static const int kEdges [3][3]= {{-1,-1,-1}, {-1,8,-1}, {-1,-1,-1}}; static const int wkEdges = 1; QIPGrayscaleImage::QIPGrayscaleImage(const QImage &image, GrayscaleConversion conversionMethod) : data(new quint8[image.width()*image.height()], deallocator) { h = image.height(); w = image.width(); //data = QSharedPointer(); switch (conversionMethod) { case RGBDevideByThree: toGrayScale(image); break; case MinMaxValue: toGrayscaleMinMax(image); break; case MinValue: toGrayscaleMinOrMax(image, true); break; case MaxValue: toGrayscaleMinOrMax(image, false); break; case MaxEntropyChannel: toGrayScaleByEntropyChannel(image, true); break; case MinEntropyChannel: toGrayScaleByEntropyChannel(image, false); break; default: toGrayScale(image); break; } } QIPGrayscaleImage QIPGrayscaleImage::fromImage(const QImage &image, QIPGrayscaleImage::GrayscaleConversion conversionMethod) { return QIPGrayscaleImage(image, conversionMethod); } bool QIPGrayscaleImage::isNull() const { return w*h == 0; } QIPGrayscaleImage::QIPGrayscaleImage(const QIPGrayscaleImage &I) : data(I.data.data(), deallocator) { w = I.w; h = I.h; } QIPGrayscaleImage::QIPGrayscaleImage(const QString &ygfFileName) { w = 0; h = 0; QFile f(ygfFileName); if (!f.open(QIODevice::ReadOnly)) return; QPoint p = loadHeader(&f); w = p.x(); h = p.y(); if (w*h == 0) { f.close(); return; } //quint8 * d = ; data = QSharedPointer(new quint8[w*h], deallocator); f.read((char*)data.data(), w*h); f.flush(); f.close(); } QIPGrayscaleImage::~QIPGrayscaleImage() { //delete [] data; } QImage QIPGrayscaleImage::toImage() const { QImage image(w, h, QImage::Format_ARGB32); #ifndef IPRIT_MULTITHREADING IntRect r = {0,0,image.width(),image.height()}; toImageInternal(image.scanLine(0),r, image.width()); #endif #ifdef IPRIT_MULTITHREADING IntRect r1 = {0,0,image.width(),image.height()/2}; IntRect r2 = {0,image.height()/2,image.width(),image.height()}; QFuture future1 = QtConcurrent::run(this, &QIPGrayscaleImage::toImageInternal,image.scanLine(0),r1, image.width()); QFuture future2 = QtConcurrent::run(this, &QIPGrayscaleImage::toImageInternal,image.scanLine(0),r2, image.width()); future1.waitForFinished(); future2.waitForFinished(); #endif return image; } void QIPGrayscaleImage::histogram(QIPHistogram &result, quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { if (x2 == 0) x2 = w; if (y2 == 0) y2 = h; #ifndef IPRIT_MULTITHREADING IntRect r = {x1,y1,x2,y2}; histogramInternal(&result[0], r); #endif #ifdef IPRIT_MULTITHREADING IntRect r1 = {x1,y1,x2,(y1+y2)/2}; IntRect r2 = {x1,(y1+y2)/2,x2,y2}; qreal h1[256] = {0.0}; qreal h2[256] = {0.0}; QFuture future1 = QtConcurrent::run(this, &QIPGrayscaleImage::histogramInternal,&h1[0],r1); QFuture future2 = QtConcurrent::run(this, &QIPGrayscaleImage::histogramInternal,&h2[0],r2); future1.waitForFinished(); future2.waitForFinished(); for (int i = 0; i < 256; i++) result[i] = (h1[i] + h2[i])/2.; #endif } void QIPGrayscaleImage::histogramInternal(qreal * result, const IntRect &r) const { uint ht[256] = {0}; for (quint32 y = r.y1; y < r.y2; y++) { quint8 * lineIn = scanLine(y); for(quint32 x = r.x1; x < r.x2; x++) { ht[lineIn[x]]++; } } int size = (r.x2-r.x1)*(r.y2-r.y1); for (int i = 0; i < 256; i++) { result[i] = ht[i]; result[i]/=size; } } void QIPGrayscaleImage::copyInternal(const IntRect &r, uint *image) const { for (uint y = r.y1; y < r.y2; y++) { quint8 * line = scanLine(y); uint * lineout = &image[y*w]; for (uint x = r.x1; x < r.x2; x++) lineout[x] = line[x]; } } void QIPGrayscaleImage::darken(uint threshold) { uint dataSize = w*h; quint8 * d = (quint8 *)data.data(); uint ac = 0; for (uint i = 0; i < dataSize; i++) { ac = ac + d[i]; } ac = ac/dataSize; if (ac > threshold) for (uint i = 0; i < dataSize; i++) { d[i] = qMax(0, d[i]*d[i]/255); } } bool QIPGrayscaleImage::save(const QString &fileName, bool overwrite) { quint16 hx = h; quint16 wx = w; QFile f(fileName); if ((f.exists())&&(!overwrite)) return false; bool res = f.open(QIODevice::WriteOnly); if (!res) return false; f.write(fheader.toAscii(), 4); f.write((char*) &hx, 2); f.write((char*) &wx, 2); f.write((char*)data.data(), wx*hx); f.flush(); f.close(); return true; } bool QIPGrayscaleImage::saveGrayscale(const QImage &image, const QString &fileName, bool overwrite) { quint16 hx = image.height(); quint16 wx = image.width(); QFile f(fileName); if ((f.exists())&&(!overwrite)) return false; if(!f.open(QIODevice::WriteOnly)) return false; f.write(fheader.toAscii(), 4); f.write((char*) &hx, 2); f.write((char*) &wx, 2); quint8 * d = new quint8[wx]; for (int y = 0; y < hx; y++) { quint8 * line = (quint8 *) image.scanLine(y); for (int x = 0; x < wx; x++) d[x] = line[x*4]; quint64 rl = f.write((char*)d,wx); while (rl != 0) rl = f.write((char*)&d[rl],wx-rl); } delete[] d; f.flush(); f.close(); return true; } quint32 QIPGrayscaleImage::width() const { return w; } quint32 QIPGrayscaleImage::height() const { return h; } QIPGrayscaleImage QIPGrayscaleImage::copy(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { QIPGrayscaleImage result(x2 - x1, y2 - y1); for (uint y = y1; y < y2; y ++) { quint8 * src = &scanLine(y)[x1]; quint8 * dst = result.scanLine(y-y1); memcpy(dst, src, result.w); } return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::binarize(QIPGrayscaleImage::BinarizationMethod method) const { switch (method) { case OtsuBinarization : return otsuBinarize(); break; case OtsuMABinarization: return otsuBinarizeMA(); break; case NiblackBinarization : return niblackSauvolaBinarize(false); break; case SauvolaBinarization : return niblackSauvolaBinarize(true); break; case MaxEntropyBinarization : return maxEntropyBinarize(); break; case BradleyBinarization : return bradleyBinarize(); break; case IterativeBinarization: return iterativeBinarize(); break; case BernsenBinarization: return bernsenBinarize(); break; case GatosBinarization: return gatosBinarize(); break; default: break; } return QIPBlackAndWhiteImage(0,0); } QIPGrayscaleImage QIPGrayscaleImage::sharpen() const { return applyFilter(SharpenFilter); } QIPGrayscaleImage QIPGrayscaleImage::blur() const { return applyFilter(BlurFilter); } void QIPGrayscaleImage::isolateEdges() { applyFilter(EdgesFilter); } void QIPGrayscaleImage::invert() { for (uint y = 0; y < h; y++) { quint8 * line = scanLine(y); for (uint x = 0; x < w; x++) line[x] = 255 - line[x]; } } void QIPGrayscaleImage::wienerFilter() { qr_wiener_filter(data.data(),w,h); } void QIPGrayscaleImage::blendImage(const QIPBlackAndWhiteImage &image) { if (w*h < 32) return; if (h < 4) return; const quint8 * bw = image.data.data(); quint8 * gs = data.data(); for (int i = 0; i < w; i++) gs[i] = bw[i] == 1 ? qMin(gs[i] + 32, 255) : gs[i]*3/4; for (int i = 0; i < w*h; i+=w) { gs[i] = bw[i] == 1 ? qMin(gs[i] + 32, 255) : gs[i]*3/4; gs[i+w-1] = bw[i+w-1] == 1 ? qMin(gs[i+w-1] + 32, 255) : gs[i+w-1]*3/4; } for (int i = w*(h-1); i < w*h; i++) gs[i] = bw[i] == 1 ? qMin(gs[i] + 32, 255) : gs[i]*3/4; int up =Settings::instance()->getForegroundBrightenFactor(); uint d1 = 3; uint d2 = 4; uint ra = 0; uint c = 0; for (int i = w+1; i < w*(h-1)-1; i++) { if (bw[i] == 1) { ra += gs[i]; c++; if ((bw[i-1] != 0)||(bw[i+1] != 0)) if ((bw[i-2] != 0)||(bw[i+1] != 0)) if ((bw[i-w] != 0)||(bw[i+w] != 0)) if ((bw[i-w-1] != 0)||(bw[i+w+1] != 0)) if ((bw[i-w+1] != 0)||(bw[i+w-1] != 0)) gs[i] = qMin(gs[i] + up, 255); } else gs[i] = gs[i]*d1/d2; } up = Settings::instance()->getGlobalBrightenFactor(); if (ra/c < Settings::instance()->getDarkBackgroundThreshold()) for (int i = w+1; i < w*(h-1)-1; i++) gs[i] = qMin(gs[i]+up, 255); up = Settings::instance()->getGlobalDarkenFactor(); int thr = Settings::instance()->getGlobalDarkenThreshold(); for (int i = w+1; i < w*(h-1)-1; i++) if ((gs[i] < thr)&&(gs[i]>up)) gs[i]-=up; } QIPGrayscaleImage::QIPGrayscaleImage(quint32 width, quint32 height) : data(new quint8[width*height]) { w = width; h = height; } QPoint QIPGrayscaleImage::loadHeader(QFile *file) { QPoint res(0,0); char header[5] = {0}; file->read(header, 4); if (QString::fromAscii(header) != fheader) return res; quint16 wx, hx; file->read((char*) &hx, 2); file->read((char*) &wx, 2); res.setX(wx); res.setY(hx); return res; } void QIPGrayscaleImage::toGSRGDBBy3(const QImage &input, int top, int left, int bottom, int right) { uchar lut[256*3] = {0}; int lutcount = 0; for (int i = 0; i < 256; i++) { for(int j = 0; j < 3; j++) { lut[lutcount] = i; lutcount++; } } for (int y = top; y < bottom; y++) { QRgb * lineIn = (QRgb *)input.scanLine(y); quint8 * lineOut = scanLine(y); for(int x = left; x < right; x++) { QRgb cur = lineIn[x]; uint grayLevel = (qRed(cur) + qGreen(cur) + qBlue(cur)); lineOut[x] = lut[grayLevel]; } } } void QIPGrayscaleImage::toImageInternal(uchar * image, const IntRect &rect, int imageWidth) const { int im4 =4*imageWidth; for (quint32 y = rect.y1; y < rect.y2; y++) { quint8 * line = scanLine(y); QRgb * lineOut = (QRgb *)&(image[im4*y]); for (quint32 x = rect.x1; x < rect.x2; x++ ) { lineOut[x] = qRgb(line[x], line[x], line[x]); } } } void QIPGrayscaleImage::toGrayScale(const QImage &input) { #ifndef IPRIT_MULTITHREADING toGSRGDBBy3(input,0,0,input.height(),input.width()); #endif #ifdef IPRIT_MULTITHREADING QFuture future1 = QtConcurrent::run(this, &QIPGrayscaleImage::toGSRGDBBy3,input,0,0,input.height()/2,input.width()); QFuture future2 = QtConcurrent::run(this, &QIPGrayscaleImage::toGSRGDBBy3,input,input.height()/2,0,input.height(),input.width()); future1.waitForFinished(); future2.waitForFinished(); #endif } quint8 QIPGrayscaleImage::otsuThreshold(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { qreal hist[256]; histogram(hist, x1, x2, y1, y2); qreal ihist[256]; qreal ut = 0; for (int i = 0; i < 256; i++) ut+= (ihist[i] = i*hist[i]); int maxK=0; int maxSigmaK=0; qreal wk = 0; qreal uk = 0; for (int k = 0; k < 256; ++k) { if (hist[k] == 0) continue; wk += hist[k]; uk += ihist[k]; qreal sigmaK = 0; if ((wk !=0) && (wk!=1)) sigmaK = ((ut*wk - uk)*(ut*wk - uk))/(wk*(1-wk)); if (sigmaK > maxSigmaK) { maxK = k; maxSigmaK = sigmaK; } } return maxK; } quint8 QIPGrayscaleImage::simpleThreshold(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { if (x2 == 0) x2 = w; if (y2 == 0) y2 = h; quint32 accum = 0; for (uint y = y1; y < y2; y ++) { quint8 * lineIn = scanLine(y); for(quint32 x = x1; x < x2; x++) accum += lineIn[x]; } return accum/((x2-x1)*(y2-y1)); } quint8 QIPGrayscaleImage::maxEntropyThreshold(quint32 x1, quint32 x2, quint32 y1, quint32 y2) const { if (x2 == 0) x2 = w; if (y2 == 0) y2 = h; qreal hist[256]; histogram(hist, x1, x2, y1, y2); qreal cdv[256]; cdv[0] = hist[0]; for (uint i = 1; i < 256; i++) cdv[i] = cdv[i - 1] + hist[i]; qreal epsilon = .1e-256; qreal hBlack[256] = {0.0}; qreal hWhite[256] = {0.0}; for (uint t = 0; t < 256; t++) { if (cdv[t] > epsilon) { qreal hhB = 0; for (int i = 0; i <= t; i++) { if (hist[i] > epsilon) hhB -= hist[i] / cdv[t] * log(hist[i] / cdv[t]); } hBlack[t] = hhB; } else { hBlack[t] = 0; } double cdi = 1 - cdv[t]; if (cdi > epsilon) { qreal hhW = 0; for (int i = t + 1; i < 256; ++i) { if (hist[i] > epsilon) hhW -= hist[i] / cdi * log(hist[i] / cdi); } hWhite[t] = hhW; } else { hWhite[t] = 0; } } qreal maxIndex = hBlack[0] + hWhite[0]; quint8 maxT = 0; for (uint t = 1; t < 256; t++) { double j = hBlack[t] + hWhite[t]; if (j > maxIndex) { maxIndex = j; maxT = t; } } return maxT; } qreal QIPGrayscaleImage::meanOfDistribution(quint32 x1, quint32 x2, quint32 y1, quint32 y2) { qreal hist[256]; histogram(hist, x1, x2, y1, y2); qreal result = 0; for (uint i = 0; i < 256; i++) result += hist[i]*i; return result; } qreal QIPGrayscaleImage::entropy(quint32 x1, quint32 x2, quint32 y1, quint32 y2) { qreal hist[256]; histogram(hist, x1, x2, y1, y2); qreal result = 0; for (uint i = 0; i < 256; i++) result += hist[i]*log(hist[i]); return -result; } void QIPGrayscaleImage::equalize() { qreal hist[256]; histogram(hist); quint8 tt[256]; //tt[0] = 0; for (uint i = 0; i < 256; i++) { tt[i] = cdf(hist, i)*255; } for (int y = 0; y < h; y++) { quint8 * lineOut = scanLine(y); for(int x = 0; x < w; x++) lineOut[x] = tt[lineOut[x]]; } } qreal QIPGrayscaleImage::lpcEntropy(quint32 x1, quint32 x2, quint32 y1, quint32 y2) { uint ihist[256] = {0}; if (x2 == 0) x2 = w; if (y2 == 0) y2 = h; for (int y = y1; y < y2; y++) { quint8 * lineOut = scanLine(y); ihist[lineOut[x1]]++; ihist[lineOut[x1+1]]++; ihist[lineOut[x1+2]]++; quint8 cl = lineOut[x1+2]; for(uint x = x1 + 3; x < x2; x++) { quint8 cp = predictor(&lineOut[x]); if ((cp-lineOut[x])*(cp-lineOut[x]) < 2) { ihist[cl]++; } else { cl = lineOut[x]; ihist[cl]++; } } } qreal hist[256] = {0.0}; qreal k = 1/((x2-x1)*(y2-y1)); qreal sum = 0; for (uint i =0; i < 256; i++) { hist[i] = ihist[i]*k*log(ihist[i]*k); sum += hist[i]; } return -sum; } qreal QIPGrayscaleImage::variance(quint32 x1, quint32 x2, quint32 y1, quint32 y2) { qreal hist[256]; histogram(hist, x1, x2, y1, y2); qreal max = 0; quint8 imax; for (int i = 0; i < 256; i++) if (hist[i] > max) { max = hist[i]; imax = i; } qreal sum = 0; for (int i = 0; i < 256; i++) sum += abs(i-imax)*hist[i]; return sum; } quint8 *QIPGrayscaleImage::scanLine(quint32 y) const { return &(data.data()[y*w]); } quint8 QIPGrayscaleImage::pixel(quint32 x, quint32 y) const { return data.data()[x + y*w]; } void QIPGrayscaleImage::setPixel(quint32 x, quint32 y, quint8 value) { data.data()[x+y*w] = value; } quint8 QIPGrayscaleImage::nextInColumn(quint32 x, quint32 &y) { y++; return data.data()[x + y*w]; } quint8 QIPGrayscaleImage::prevInColumn(quint32 x, quint32 &y) { y--; return data.data()[x + y*w]; } qreal QIPGrayscaleImage::cdf(QIPHistogram hist, quint8 x) { qreal result = 0; for (uint i = 0; i < x+1; i++) result +=hist[i]; return result; } quint8 QIPGrayscaleImage::predictor(quint8 * x) { quint8 xp[3] = {*(x-3), *(x-2), *(x-1) }; int d1 = xp[1] - xp[0]; int d2 = xp[2] - xp[1]; int dp = 2*d2 - d1; int xd = *x + dp; xd = qBound(0, xd, 255); return (quint8) xd; } QIPGrayscaleImage QIPGrayscaleImage::applyFilter(int type) const { QIPGrayscaleImage result(w, h); int kernel [3][3]; int wk; int a; if (type == SharpenFilter) { memcpy(kernel, kSharpen, 9*sizeof(int)); wk = wkSharpen; a = 0; } if (type == BlurFilter) { memcpy(kernel, kBlur, 9*sizeof(int)); wk = wkBlur; a = 0; } if (type == EdgesFilter) { memcpy(kernel, kEdges, 9*sizeof(int)); wk = wkEdges; a = 128; } for(int x= 1; x < w - 1; x++) { for(int y= 1; y < h - 1; y++) { int c = 0; for(int i = -1; i <= 1; i++) { for(int j = -1; j <= 1; j++) c += pixel(x+i, y+j)*kernel[i+1][j+1]; c = qBound(0, c/wk + a, 255); result.setPixel(x, y, c); } } } if (type==SharpenFilter) result.invert(); return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::niblackSauvolaBinarize(bool sauvola) const { QIPBlackAndWhiteImage result(w, h); const uint WindowSize = 15; const uint halfWindowSize = WindowSize/2; const qreal weight = 0.2; uint xMin = halfWindowSize; uint yMin = halfWindowSize; int xMax = w - 1 - halfWindowSize; int yMax = h - 1 - halfWindowSize; quint8 * output = result.data.data(); qreal sumPixelsWindow = 0; qreal sum2PixelsWindow = 0; qreal localMean = 0; qreal localVar = 0; qreal localStd = 0; qreal localValue = 0; qreal mainSumPixelWindow = 0; qreal mainSum2PixelWindow = 0; qreal *sumCols = new qreal[w]; //= {0.0}; qreal *sumSqCols = new qreal[w];// = {0.0}; memset(sumCols, 0, w*sizeof(qreal)); memset(sumSqCols, 0, w*sizeof(qreal)); for (uint i = 0; i < WindowSize; i++){ for (uint x = 0; x < w; x++){ sumCols[x] += pixel(x, i); sumSqCols[x] += pixel(x, i)*pixel(x, i); } } for(uint j = 0; j < WindowSize; j++){ mainSumPixelWindow += sumCols[j]; mainSum2PixelWindow += sumSqCols[j]; } for(int y = yMin; y <= yMax; y++) { for(int j = xMin; j <= xMax; j++) { if(y == xMin){ if(j == yMin) { sumPixelsWindow = mainSumPixelWindow; sum2PixelsWindow = mainSum2PixelWindow; }else{ sumPixelsWindow += (sumCols[j+halfWindowSize] - sumCols[j-halfWindowSize-1]); sum2PixelsWindow += (sumSqCols[j+halfWindowSize] - sumSqCols[j-halfWindowSize-1]); } }else{ if(j == yMin) { for (uint x = 0; x < w; x++) { sumCols[x] += (pixel(x, y+halfWindowSize) - pixel(x, y-halfWindowSize-1)); sumSqCols[x] += (pixel (x, y+halfWindowSize)*pixel (x, y+halfWindowSize) - pixel(x, y-halfWindowSize-1)*pixel(x, y-halfWindowSize-1)); } mainSumPixelWindow = 0; mainSum2PixelWindow = 0; for(uint k = 0; k < WindowSize; k++) { mainSumPixelWindow += sumCols[k]; mainSum2PixelWindow += sumSqCols[k]; } sumPixelsWindow = mainSumPixelWindow; sum2PixelsWindow = mainSum2PixelWindow; }else{ sumPixelsWindow += (sumCols[j+halfWindowSize] - sumCols[j-halfWindowSize-1]); sum2PixelsWindow += (sumSqCols[j+halfWindowSize] - sumSqCols[j-halfWindowSize-1]); } } localMean = sumPixelsWindow/(WindowSize*WindowSize); localVar = sum2PixelsWindow/(WindowSize*WindowSize) - localMean*localMean; localStd = sqrt(fabs(localVar)); if (!sauvola) localValue = localMean+weight*localStd; else localValue = localMean*(1 + (localStd/128-1)/6); if(pixel(j, y) < localValue) output[y*w + j] = 0; else output[y*w + j] = 1; } } for(uint y = 0; y < h; y++){ for(uint x = 0; x < w; x++){ if(y < yMin) output[y*w + x] = 1; if(y > yMax) output[y*w + x] = 1; if(x < xMin) output[y*w + x] = 1; if(x > xMax) output[y*w + x] = 1; } } delete [] sumCols; delete [] sumSqCols; return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::otsuBinarize() const { QIPBlackAndWhiteImage result(w, h); quint8 threshold = otsuThreshold(); for (uint y = 0; y < h; y ++) { quint8 * line = scanLine(y); quint8 * lineOut = result.scanLine(y); for (uint x = 0; x < w; x++) { if (line[x] > threshold) lineOut[x] = 1; else lineOut[x] = 0; } } return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::otsuBinarizeMA() const { quint8 stack[3]; quint32 sum; QIPBlackAndWhiteImage result(w, h); if (w*h < 4) return result; quint8 threshold = otsuThreshold(); memcpy((void*)result.data.data(), (void*)data.data(), w*h); quint8 * d = result.data.data(); sum = d[0] + d[1] + d[2]+ d[3] + d[4] + d[5] + d[6]; for (int i = 3; i < w*h-4; i++) { d[i] = sum/8; sum = sum - d[i-3] + d[i+4]; } for (uint y = 0; y < h; y ++) { quint8 * lineOut = result.scanLine(y); for (uint x = 0; x < w; x++) { if (lineOut[x] > threshold) lineOut[x] = 1; else lineOut[x] = 0; } } return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::gatosBinarize() const { QIPBlackAndWhiteImage result(w, h); memcpy(result.data.data(), data.data(), w*h); quint8 * d = result.data.data(); qr_binarize(d, w, h); for (int y = 0; y < h; y++) { quint8 * line = result.scanLine(y); for (int x = 0; x < w; x++) if (line[x] == 0) line[x] = 1; else line[x] = 0; } return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::maxEntropyBinarize() const { QIPBlackAndWhiteImage result(w, h); quint8 threshold = maxEntropyThreshold(); for (uint y = 0; y < h; y ++) { quint8 * line = scanLine(y); quint8 * lineOut = result.scanLine(y); for (uint x = 0; x < w; x++) { if (line[x] >= threshold) lineOut[x] = 1; else lineOut[x] = 0; } } return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::bradleyBinarize() const { const uint windowSize = 41; const qreal pixelBrightnessDifferenceLimit = 0.15; QIPBlackAndWhiteImage result(w, h); quint8 * resultData = result.data.data(); memcpy(resultData, data.data(), w*h); uint * intImage = new uint[w*h]; integralImage(w, h, intImage); int halfWindowSize = windowSize / 2; qreal avgBrightnessPart = 1.0f - pixelBrightnessDifferenceLimit; for (int y = 0; y < h; y++) { int y1 = y - halfWindowSize > 0 ? y - halfWindowSize : 0; int y2 = y + halfWindowSize > h-1 ? h-1 : y + halfWindowSize; for (int x = 0; x < w; x++) { int x1 = x - halfWindowSize < 0 ? 0 : x - halfWindowSize; int x2 = x + halfWindowSize < w - 1 ? x + halfWindowSize : w - 1; resultData[x + y*w] = resultData[x + y*w] < ( intImage[y2*w + x2] + intImage[y1*w + x1] - intImage[y1*w + x2] - intImage[y2*w + x1])/((x2-x1)*(y2-y1))*avgBrightnessPart ? 0 : 1; } } delete [] intImage; return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::iterativeBinarize() const { QIPBlackAndWhiteImage result(w, h); quint8 threshold = CalculateIterativeThreshold(); for (uint y = 0; y < h; y++) { quint8 * line = scanLine(y); quint8 * lineOut = result.scanLine(y); for (uint x = 0; x < w; x++) lineOut[x] = line[x] < threshold ? 0 : 1; } return result; } QIPBlackAndWhiteImage QIPGrayscaleImage::bernsenBinarize() const { const uint regSize = 7; const quint8 contrastLimit = 48; const quint8 confused = 1; QIPBlackAndWhiteImage result(w, h); for (uint y = 0; y < h; y++) { const quint8 * line = scanLine(y); quint8 * lineOut = result.scanLine(y); int istart = y == 0 ? 0 : y - 1; int istop = y + regSize >= h ? h : y + regSize - 1; quint8 minimum = 255; quint8 maximum = 0; int maxX = -1; int minX = -1; for (uint x = 0; x < w; x++) { int jstart = x == 0 ? 0 : x-1; int jstop = x+regSize >= w ? w : x + regSize - 1; if ((maxX < jstart)||(minX < jstart)) { minimum = 255; maximum = 0; for (uint i = istart; i < istop; i++) { const quint8 * lineK = scanLine(i); for (uint j = jstart; j < jstop; j++) { if (minimum > lineK[j]) { minimum = lineK[j]; minX = j; } if (maximum < lineK[j]) { maximum = lineK[j]; maxX = j; } } } } else { for (int i = istart; i < istop; i++) { if (minimum > scanLine(i)[jstop-1]) { minimum = scanLine(i)[jstop-1]; minX = jstop-1; } if (maximum < scanLine(i)[jstop-1]) { maximum = scanLine(i)[jstop-1]; maxX = jstop-1; } } } quint8 c = maximum - minimum; quint8 value; if (c < contrastLimit) value = confused; else { uint p = (maximum + minimum) / 2; value = line[x] >= p ? 1 : 0; } lineOut[x] = value; } } return result; } quint8 QIPGrayscaleImage::CalculateIterativeThreshold() const { quint32 distribution[256] = {0}; for (uint y = 0; y < h; y++) { quint8 * line = scanLine(y); for (uint x = 0; x < w; x++) distribution[line[x]]++; } quint32 integralHist[256]; quint32 integralDistribution[256]; integralHist[0] = 0; integralDistribution[0] = distribution[0]; for (int i = 1; i < 256; i++) { integralHist[i] = integralHist[i-1] + distribution[i]*i; integralDistribution[i] = integralDistribution[i-1] + distribution[i]; } quint8 oldThreshold = 0; quint8 newThreshold = 128; while (abs(newThreshold - oldThreshold) > 0) { uint meanWhite; uint meanBlack; uint sumBlack = integralHist[newThreshold]; uint numBlack = integralDistribution[newThreshold]; uint sumWhite = integralHist[255] - integralHist[newThreshold]; uint numWhite = integralDistribution[255] - integralDistribution[newThreshold]; /*for (int i = 0; i < 256; i++) if (i < newThreshold) { sumBlack += valueHist[i]; numBlack += distribution[i]; } else { sumWhite += valueHist[i]; numWhite += distribution[i]; }*/ meanBlack = sumBlack != 0 ? sumBlack/numBlack : 0; meanWhite = sumWhite != 0 ? sumWhite/numWhite : 0; oldThreshold = newThreshold; newThreshold = (meanBlack + meanWhite)/2; } return newThreshold; } void QIPGrayscaleImage::integralImage(uint w, uint h, uint *image) const { #ifndef IPRIT_MULTITHREADING IntRect r1 = {0,0,w,h}; copyInternal(r1, image); #endif #ifdef IPRIT_MULTITHREADING IntRect r1 = {0,0,w,h/2}; IntRect r2 = {0,h-h/2,w,h}; QFuture future1 = QtConcurrent::run(this, &QIPGrayscaleImage::copyInternal,r1,image); QFuture future2 = QtConcurrent::run(this, &QIPGrayscaleImage::copyInternal,r2, image); future1.waitForFinished(); future2.waitForFinished(); #endif for (uint x = 1; x < w; x++) image[x] += image[x-1]; for (uint y = 1; y < h; y++) image[y*w] += image[(y-1)*w]; for (uint y = 1; y < h; y++) { for (uint x = 1; x < w; x++) image[y*w+x] = image[y*w+x] + image[y*w+x-1] + image[(y-1)*w+x] - image[(y-1)*w+x-1]; } } inline quint8 maxOfThree(quint8 v1, quint8 v2, quint8 v3) { quint8 max = v1; if (v2 > max) max = v2; if (v3 > max) max = v3; return max; } inline quint8 minOfThree(quint8 v1, quint8 v2, quint8 v3) { quint8 min = v1; if (v2 < min) min = v2; if (v3 < min) min = v3; return min; } void QIPGrayscaleImage::toGrayscaleMinMax(const QImage &input) { #ifndef IPRIT_MULTITHREADING IntRect r = {0,0,input.width(),input.height()}; toGrayscaleMinMaxInternal(input,r); #endif #ifdef IPRIT_MULTITHREADING IntRect r1 = {0,0,input.width(),input.height()/2}; IntRect r2 = {0,input.height()/2,input.width(),input.height()}; QFuture future1 = QtConcurrent::run(this, &QIPGrayscaleImage::toGrayscaleMinMaxInternal,input,r1); QFuture future2 = QtConcurrent::run(this, &QIPGrayscaleImage::toGrayscaleMinMaxInternal,input,r2); future1.waitForFinished(); future2.waitForFinished(); #endif } void QIPGrayscaleImage::toGrayscaleMinOrMax(const QImage &input, bool min) { #ifndef IPRIT_MULTITHREADING IntRect r = {0,0,input.width(),input.height()}; toGrayscaleMinOrMaxInternal(input,r, min); #endif #ifdef IPRIT_MULTITHREADING IntRect r1 = {0,0,input.width(),input.height()/2}; IntRect r2 = {0,input.height()/2,input.width(),input.height()}; QFuture future1 = QtConcurrent::run(this, &QIPGrayscaleImage::toGrayscaleMinOrMaxInternal,input,r1, min); QFuture future2 = QtConcurrent::run(this, &QIPGrayscaleImage::toGrayscaleMinOrMaxInternal,input,r2, min); future1.waitForFinished(); future2.waitForFinished(); #endif } void QIPGrayscaleImage::toGrayscaleMinOrMaxInternal(const QImage &input, const IntRect &rect, bool min) { for (int y = rect.y1; y < rect.y2; y++) { QRgb * lineIn = (QRgb *)input.scanLine(y); quint8 * lineOut = scanLine(y); for(int x = rect.x1; x < rect.x2; x++) { QRgb cur = lineIn[x]; lineOut[x] = min ? minOfThree(qRed(cur), qGreen(cur), qBlue(cur)) : maxOfThree(qRed(cur), qGreen(cur), qBlue(cur)); } } } void QIPGrayscaleImage::toGrayscaleMinMaxInternal(const QImage &input, const IntRect &rect) { for (int y = rect.y1; y < rect.y2; y++) { QRgb * lineIn = (QRgb *)input.scanLine(y); quint8 * lineOut = scanLine(y); for(int x = rect.x1; x < rect.x2; x++) { QRgb cur = lineIn[x]; lineOut[x] = (minOfThree(qRed(cur), qGreen(cur), qBlue(cur)) + maxOfThree(qRed(cur), qGreen(cur), qBlue(cur))) >> 1; } } } double safeLog(double x) { if (x > 0) return log(x); return 0; } void QIPGrayscaleImage::toGrayScaleByEntropyChannel(const QImage &input, bool maxEntropy) { qreal rHist[256]; qreal gHist[256]; qreal bHist[256]; quint32 rChannel[256] = {0}; quint32 gChannel[256] = {0}; quint32 bChannel[256] = {0}; int width = input.width(); int height = input.height(); if (width*height == 0) return; for (int y = 0; y < height; y++) { QRgb * lineIn = (QRgb *)input.scanLine(y); for(int x = 0; x < width; x++) { rChannel[qRed(lineIn[x])]++; gChannel[qGreen(lineIn[x])]++; bChannel[qBlue(lineIn[x])]++; } } qreal sizeI = 1.0/(width*height); for (int i = 0; i < 256; i++) { rHist[i] = rChannel[i]*sizeI; rHist[i] *= (-safeLog(rHist[i])); gHist[i] = gChannel[i]*sizeI; gHist[i] *= (-safeLog(gHist[i])); bHist[i] = bChannel[i]*sizeI; bHist[i] *= (-safeLog(bHist[i])); } qreal rEntropy = 0.0; qreal gEntropy = 0.0; qreal bEntropy = 0.0; for (int i = 0; i < 256; i++) { rEntropy += rHist[i]; gEntropy += gHist[i]; bEntropy += bHist[i]; } int (*chan)(QRgb); if (maxEntropy) { qreal max = rEntropy; chan = qRed; if (gEntropy > max) { max = gEntropy; chan = qGreen; } if (bEntropy > max) { chan = qBlue; } } else { qreal min = gEntropy; chan = qGreen; if (rEntropy < min) { min = rEntropy; chan = qRed; } if (bEntropy < min) { chan = qBlue; } } for (int y = 0; y < height; y++) { QRgb * lineIn = (QRgb *)input.scanLine(y); quint8 * lineOut = scanLine(y); for(int x = 0; x < width; x++) lineOut[x] = chan(lineIn[x]); } } quint8 maxTh(qreal * hist, qreal * ihist, int start, int stop) { qreal ut = 0; for (int i = start; i < stop; i++) ut+= ihist[i]; int maxK=0; int maxSigmaK=0; qreal wk = 0; qreal uk = 0; for (int k = start; k < stop; ++k) { if (hist[k] == 0) continue; wk += hist[k]; uk += ihist[k]; qreal sigmaK = 0; if ((wk !=0) && (wk!=1)) sigmaK = ((ut*wk - uk)*(ut*wk - uk))/(wk*(1-wk)); if (sigmaK > maxSigmaK) { maxK = k; maxSigmaK = sigmaK; } } return maxK; } QIPGrayscaleImage::QIPGrayscaleImage() : data(0) { w = 0; h = 0; } yagf-0.9.3.2/src/images/Aimg.png0000664000175000017500000004061102263216100016744 0ustar parallelsparallelsPNG  IHDR>a pHYsHHFk> vpAg01bKGDCAIDATx}tV6^A4+/ +*(H^{Bzz﮻əyߟdyt_W;On 6/-M_gߏ{&76|YWWaλZlE[gV?ܼOf̘cG G S03лwLn }:O}Cx@&ƒ}m!;G=rq7pp>Azde{,6$}tv1t6:phhO6|Y#OrVz İPfc[¢gW -f+2sݢ.o[x&Hl<}JGc;E%P3L̵(Y鐍Mw2 g7E#Nb: =hu45g$&4ۼ}̚aJ9{GfL #{ C@;]fthzy6p0#fHQF1џѣ#"bzwu'5n+sx߿n[5(0q̷ݻw11@~3k윹3leEgWc!ۇ@` iӏM>xϞ=aZ˜tHߏE1cGY; ``xX<+30&01zu,C7|qA&9?D#(ؑ7%k?:zk)k-cƏO.򱦰x[_:uuݷ0-3*kcg?ۛӟόMS87,2!:diCR++;ZߴX|l@a^ #wC"<#1P#XAE@CI?F2jO!36n"?e=/˔uɲdu]'Vgӟ.3#`fxRl v/(C .(W[0ƺ:{h$ܨQ#زy}^Jr0Ui ]Jv!FaWE*7 榳9LKdPʳ9?iģ.<#]m+ۃ㹴( [k~\ԅ4Cu> /ʐ}}Gd~i bPqǿ`xNA0@xj#W}Ѽ>WUQue}pYI_edrr4?":)Ng =i\J35Bi~,%<7))'N#akB^VӺԇv}p'%MogMQSAvsZ<0@k ^?NeN|mǏn(dUWSBZzP9+-Uf-+;ƯղjObEx0w*,UćܱH'5ip&C]i?^٫W[@u)sl!]ʸ ?K DIN y0;Cu3\W9]E~'6N jmo؉|(.A8r#H#C8!\ ϲ,G/Gld$eXI`lc|io%53ǃf !g?]hasi6Pxv_{:LuUO{%8ARC\oɃ4륻EZ,rN H/Ə}}ڃ e3’!޸Sq!]+>T["PYGўֹmk˸P Ϙ>=fY$2l=>ܓǼ<,tm?[n$"Ag TM DPqQn߂H;Rtv,P!l$pgĕmg'N6(.D4 H2z.3luv 3ǥsy>S[AƛÆ fgFqcv™b7B5 LP`TY3-9~-)-N; ];iʥۦsoN= Utbҝ;LaO /B #"" ' u9ߥsQ"z[43s3)Q7Æ5'̚5cig/-My]v`Qv4Z1Y5TfQqZ@j38S1{0%8Ux E[ D@,ā0`,eL0X½t.Z!tx4(@Eˆ8!￧qMZE:hMzf$ TĈ>详tJ%&^گ_9e@8n|ƥKEFz^ ҩS,*yϢH{*. 0H < J8w*\t!*Nv-Y }nmm<+L̚ ^29z1 v2"Y,}4iG>]]%{;eKJRokx(ˈ, iC 1#mЛN&ϏҨLM)fb)X~|o]MMτumn!${1;RB`'@I*[ :H!6fT?}q#*(JHvD"2 2 Jr!V#L˰0ȧ9 yYʗ#^=)(E4Ih'6G U*Y/\M yIrp4m7o֒ݻ=`A e_LJ kh1_I ^6l &zQA 6ʔ׉"݇~c[ KI&!%񐧡" >\Ip 2L'r@{H矙ꐻrFnztx3e'xRFk!A|8R =CEVgРRˀmKOg̘zBrCCzP6IƶAeυl()ܖCxQ^ČcB2!3P(\Ud3}[k-"/.ԩD:Ht(~ A^YÕiƍ, 7[TN)h{4K0g= .#ME5SI_`ԩ=^y1ף^kڲz*SwVZZAUriee1ʳ,K WЩSMPU`&K]˵k5H˗/%|3zRɂݻ6g:WΜ9ٮI) W.Qe'{4bMM$?<f7f&f띈x9cۋ&=i`=o*Utr)5摎ξdֹ&~\g<óLG^QHWJD18Ɂ| ؤN xUWr* uˋݻž޽{}dɋVaaz@ShM,M _=XMgk#cKB| '?&d:9_xuIg)It[Jtm"=OlF~kûwo;v,Fw#V&LСx1t74h,,rjhHU.&@H}]pKp{+x=,H4- $EƢvuNZ`z1cәjM.%S^mg:5-&Q4 PoR)+‚N5 =ABBkk7MQ}$|$ ڵDʼn;v,Hױ3&1})U_zo"B pR \Ӄ n \n:|1,ѣg>1 3̐F{QШP 㛴qIeLp`22(5zxi/+ 2'g2, ?Ңs"(^`]<_,91D $|ѻyd E tj/I˶m唗cӄK$dfaiVG9Q %XPJh?Delc%` )] I< Lh4Ku3JIfQnqP J6-XwSdr߃򑮜Wcc1YX͚5ocu7uK,bK_;uO/P "=R!ĻAio {)ׯ E]( g yLdgfFҚ5mE5/^ի?ՙR {vZNa ^ 9C0hIl(A2]r?;F bk>HFZ63#wɝ? ' +P/Hx;NP;QEA sO@_e] )ЭS>Ou㧞m/ n&̄MuuBIu3b^v.!|z5E)mpg~x,jvHYrǧ2q8KiUU]wPRKo<v@epzLдi4pbr)hOy0SE26>7mlu{-cD1/_2Ѵ *C_#Ňm(־a3D:!o` 4=7\F~#Xۯi`|#31/2pϋ5:~;\8[,7HJ w/Yؓ VS8["6 m7.bFnG<ʀ+#-q,Lw,7A4[RӀiટ(DG^0TiGQVcOپԓѭB:S Si}A$`)!f: ƶ0n8٨8Nv-cݻwhC]gäEh:Ϣ3*!˗NR}]*+QB6ӋZH*Njrrgqm'/_*/czqΜ')Z.l t}c62:Pw:t'm%TZM.dX2Ag%VqHoGN0j^G nG!Uyr؟"="v@2JfVgw֋!Bxذnܸ6(Q|y Rėæo:#YT]cINk2[;KUU F85|JMmQy 2%uw t}^LJ f۸ZqnE>¤]QWŊ8Y:Q&&skSb9GٰbaktwfIT]-("%\:#e:G^y5-.r|(>Ău0%,BL W @f/Q@ ^v_ѣ{(|zg޼Ymu|m+rcac jOP%CL#p1 Ŕ'n1e9hok)7 /M)N.q6.ۏ6.%s:0:M8n1x3صA҅CY~yvݜ)j\9/_0Aaz//JKCpOMΝ8u?.q tXkwu\d)OˀÝ[AOe/'xÛ8W/|7Th'56dB7{ hىT .'@݃q˗s|SV )^9;v2"i+ zD^1)@uٻluIq)n|y& I!_heEZyS) "0&Dž^:)oAd̦Ptǎ7f[ ܢL|NUr#{V/0$;nLԁrX]/Óo F(+cK*|)=D^@kW#2+̓ҿ@Or=';CK#;3'L2/{>h=@nks>񋗧g 5He~@{:/i-<(& .](gTmh! 4LSOwD(~袡!J z _Zk1+fv L ~1@|rr0>7kN]@le.\2uLM |4N TRIzD@pHg ů~TVư&(P@IeeEѯ]Knc#v 'p_;.&mNc\Cadŋ٣G.3汯vؔ_B̎3R=3 ~ ?D%^ķs:?RCdȋpw#LTTB ~i_EazHpbY"< ?Hio &>os%uPj9|;`@M QBAj T]HGw158(mNWSYn#_1rd8d>rᕅt!Ly6{>`)zF==9l7-LTِ_(JGnqZhG=Ne8ڇ0?dq(ݚ4nWvGu"df=~iQ^ &hL;ĩ4J tjG":w&_iuUDm˧‚@^Ut6u7ϦGNM2i9`7SX]Ss8 L J#)Z|Oox*3RMDpwy(~*d8 AL ?ɖD*mk,emΜX;ei҇:Nb+[+(&yB^~b퍮΢slJwh_&TSzKzXCM&Iĺ[<F\͠œP^&{(;ًNפ nE[G>ke3DOΛ )x%̍X4eƆg+JR)hVzwx\yb-.~|*50q,A0A"Pԋ(@&Kf#- ?ÃhGN'(ń- QL.Ql,G h+,.4p ,؅zr<)v͍X0a/\Z+Nk/-lfiaa 1nt#-EQU/<7u5YO=SG^:{)BP?óFېF~d:Y,OAm $3:=j_mLT26m*6ӊĠ#P[;, 6cDWfa<|x#_asLK yC,t)/>@gAtQʒeP+" oMzey?ܶx:BXe`ag~裼zr+7}.%35<#Jr5J[fX@@DQfm3鈾ֳ O^ BNMb;2d3tM6+UiTQrUfwUOmP?cls"Ў, w-3b]1Tkj͐M H sh*d di",#Ӫ9 KYAzKC#lr,Q&!\Bz#-eY(e2it$jY9TЌ/"#<2a-aE&G3 +}Y~q9K<',A>Y>ρYskO.mtiG# '3,_ (eLϲ_yd:0~?0> Oп]&"̝*Ө"Modw+8Qx6>ܜn&/3xNd Z|*G++N4G< ĩ4c=qḣr\z? ~~Geʾh2VxҼOt:T`j^{V, +RwDav$ʈn@kq!z:Gkh xYܿ  RaTV)wD~ij;![9p*3V?:_/!]` 3z2m咓ITʍHx zP Ӛr3T*EhLfMF1M 6 !m`'c"PE q[q']ݽg&O3گ7ZSKEqwDfj7#/[E&)Q)Y v~qHmvѶmJnjy싮]W9[leV`*`}'xVRaw 9VFC|[e7#’qժ}tЧ]ՠ3 ֗_ ul՚vAo_ӧOٮ&t1ubk)覠2)<6~6!,܍2"DjJyYQ隤Ȍ0Wvg#G} c>NΟ Vcc kC{>o> f̘}[->=!:I}Fl/ʴtzq 3 5kڼ/}\qEY鐽)yx[Ex xx?u>5j!JrnvޯϞ=FN߬9 y2|-Ϛ|lB)LFѱS_?[ew|b+xhl } >tUigE6hG!aBLtUEF{RTK/!;Ռp;g' S_B=zL;w櫇l'?[ upHwbɗy} DQ@"Lb<,ݜaJNFdkO6z둅q21=LFƇn0G 365("-ʆXa=t ito|s ўLho|'()9S)5-,]aAɌeo fPF3o/gVDw2$ktscdĄ3G;y7~W׬Yqiժw/βW^Y\/TcxӧONc2R$3 xFŸ˴3gNI{/᲋͛=gFz<_(~3~{С. pRאMyc Ih,!BXZtxY =y(v4$ k21;Bz'azcw_{+AO:I?~L"F2lؐ@6 տ?~lj㠥eaX)dX0 c5o[*魑{r우oa|v;=3eI/an s(A\|(,S>&RiIؐPQڴ!`r( OoKrv5!;:.Dڷ˿͍V^jYJLT޽{;uV!I׮] :<6G?2g4 k:Ɨ[%FY |)쾼.Ʋ$*:ʋJN&RYIi_MeVgm؞SʳaFպ<G~-[Xժ}e^]D1sJʤIƎ1bXy:ln] f{0 g >Vwo0)8?;t^PP!ſPQ"%??][l:Sx+U3qAHh˧3򨔙$*Ɠm4Mn01]֞ccdh|t!mƗV]÷.,yyaSOΛ6mRqYDB<C!2_{̮R6uR/*zVѸ)oݞP`)26Qa (`ڳi)DqJ!>ؿ_nלWΙ\珊Al=H.D% ?| 0-]MYꐩa7OG{X惫y̝5eFE22؇5b7X촴v9m?دe.UfBO*Sg*vj?Z96RQ)LA9Pemп P^^?jzxcbI߬䃰P79t ˉ%AVV:e.^y g7cs4 K6FHGoF }'߻ۯ}qPƌy,r!lyҽX!fόM:"x3^.Ui +o%sQ%A%*"1{)D驌_@-]Zj:/i'Vg0อR:A~ٶl[%424>@,]}w7|{~ZVʦϘ2aظѣG ԒmmE^Q֨ו9e&jȏ)wLU'lKvW|uh~Sn.ѣ7!Ka;vlj1??~dkg yg7>_+>z[˗^x *}nn'=fdCC}tJF"H|%p 0>bAcUV^a sgՋI$ثMS_kb͢va)%R@$֎Y 0.f WTTxOųHD" 8Q{Y =,+ۼjΰB2 #4o,U9i2ƽ3xu"1hF~"Yj.D$%3˜td´**jN'<ݵgҜ\( Oe'ij"[YfYb!QߖBS r[rH#gV\hqFG}G9BNN7--M X{I PG^c:l @CáuZەwEC&P_|>/8˦K -1KGoVsF}D)q @G ,*"|ғ⥻ɽRqdDS> 5kSўY&7Z9 #?euL -6@^b6?_-- IENDB`yagf-0.9.3.2/src/images/appearence.png0000664000175000017500000004123702263216100020177 0ustar parallelsparallelsPNG  IHDR>a pHYsHHFk> vpAg01bKGDCB*IDATxw%Y:sOOF9gYd#˖qewYxdvx} k05KXzYdl%˲&}SUь, lOSO9NU~_ =oM  k޷]Jn>G \soE@kS39xgN BGN?Û /EG!O3/N*Payl&A/i_3Pmgy !z?_[-6B|mKa۲u>O̜W܃6 ]<2Umc*^.eΙ^qe0h1i}Ce>t'^u꾛'PimdQ{YpB/&?}SE򣵑Mq։߬_'+?+lZ18[J)T~>\{c8z XGWl?t68^D`% ?Qk_6/ŭg7+y% RSRѸ |8^!7 :47zN|:C2:Z1?j46F^ H$i/w.:^\CՉWBt ?/rVҹ 8݋ V ooL\c}~y|xYB^ikoE}3>oLOj?|!<^R iv! &Tׄo5B6ċ'1Ԗ9}!>6Ȉ7FoBT,>Jg }xxT zayC͍ïGOGN9jQ#U `- `Ɔ-֐LK4{_/Bts>mP&?GgYw_muyuP(U#\U5F59I's9}%pT~>jL\ O=qI/Ov^9z %~>Xysc|#[I:i:ws/w^<,U{uV#3Ʈi y&xtwr׶x~3K}Ե[F>)"Te9%{@%عh%Di B\ 9kZ|1Bp;2X-<oS{cuEUE!k֨_nǵ>M܊駓@c3=" V D%-jRU LJk&P\K9S,C$AΠÛQe viix6Q QCc8Y=5Ƭ=j AqKydi<6u6'}+{N,Hv-BnvzmRG^ˠ!Pxdz@P!BeH/*EZMދ^z*W*UIq25K` 4/@faV/6;ap3?q#okVʛyʆPuxLB)”Hߞu(] c}POиfGqiһ{1I,͈ cJϡn,M. \ 5mLfD}33 ,0yqwEeAqmEitf(xlW|j`gp6.)(B#VJӸB!Y\:;Kn݅b\tȓ<˱&X0%t ݳѓfBD~Pox v@!;@\"\"[[Ox"DWt>+F>a?KX߰alvE+\=T+]JdP޿|Wk_֢Qaf>&3ٮec8QÛ`AP# .DfưK'[ O;Ph5kV}.w:vn0?e_{!/EՑVa9Yي@,y_3|LPW޿ f 0%_x^]h-λ/FW8㑲䝧 Jdxqx9d5t23X+~Q?{wrsV| :T~_[\V'=qJޯ{UWpU \q1WQe C'=٢"= Bꥃ@@ցmocW`@"Yg/n2ahh Gtxy,8驖;==(.In7^rw jß'6HchGL >DW+W)CgԧU-?oMezcWW K-q2})ڇHXv[?UطSr* kR+>%uXAaED%65ܲ Y _~77}-w]ùI]Vy!5ZHBq5j(~6>Jg\s2eɴhfm& 2q 'o6sRs;fQP$* q8ԪL,$"Tkjs)<$y%;ѨLb~?{? i9mX#v .PSv0iZ9Œ4qoFOt5gD|#)~ֻG_ @B/Lme,.c?Z8P1ӴmE&Ni'4 Ru AztXz OLQr3UJ9&UmhYi"w;1rWWсёGZfl5i2u>B,_2fdwelټO07?O 3u'/?ܵ[bȍ'g_``bCL!@xǿكr;qZ ˦Rb1k[6i)Hբ~>%~# gw=_[ D:cm;6g);m#{`${wrP[wBld$X2ɭ覷/DJRtT)HmȖ~_@'43XRi'?{o?s/ f@?{Fr{啐%WߘV|bG!gY6K2s"q$OKY@Y>EM I#ܲe/n\ jlT`!::/&ʝ\u+#FFr :%yHx7VLKکC+1d=E/+q^>8\qtƃ'6Mi)>[~Ֆ|۫ o3[L*6L2[,IcƱ^%% '҈(R=Aza^]߰]ᲮGD]D$l&K/Vr;/>nuoUe*t!0iY<-x82s'&1q!pW ޸`7vqkݿF7!Ak2l2!w=ї!RG կ:!(ɘkŖUjIBL>00bVf;#o/|n=3,e6ˋ\jyvظk-b&Y\p~.ĔtߗF-vw[[UwZ < g6n~NI $T^쪦~+jעpN@;ePcl8q&ugl)LEmvƯttBvm@F 4[1sqa8RɲIm#ێɌf tݑq[VeA6QrI%K,;@ Gd:iZ @(Dok|*`PPͽW\y9ۦi_PEp紅}yȠtyJCQ'R&AkE9:hl-j~y`*FTQJS+iڤILc8ꕄ,WLq+ 5Z^hə{߰o2>pZWnK6ߑynKe#stwg&SO, V }/c_:8G7Opϖܳe#Aryѯ ~\?WӤL#C(H"'的/0Q!wɱV"jS(S?7#{kV!RJY>&YF |'fs CI r/F(I(/N/Ya}kn6UWUś&Hm3Y{ɳ9>WO@Ed!179:~%b׎ORgL(MN3Kpy֥"^Kb%? wSF:F.ظ* q\cӞ~K8ixq.R;6_>$/Eܙ~v%9ma`skړ%aCҶ895ɦa})şld}5EkYfhkWkWyxx1s_!C<ӯzMr@ جy 6}WIHa=,9Ri3&6SA1ĭ1Nۚ3XO҉3odΛؼaY,5/fCp+eY^独Yu,>K1yjK,LUJI`ZMqݖʱ:r\i^;p-!8csHFiͦGvl8ڲ' xkp&;=u?*pws(7]=N% ܱv/d [\0)IrKoe1RiLx <R(2J>ENy좔,hQ{e҂JZs7ڍP5Xuœ[W@({ pݥ#7Շ ?-җwx qP(BpnޟSI <(n֫|W~稬xp8ҋw0TaJb&a1B2qyax_x=$uĩ%L(H!P:@* -#Id9! LC+}AS3jd E,A˙]᲋g 3w?mɴPUºbD54I%Wz %9Ǧs7n=u7w7(">FȧGeXqaV@1't=揦cJ,0 5eD۷oTovwjz!zտfI!ĜTw mcL8F?nW< |vc_cE0SYX(5٢ԕ*25o,A@-٥xn}HضQF.E5Kt񦍰II2G2@CObR;Lζ !}#z+,d4fy )BZ|!?wF}"7( R8#K/ܰҪ^-JVH1f_1cMZ$ɊI%`dPl[d#P"=>?aUXU";Wi:uZ'ggu޷g%B;eDH"#;/}MI c:|O'-x 33Vw3f~!RS5 u8E[OXkyl# ڿ \~쬾uxkpOM*ԓgM@ A btk2DYhr g-;xA6޵Ia-qd9dn4ީ'7zd46-;2۝zY&yB 'Qxgl!39AzR.*R%D(IrЍ?bxT+ d-@JIsonlV^ׂ/KП)+%g"B c<{OZ<nihW3\~.7(bYn $E=LWg$wS;>Q Ҩ ř$k#ӽT)A*,6}>ʾ٣?ӊ"j)s镗숴V)Ē r=:!@0ф[sX7b]Xtr&!: Z=k{'X$̿ORÐ1aZIrRD-/wVDimٺozjR@s>۵8_iuK)JZ} ijy8>~t<$ kC S=I4U:G:zʼƩ+ +IZ袌iqR9tEz x$' VsZ)p8O)@\cLt=Wf K]٪厐˚c"wƞ0E *0ZkP 0/<G9<$_P T)p(}: ∱E9l'LN-1=wLM1qĩ>8)?88j% n]|0R'NO~w\u3z9Rhw2 4ތb(r.GKEa+-4vVf~U#,o!^УQES{GQ'0c"DH JVȌ'f[hldi[tPR"V"Ԫ:NeT;iw')1Xk0 3jiɞ s糜}Rp2bvcONx"g+vץ7vUdy^PlVZl'u9qZtBcg&5yH>4%/(bpf$: *s:GQB9|$v c qfY0$̦A%(ͫRJIE_-j-Q_DX Ͷnv8r{,uՊ9!hvAVMwl:C&ǟ;t-bl("O ]r+U.V_qF@~"&`: j㰫0c}q`s3 foڃ1'n("%(P%1pY$xXVn8ǂt${7Nbavz7FԤq.H |^ҨU@Lwv.J_qXOp$INMϵb\nT`O3"QtW_jYk?ߠm( J]+ >;&?~k/ڊ1;^vtV*3>n#¡}Y,=Tx>;O~q :~%:ʞÓI_tRTy)鄚UvK) XRg5P ĝb%s)BϮMjaP=V~E *x\/$ L-c3Zq44yO;֍{k*soz=*IQz0Y]n(3RG^q%ʙE'{? - 2&O|BCM|Y2h 6w_ IJ`Ԅ0>|Xȓ|e{t(ݧiY8/ԕ 4GK7,v!7<~&g\HUpku*]/q%A%n_`#MrscHq˅sqNJyrl/cD[}|1 Z |XNkWvӡB,-#;pƑ'fQhўqzyuN+2R+*#wߺ[ξ:S͵"~}GO?8 Fo"46=bU>YO]yx ԹC1l] ASrywXn7J#fG/޿Tw|Vjy?WrJu~sc*gsyjlU} $AUCk9 .{ӫ6}wnT.JX(yr|}jȖbΩnۻغ2o18 - ((; 6HEb +  Q ,@4j7nItZ<%B ȡ)7{P._"zoytL]>K)i_ ݊0 ~qERfx ިԋ|hT6Mlv7D%xd7R|{^&P3k|"'Nw?A(ٻH@BGa&(H"/.BW$ʛ)X߿8/&`3@@F*ΰ^FӠowqn/hg DO*=wpB/׏-.`nz'p|E0zn2&dAi N:P>ັ39}w~-`08RRDke-H;&'sG "&|υ2C5a|y/6  g`$o=/bpsv_\x퉛apl9P78WprrLM]m:a> NkX]GPJ2dP"}5Lhe]A$Nfo.\Y?g&VpiiZ«|"&'!64IOJBDN;0?di>;i?=D| ka3X^A޿~ gƊFEDsזW9Lg1]EQl6mx (HdOzf }l9$%XMr&.-?jI\l/?ccvf&ꨏ8ztZ\kRt*v`mC)GXg˫h>v$"l¥mgy}RKAxO ʘj0٨b:RńBu}^Ebg!E@>;dѽ#܄m;f_y~=C_Z#+rL1OrisatIME #3I~$bKGD HIDATx tǫXE+"PPQHbrCmjUb= V+nmhʎ"&[YBCIH'aw 7maB@޼{ofW+l)ʣQKj:Ւf5ZB83F%XpIpi-8H<: (vױcha:qDܿF>ݘ/2y46"K!X؉PEEbMEPRM%ʨLU9 iV0UTY&|GG>P~WYU*j[Uo&?Єc7j_uAiw .mi?FW<,HK%x)@@nG7>~Kp!)a}$@=xs z {Pn #MD BӒhFOT g$x[ ۋGc[`P o(7֌EO>IӧS0+Kַq#m=fkG3/87QKt OT Ȳw@!ק>422,C=TMK9jݚFU.CWqv^B,kGM܎eU?-xU!>K{O[8*{-AE&}qVe@1# Њ?!|JF_k-ؑ^pXx5KW%7Tep#<.!@B>uxG?"^oV1^J${qa[mB!@GOMoUdμZީ[Je={l7kUiʣ5c~s._^uہW31ch@M7>=vQ @l0Q*s1Cɻߟ|*qoUWpݿKie˴Ўk ^I޴q!b{nl Ћ ߈lD;ޛq7Y;8pz} `zfom(/)9Y8%H3{.-Coy7^o,'LoRu{9cM a> FÇm|hY/XY~V{YF-eX*ްA7_*BXE-d~@,^۴`ڕ|>Sqݻr6o6sH^qp̼oFj<9KL֕כ 6AB'#bųaF>2 3#GՔ dϮÀ>Z;87p=i%@jvߍ `3Fidջql>#j#.eg =gэ7Wּ$ʛ=-6sP2@ |NdZ  Y&zA76hF#@$ɞSm$0 ,oK4,̙5:`°J@7 @+ÑCD;ەw~K|Z|;W_yŶ6QA jj&BDES43vGkw!gK. 94oѴYBvp2iW#c;*tMI|5˙>= >Z-e#/Zޞ֫f[*|Q 2}I0tOKm>< :|.r'L/oҺtfxy;&1c;jkuF)yO@c__cX:i>et kcF1c5|2oAh?|oψ ݻm%nrwqc$LBٴG ^7y'U{c|QTtc ?"5-gupuub9p_|Ex,7Gsg Ν6qi# Fct" l. 7f 6"2csn%s@#I^(#_{mC,ۗsrGtVĉQW%+K!h 9b!l2ƂoAʞn_}  m3nCpwn%/ 4'L2G I,{9]|r[8hFlhN-Cѣio;kSü!"h&e oLx2+kҩv^teNMu ^t fk4px<_j_VeqG8|.be8G>N*(hɲI@ {Ikݬ=]G[BW=R5T~IyWR6N\-ʺc)m[Kr0*r&/&j7Jaa>!)|els>W \qUTgҨ18&+fFwyBf;B\BwI8I ~7>j80I_c7+ {L׏v﵀gXAVG&'',Kn=J8j :zoQG ߦW0poM5cP |3_H*abhmM<8X9S`:˽8wӿWK{C~ĉzX?x1r`D5Ph }ÄICp#o@~kp-x_=cFXbW>KK//~?C+WFI2Qtc1 * >|xYvhj*aCq_(T}{L @EM_* 5k7`W}{GA. 2|wk1mZ5:|$|mslRWNESvT T &pLơSqߤ |-eNmB%n ?⊡Whyy"LgN|7,9Y1Z~wT⚀?nԉO:x  6'־4I_șQޖRM-*q#)|P0ѣr\G= >TXu|$:[[Aǵ TEQSh( NCqO|Do_׵;˘uceݵK3D'Ryٶ-A٧HC%ܣ]裮7fl8ʞ||o|h"Ґ!thݺ܆HXCE]ݽjtLҨ1%nT6t(.& <3\Eq>&i_WY={ڵrUREρ.Yy]GoMud2|86@,a܏>=8k^==wj/RW/?9O(ӟpZŽ Nb3tby2G _+. Yֱg/WD;Ncc< [ڭ(K;H?Ёczu6)Yukw(9ޗXg]q9"T4ho \w&|5FBu{R /hkgx~V{*_;'>T& ۱2S#x&#_~v|(C .K`*{ԧs+z%nbƍ޽M(XSuPtnK/ F o,r9U{:t#|-ʽ>a N2~u.'?¯ݳBL&ۗs8RUZI~6QP@ESRRWDZ0iRBu,zW>Q=,[FE>><yQ bP7 mBA>?zBSOo [qԲ2hƏ›njX[<'eV^l,Kdϴ]{>NP޿9vɟi.ȑߩew/Ǥ{)̓եn$  8%\eX#6rVF[ch Sg!fpU]峇 7+}r"'ܹGS(/lTyVv{p_o.Z~o J?'4mE4Ñ'Ϸ`J n[Ԥ _nj []nj_j_4 pbtnΕWR,K9rF92z~(㴄l׎)ȸq 9?(_ttD#.&GxU@{5.|^*\ܓ=݀taRmw=6./ޏY>VV>{< >%\yT[z+f<2SG?!|5gh`!hooߞ/L>_X o^]Kp U&a_2-+=fΤ}&j/:YjhR+p#rZxNA|饨A%}MZA^hl^GH$*d^BEEŚo(UG֝4 lsϞ=iǬKX精J ¿VwG3b.y0&d*87/c}.**`0H~.)B*(i ~ X{i(y7|OA7Sksry8e%`'u/Kҳ%lXw/7Sݴ{o Ν;i +>Y%384\.8:}M*|p _%pxWA޵kرm޼6qFi3=}3~;efeibذasߍu)E.Uox@8xΝ}v4oذ[Zj5Ҋ+is%Кki#$;;2224֭^{|޻ڲ~$BY9c7p0t(U1+ܲMS~< ܖ-[4kf C^L7}/^L_s2o|3g}5{6fK[n04nܸI|ίeg\ [ 7F~a>b/X~c>><ʓS4Yfї_~I_|}4u'LOwǎZO>}+DD@ZycPòYDL_ïL9~#|  >:&OB9W?~<;>#9Νc6t`F$c[?o޼mڴwGmXc3(57RkoVۛNC _333qß4i6M0>3-_ArF*|*%Kѣ](2*b6x{?A>-'QOfxٸ/ 2udVi^.dFja{?ll Q+ /+2LWF/}(rd}m>ʕ+@K![o>=XO .%?q3Ν+uqDX¢΃&x%~f?@¢7~hl߾> ァAVf7r>=׈F^D5|B:<@ؒ,m}ӦпUI7eѽCW_q? 0Υ |*'/iZ?._ǟ g }DUbJ0őeBz9TCNhrca9K=fzs;x8d"m@1/Z>|C;X‡>JE#|NWjS=] 5 _?jZ9`W}&?'xk+xE2ۅv _s?^_l͚5{o5 5ؒʢ" ` zW i031o,J ͞3G~ɓ'ayO SCaǎ;II< ?!>{zؑsط[r׳ɻl&~N?l˖-9\ K׮]; :}Owot: _ F3j~w\F,X!7|9±7|skń/ i^N ,PLȒܬJ@/J:,{- ~jj{iq.4q7Rv)>\̱]Ɵ _4H_+HO@dd#/𓓓ˇ q9Sjr,ϱ0P? :^|٭\dvciĐ#B~߾}yumRnDJx'O֞azgQ^NCqq@=} …[w~Nb%Ŕn'Mo\QwU(fq$ t2dXŃ5||&))FqNaW2''1Nrm}f͢[n<\qTJ\)ۼNItPG@+7wʔ)tn{oQ< 7$z>R'Sŷ[ rpxi`a@\ׇyƌwmS#1 DkYCY$FA3{שga pHYsHHFk> vpAg01bKGDC3qIDATx} չꞕهafdME\wsrM^"$z_4&h1j *h\@@Pa`}_}St^;Ld񸈦iC|>/+3 V c>$e L?Q O0olii 222_U trbxwA[ss罱lmm sbDvy7^OIITU/!33M8q w`o} ա!Y(d3M'㕈Z.g55OH85Mh$YMu-,mR}I-w =YGGg!(9 Y4nÔcWv>m6#]Y$(\n }] P %;@й(Ç'&$$Zۨ%aP@ x.\(n 8 9mXݚBx- UBvv6#@(...bC48 -_y̘1œ'O^NػwKulViߟzXb]w,`vTm z1>'O`{>蝕WP'O4i)Iڭ@hx0nܸ9WW^yo9rpD9rųg^VRRr kq/??&|o]jsIǏ3kiΝ;7۷?x[gggG,Ŀ>ww?QZZl|衇NM>UW"bc4^([nuC}yċ.Rn6mUt6lKNNf--/nGr2-5% c68g˖-/~'8O_t_W\|y&~֯_r,Y26#sIIHYFs+,,Xh?tFF G~` yI*+އAI\ ֲcǎuرz_mݺ P_q?IQTTčL R`7cIA0 O?3#άiPA@zf2GN"9}Axp񷶶vΡ2A@8qu@%ֶ6W_|ڵk+s…+Ν;7& 0F+ ظq#$A@0d@ĿgIA|+СC|9a(U?n'N [0|K{@wXY~@09H"TUU]܂?~3g&(,d%@c0A$&ps~4 '"gger MKKc$;hpK)Ih`m<x li#PgV9WsKHOɘ ^ FxBǁrDKKJ woBzRnj Fokjkٶm>}A40ٳo"/ AT6,uK% NJ=1)<|]w76I g 3 #IOB괳sPBTUU>zt1="sr0A\0TXD-yD[k;V^ΪkjYnn]Rcр0u C=A|.I H ٬Y2;Ihc DCc# nn1ûZ,r*(@tᣬ VX8WWIė=Y "u+Bv=edv% XG۶m,G3TbZQDM2BC1 A*hGc*%uX܈^2y2;oh, )Vf ATž\5̝;8Qɥ?Ͻ-&wR2f  MA˜2eE["UаSBUL4H ,??ucuBF#Lm[LoA0Xp _JKOg ../?}%>N XN:=NZ )}6^>MHadtT`8lY xn@\h<.]:AGqK7';+"wyaу`2|}kKKC=Or #ԃVx/|,0WI!qtXIKK6sVOIK㳔7lI?^/yUW,מʠ91E҆qkYsWH@(i8'A? ر[noSJ4Oo6!ypݴ 85sk8Ow(gJ$:y/w zFիа!//rn9%郁чBK^M%I."__H|$2Bwu@E;C)!6E/hŘ_ +w$׋Fa3\e-4p^T ȃ' L)Pfz$1aS 1kCe@r,>E @ٞKd6 "IASzUp7e65&@ꢏT,b k`CaO(m}k֬Y/NwNbvʆ ٳI'hii׏eBN="7EH 4"*=}z,#ҷmH"TwA4OzW7477DW%Cb_|-Ȣ%ޱV&JP3`!e^d&;qNj^s s bqCB5ʒCmYHG EƆFnpBS WQ3}Q3.>Dţyf ,mŔ/^%lQ.MYލ--G'.;}jl1D׉еutpT Nw60v2p_ `l$EEXTXҩU'OleS=kZT%6HYAa!V_Lq>޲8L(kyq|$GgvtziW(xVLJ + "I0Xڊ )))aT-!1},7'ܳLnJ`#V\"Ӟ 0980-'&C2or3@FfFJii-ٳ222&bj9n b=ΜA 5>3v>VDA`wr^egKF:ܢ͟?ɠ)ڮK )rWY6tt+ŧjEHPXE'ݭ6Mcd2B`!-ۻ gٺ]f̙r뭷~mU^%w_b;%G}ht‡O hE/qh9Cײ%mՠ~%$!Kx{>rwquqqq)j}>ݱQ0p>I]I^CR|oqX2.\(^ '_m܃.߫2ð D0dI,1Sv)(d^pyWMz@Ma& 758D}5aSNhaA0ޘ!͆vԣ#hELSba6c` p9 ZSFJ@:X3r&O{vݺϝjD(?6=#cu?Rw'pR7PZ:k((#tek~5& @7[Ob֬Yy^"K#$HOOmeZDY|aeլyǑp7~Сž??˾D` sYz Ϛ^Cbd?y+F ~EݝPyAʿtfRP4Btm޽{_]<Ӣ:]JJJfN: ???wŊ7Hb~?BLkUEs+p*!e{ʕ_V^hQc=0=QȉYď DZ(rU pp?/z H$+Iz9tN{Wkjjҿuo YW.WZ0I@?^sYȑN6wNII+ռ?#bޫ^R~LF}kTRz5?/XjN\j"CrL!m}=>+[i h+t,  ysݷvۺɭ|t]8sFؽs_9Y Qk  /l۶퍷~{Ν;%wg,[lʌ3^~7M%bukp/IFC"T/t[67#]vtXU;680p4PYj3[{B&. M]u픙ͦj9 ι֜?u$e+"aNKcK`LHdn޼90) wϒ Q#xwEȳwPng֝ŽVW:f2%Ü7 QYO>9B }1{n#JR@۲e__k4=2fҳ "CR@8 9ʛQ &,7̣ iJgru=́YNN6jAb,?~VqC\,UVV7^[n U¸“FpU$B^VƐhfT\ì$˗aа~Gۜ^{Z=emH믿_dE 8gP.ꄢY]bA UD*wO/(NVV4$!LWLΝ|{O^,+e|[m0:!%a%y;7l{DfcU PU8TV: ":8c6bDoGt( "w͂$EcL s=BI}zKg}۶mU<9< %R@wى x4VYA,Nd qAVUɑDOfў8ǁ2SUu {?H:5g$K 7,2G'/b:+WBJtG˹(y^wև d ,??zЁ @lkk;:X,HBznj EKp@`a iXK׺FrR"*lFC#IOOO$Cr}| R`Stɒ%7v{nnnx3#ZM @fvD%%!{0 29Ӣ̄V)yp;rOOMMKq{CUL[Vo>rȠL|q15ke+W|n ɤWjי5ݱ jCibͧ}EA,+n4yAѪ-c ,לm"\S5Pi̫sƍ0t;ijlD=k֬@p g: >F}5wLTtbأZAeS2-DX͘)Z 0#Kt\GsWfra"r3,;1Sг `VF!FeQ;n+l3֔3>˭Oj$6mB:tpȨ^{SL`ėq7A}X~NU 'ax٭t>JGRӶޫD,cKմn_D١7fKdN/E{Mv #`; rN\g x1++k4@rrreMMi0([l&*rF`*[hCdEZ8\],Kxu_|w+ñXn}v"~y3uuXJgy_Cvā^auw[o,::V8=_K*^?UUUǕ?x7MM!m7Ok݋GlWi3}&8r=O8,MZ #zA;]L) qN3k>!@/ Ps9%,_ ܅"5^c`*ӾOI#~$@h@|9A/@,𨮪b~Q}dҤI H 2p6Œ6'=:0H":u՟8qb8 .8I,GAbU X Ys_yկ I2ꫯ+v4) (1(PLX~ļBvUCo q}kϟX?T $ Dㅏb 6|f͚/WTT1{62 .˗߾pe&L$Mv { LeR._{01AbwڸqsoSVtۡٳg_rGϟT6U-RO9ٲ rkJfJUS@6l[,Y|ʔ)( nɂ<:Vӈ;UH :$1aM6=e˖jkk2c\@̠I̛7?=!1AxIejkM:X|D glڵ_ė  v|N7mڴH"\Fsƍnqa\m}۷CDO>BU fO$S%$Z:$Ç~ß_g"\xIIW}F[ XVvK43 Ǒ4(5kւŋ/'@茯F2`1}κ[I&x[I)DWr^~ٰc?nݺ" g?}ԨQ3NzQCwКl]5 .{9s椷wtX zn? Sc:rh ĉ]& Ngh"޽D}LWHPS %Cw߽YdkttvmJYh/7}߸s!g B" uֿ%ӽ`J89xҙ٧\WRt;H:F6Dk+_ ܻ*8 )`Swg!=C RwOH s롴EzYAE_nՏEiVMx❘=2h $j'mp&$JPEԫ]_SDs;YĢDGz6Gl.r}V1/g , W}!bqC$@&g4$q}z4#R0 +MP,zVQaCK;H~NCG a9?~3Xgk9z z70yDtFCc,i ? NHS\F743z%g+ *6q~?Wt'$$%% +cɨS$`fD3)))YGݒ܉ n$!tC.8w *E1ZNױUxh H|Ks*5655Blnw^8ER .EL  @LVnkHU<&F5{P r ԙfrr ̸cÜE{7@] l޼y=q r@ ŒT8(f mV+۷jKM  Y&w{IXCYb:2];frp4G}X!55G5OH:෶Dt+C-+gӽA:)Wwy *GN Y @η#P%x7ߍ]9Xx8;;;q8&φ˽[$8_s[4&$1E|&:l16+`PH :a+EHg.1\7btgqiG ޷!cX:dddIdĒ' |'"P!"pL9-u8@!8pYubp|yL; _Aѻv?@544۹9C5\*8R #Fͼ<Ӛϥ1}JWQӨh-  CF#ߥ( &.D)FasN^@Bnڥjkv|@@655aeee}{`)pGGrRL ćqH7! ~+!=b#$- nfb'yRKWD|P Kk\@@ &:mx|?-F ݜ0a@Sp;vC2&@ rxHPc"PăE⸌%8"2P!dݒW='=#6IC~Ũ6nZ3=jFsVEoQDt p.Z.5* k۔ &VRaWľn}/ 0E"p0@&EZJq ~zLwɛ"IՀZ9͚ l(#N4Dν؈{DOD5кp{5%Cc,l}æjBD$@IJ| D}'gkY 59a#c"P2v΄t)쎪`MQ6/#Uz ,[f; `0xF~H@^Bz FY Ǚ^RE/Јvތ8:@.h͡=pso!$ _pF-Y{:۞}zf~!sR!IENDB`yagf-0.9.3.2/src/images/back.png0000664000175000017500000000473202263216100016773 0ustar parallelsparallelsPNG  IHDR00WbKGD pHYs^tIME 'Em gIDATxYil\}͌wvB!+ 5 *"hm4t-EG7ҪUPY @H4QE mH Y2߼w3Ubɑ?r&7̽wιݙT k- #pbW ֿ8}" ϶,77=bļOM3:yh!K{Z֚,wtLؼ}-.ń40k)XpN뎫Qhnኁ ؾs ܼlª k/@lxm:"bBdZ}[߿ZLmAPX L?L:@N 6tɒk.oo*!'FκuUVUV1[K/Ŭ9SP Tt?wp H`!7 Ā,`2bp +bby+YRXljkkfP`. qOk_Sk~ېhJp6 "èU} nODF[k…7݇n{ihlJ )3 g&2fЃdsﺭNw~swIun0 ]FB2gFXFΛs9YukIa467ew?ƴ|;;>|oZ/=> i=w)t6*[7S[Vn`Ɵt-4T@N!΄?7'ןֶ+w{'Tvz]28wTZi 36>ýd0dH8C2\|^E)t1}|N\(Θ7k.Cd!w\L" <㯣};ObG㎞yhumowB' 4UH <.oz/~8z̗J{uoOZvQ4EěJVĸb#m]gL'l~artn@x užLY?={߾z{kgg x GygDpw:q_]Q5NTxĀ7(oƘ :lyn#`k׻ 2~ X=z7۟E_P1?oI1 x{_Z_cKbn$S" 2KJ ӟJo~s"7Q{vuLhOÆ3߈^XӷgiG18qn>p\bTp4Y`IENDB`yagf-0.9.3.2/src/images/box.png0000664000175000017500000002056002263216100016660 0ustar parallelsparallelsPNG  IHDR>agAMAܲ pHYsHHFk> vpAg01bKGDC IDATx}Ydyw{oڻzpf8Ch1DjK6N`$l Hb8F -8Ad$($m@Yh!)p齪k9yR][w3Lu[=lllllllO Ӽ3*@@@?>Co@"~w-3FѼH7b72},72uRFK|/$,>~.,'A$6-?+r%8!4N|z,BiQPQlzMG/ܿ}GViPǥwyG?E|khm)gsu H)`1>S'|%((`)=N( GςD@"߷ v ;m²tjyǼ<7'p s+^;NH|x.MfA"4M  $/" yqtyV U,m\BeeŇ|hV!B/H4PT5d19M{^?i˘S)ce  3" $ wg< DG;[ZDmm `^*kQ7C۳ >v+?3pa۰,Kמ,ebt2c,Vf3'dǥg7)!?d7>j{;PT 9p C}n eFxsrQT "ሀk)(VPJ8mli"3U1dl/L?L-FAl J j=zv}@R{6r1\ F.E칒`ǐ3Geò,pEB($ZpL8 _H_'o~ݣyTo[R9tk̞~fJVKW ǓYi\ a"!/ $! W'[~_zs^],@&\:auꨬ32a4Ј !Ӧ 5_؁~hoQ$!ЫR}7$VT^|@^\;yH P0n29&Sai /Kշ07ѱl0BU{`ay/7iDg_ŭs #0{=T#`42FcLBOJO:R9fkLpl0akKSk7y5ę`VyA+WR$}^k0[(,=\mc;Fpy˕nfUc1UquQ(w3)\~E-(˱ \m LcC@B3hs9XC0%-G )pa}}ydqn& =6m˕ip6xnuO9k&vV:zG[r$ %ۻ㨁I^]]|A-"r?(SA(DύOTw*INxKj s|[ڠq_x LlJ.G2a<Bx>Ip8p emm\UsK*`9V> !Q?p4Xs"ELexO&15>?>!rG} I8* |сf SvP4iND,QvE:!b sH$F#CML|C& ^-w އ%~Aʹ0[,cFj^|1͘G5L+/C:}?/a!| + ӱb$X5E%AAY ʲE@ky$HՅDk j|BK=Wxr"nM0batD 𓨁QC֯.>ye,D(LP0! ZXH"W(:=d\/=~ua(AR&,e%_xY?) T]c[hB ,Ӏ$ PUW{EyO-uTVBhK2%.5d$7>į- K4>@#D i $CDKdυXIQYZ G{{6[ #%}_ӿIhV|Y8&h74[EC̣wR #Utx֟Ukw")I!r?@oyyk;"/X@.>$bѕbya>奃Щ7UŹ9*^}+eF8C@<"HqSb?F|" lƊ>Cv$#+S~|1^a@Z!@B@~MϣrnzXNB >BFq+zL#d,>@QќR'ɘ1 ?zNs)Dƾ#[ wnG;8I4KkQx L̩F_"ڠ߀B|ό. Ê|@80ò{Vq5ֱ(cta F;7^$(6P߫\\!6wx".,όC 㐎ㆁA#̡ÎFoma/..^˫${{_NCȰqQ Ut]}6V7 <}m-OF1ID.|zɐ___,ṛPɫ}/P4+k*R#!:&`tmb h9- 腥.,΅=]W }S $>{} O?R)(9mH!!2Es@T(` ` HK}!`i8T%ΓP,VJՇ<]70#9P;USQF (%?a&,`hjx_8Asm^Q9 mò0+r9(LgV78WJI hl0hv޽Ņ瘆|1'B܇^Z7c, O)ǁ8M˚0, ~zRRd{8@C/@BǷ7ߞ5!!|ɵPT-A} m{wRF>˂P(cܧ (!0M+,Enlǁm;1R >,7Fid B㖮Sp) ]X_QW3JIp>49\(<Q88s UIR9& r T}8"DZu8g>[7xu)=.C! aᎻc Iiљo ؖ|?E) L  soS+Ɍ0F)uY (;P_ R 6ZG2M'ִ %Xvz- k6b R B@{m@uUo_}ϞSTؽN&\S] ! NE׃ ,),>p{uJB #`\-bd ;dͺhL@!a/E{; #9 :wCe\YhDkRB@:tPxGe$)02ނEHs!$$$EıRJi< ܗcP!h$c ao>.sG+A%d g"ӷ\A+^Mb¶Q _ yKPxL( ]?|ۦ =1 -h\+ڟY?0t,ԏ:*攟5'цp$(`{ŒI)FLR4=_":PBryJD~s9- IH Qq)F~wy0TFxq<,}sJ޸g'cry=8u@7.{p? 8] :AL @)g!c җDf&r{`m&2`:p, ra8"c ^J<8h< x.,3yK/?33}p+M|ɟ;2!aCgOD!LɥJh\ѝ5g#Љ\FWSQ[Yg`?R-#p9)2l"xJ6 N Ӳ骶)k)%jf+g(H &[0Kn5W5@Hðry$ t7DlW.GՀDc<2@zr2&p@Sn )~rmZkufmw9-\R H"fTHL ;yuWz2#xl c1#u77Օ`Ug:`IJcM7"cgzN |*<} H@矾R^õl!wUkXPP0P1dGm8ŰR"n^[WWG < ZXu'~PYZ_Ь2Ϝ:8EyhqHNSs݈Fh`ݰkxrx f%0EF^/8bnumt[́4{-[t ^APGb}c9U?wPzv pK:#xl#S\vQ[$d~_X]ăwGui1 @:RJӄ/UCڕ˧>KͥLelէBQC|5Td2'⿹f엻3֭rXq'cI\ðv񓃃5#: A >~200 VzQDh֢&N5M&>yUq]ҏ>ם~衇|*GۉJ]G1uBH:YGrV&KΞ9b>*'.i4|3Çe8fȩEYJ)jcPSEfl{N k ׬򏮹w>*͛~>S6lp繙xX^s奙+;-WZJ!RBI |~Q}G\={N24:y׮]ٝ9 lFD`߾}˖-[E1!+(!S8J"DIR cI!N4IFLLVBJ%9|ϡgr>no@;5/{WXyU+TG8NS)%QJ"@[K\א'Iqb\cl>"B# cb+@rpݷt8I$qg=^0;vz/햛2I7iZ7#_ukVҖBs;È'OfDJoYϼ.@>u]^y!9gg:rx麍s\Qy(uk X"̃O>Śg>Y V igZ +'q=q=k7A9NB7x_m,iwRm[Ny/w Byonmo޽oB@qQHzVcLݣG W_kF&ǵ!lYw֤HHӔ(IFa_F$+YkI__R{%N9s J%yE@6֚9U,UT=jZTӻUc,Q,(rIZBJ1X!Z$ BJ4Mi9H)Ic mmmc.k=WZ&'>ymLQ3Ňjh< ~cA3e¬Mfs!HSKKuM{[mAzH)b_gs79]yBiZS(\AWZHb"M[iQjhB[AYH)BؖCJ5'R" B;v8.]<V-^#-y)Affi3-f)Ć % QjFHJKd1{ARb03>}Ki͹9RmjTmjM-ij_cM=ܵQ! MֱN~t6$')vf ap$yZ+._.^z &&{zuhcq _$.jh'}׾o>AOG5H( aF1$)q !ARaLHi)Ra&Fln왌3u]Rx ̺uwam( Xd1|ϥP9 B< .epd3%f}m~Sסvr, 1x^x: Cvؾmqgp:0hk]A=֭F"mgiƹAkNiRmBU)1wA+p݃t<%+%aj~٤$IR9y.PgS:Ʋl%d2atttVabmjMd~`ַu}K.U'Od=k: n3()"KR%"4($"FΏ1a=X)i{ +6󊤣\.a~(B)EVcll 8^؅ Q,J\.Ǟ_N1+gYyԂAT+eN0$ CjZ:"* %.[J' 344ӧ[o>rÄaHRZC#<Җ֚'OǾS.82u;YAc܁5` dO{[q]+pRJnvy2Z8ի) nxxn::: <-[i&8qccctu}Zyzܨ祤q)o,sGP(+ c 3>>Ntww/w7n8+&5zjV^JB:X}⬆dekײw^rmmme;Yx12 j8Y|1m.1+I$!MS,===\r%_~}}Yd1bU L|u+hO<ӧjA% ^u&u(l 3w^#lm,OߟyиdϞ=g?}ֲd,YBP 8Μ93< -oݺVF`N"t$iL4C`۶mܹwyVqV(JT*VX<7+ٳBӂM #h57ƴPJYBcZ̷6mD&cͬ]ǏsܹֈeѢEXks2ccciֺR2VM3S uь^.b%l6O?M79rϟo!ۋ5W][lL[.Z0 /0$MV5-̑x^*( DQR|c -ݻwϲeˈ8qy(]v[n󢳳+O#32>>ZְRTU) -J, p9֭VTj yڵkKH}Wq^|Ň:)k4ehh1r\qL&Q,RV f2jRJ}4VAVCO>QJq!;> ڶ޽b Js I^{5"tvvfq]頟I###,^x.?;q}]> ^8??zw\/MD+@qzV1gĴB}~zx Y|9+w1 IENDB`yagf-0.9.3.2/src/images/clearblocks.png0000664000175000017500000000637602263216100020365 0ustar parallelsparallelsPNG  IHDR00WsRGBbKGD pHYs  tIME +& ~IDAThZ{p\Wy}{JJÒ-Ƕ$$nIu24LӤ0qBhB8BIad:Ӗ!C# CccK⇞lٲݻq+b;^63ovv3Oy /zajt卍BҖݶ`jh(uW:t?9ޏ FH6 ( AHfq) ZkYde(x!S-=fӲnZpyy١⺭w]8IyO.y-. lh];tuO_z}kbk+ pDF_ ڭDЎ }y!06ŀdl'A$H{б(vgD(y5@Tx-״KW,v/woS~8w: zǢuŋ'O4p qp<$@>Wߘ|ȿWCGpm+v/VpQݻWEM@(8s籯o"qabei()Hـm$@* NLbYQWPwXïUϤ9![^SSiyK(zpcܳ'z/}n(ܳKk篹 ) Ivlţ@䍔l@d7ͯFc8o޿sY hw_pߎPmxd֦UcF͉30u,)OCh8NlK\blkSٜ36]wuZoV:i`5Ռ*z⋙FBfr0LjrD_'o>"(V)XO3* V wVLҿ8ݨ# ׶ssG~;'WC θib`e@3CiFL3}ޕYI&y飼iN[cfDx#O6tD@tD(c Ĥ;Wbhf0[HGHmÚ4>4]XYD H1 sY֬ue13%$đ{Z=Vߑ;S pLNNE$Hɶh]A`x ^`AP AJ%!G ,XeJ :m :NZ]Y\wj{ߺeMs5Yge b/A)SfH@Eh1qNisc@$@Yw迥yֆ _^န/>% @)Kkr2Y dA\,x`J:eWJib+5:~cZH) BX[ #w/BNς5i-RH"]$N=SνVWII`E(ϕXQNUe/ 8 o/L\A!{Nd{^ 'Z1=@( J`eAz`] KV6j kHA X#$>>̋}Wo2G"XAI f_*1FJ x-q"t.YexKG~1%gXk_E (}o]c oueJ-INCe* ]Î+p4cI=s"NfOE_qJ$) $,EaxOԼ|zcQ~{=,Ih-VM߽w!>O~Y6" @g2 !@۶*W8F}h T{\z\GȌ0,%ӚW훆'yx ÿ[yy鈥4@K.\HԳp>FH@J)$ 1j^ǫFu5ًu*\034x9!Xy9  ȠaGښ ś\B%xkUͷƦ?YT[; Ki Ag0;1lCfGH )ؑ>*%3́uDtcSAXP418Ǚk:柈7Qۇ /vލF HtҎ͍׮6T o,u\qc%+ǀYw%NO"%nX 0Þ_aw420Z3YZ!v[ l]rVt@{tbjKNĹ˵%ߛrAǟ \:/IENDB`yagf-0.9.3.2/src/images/critical.png0000664000175000017500000000565502263216100017672 0ustar parallelsparallelsPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org< *IDATh՚klUU{{$-0t24NLPQDa"0@ | ИIa),Bg?9pdg9kkg_a\bWBq 7x@UhW\!`0 Jn >K8.c/}b,F@>ކ& Jtvvjoo?vy/"!D)0OߔR(Zc{!LL&q}lll)1buΜ9M̲rc l)e8X@h__H&9D"A*Yx;---љ'0zUVVV~#IX\ÇT*S[[rGG;Ƙs,~0 y5Zq]7uݰ?j@|ŮJ)N!y0:ި[w"z{W!^ %v&B*%Z)Tj dPJJK5כk"`Nf\8sm0J1v֬Ր86 ytn΀#Mvܮ]h#OAnEgzzIl݊f<GEIIΝ6?LM&Gy @9x&NADs嫕+Q,ϝj53gDC|߾ۋ6 rM(!P ~}˖f)YB0s3uyw7sb6D,v)hBN~GAkGHj*X2hίVAǶl֭a".u/Aӗ I ̦ VrJX_۴ps\Wqh4"( X #ū/w]C7rt˖K"r-pn9(Vo6 VDvodvf cr/˖1hJnoY-X#K{^>u*٬l6|c’%92ld.۷C-#LQ qrva\8nx=gzIݑE(]֨tN㔔&Y1 Hիj͚$j̡zl[Tr\1 [pq߱Yb$R*~ (/_{׭ ‚ᗇ(si][# (t\q]⥥c=3fjvFD8|a4~ATB0vBRYuP4Unt*M+fofDWJZLhQxk۸`,ʅd6KyCK$\(DYo|ioitiN^uPRe2N‰ŸLٳsض[%]xܱti#stÆlBORS)[wC)ř;99_d&)Bvhd k9n]gل,hK"6`@Q߷ox< G#Yk#9xp 3#KT'LGMl̘0/'sЇ֋W1; >.RS.-d\ggd2 ߟͻjP7L| \ץT&Jη$z\j,f'/ߖ/YcA~̃1Fvgȑ:t('"UϟOSO:r/([z.9x0$,%%b[[Q%< 8g-={,]]]T~\߯y>PlHqw`=:ظ#JTTT0bĈANW.k.TqD]RWGGǧ/~ǒ7n@Nf!_pR|ǬZAPnEn[5B7} ~trAa7?qpD}OUGuf3T,[sͯqPnn> *ҟ.iX k"ՍN޾G@VQb*w4MZ^]IENDB`yagf-0.9.3.2/src/images/cform.png0000664000175000017500000003301602263216100017176 0ustar parallelsparallelsPNG  IHDR`;я sRGBbKGD pHYs  tIME z&tEXtCommentCreated with GIMPW IDATxڜgYz9~;GCefeVE٦ f[FM5bb d H@/в,˳]k̈̌xt3xK^ /\s|. JT2#4dvP&#-h(cH~"NΙ S M{r@9/)fV ݆8 P ?[BD($h7# "BȩPDX )zhBY0$ g%d\?{25@%2^t;LlNg:LT/G%J?/8ch2ItnVQ-ߡa:D "BrND=~Y*2K iB?MD2>N~*mѶFB#T\#$mPI1u2S@!Ґ҆3aqmC}x憒n c2hi7rt@Od1؄2Vt J&o 7״ZHnr4&!QpDAQ^c]Ak0D˜ F@G䬰N3q9! !I1Fq}ఢ ea%'ہ9=i}d'r4`VkB%$,Ͳ&|( JJHYPl]$@% Le)%FU&Lj -B(8RR0nBj@cB#3x݂`cJwr`.PI;7rÔq]5T)D nsK`k,9 !ӏe494uIV8+B3dU8 "*C[;RS$Iiƀ6ɣb@Q#iAD.1QD:E-A&~%%Y@ȂX(H)B@"Y@ H~o2.)bCp([4 XI9q?dr!!DkI0vtRhW#EXh9P3hK~JA1l{6-ձmGQ#|(dT(1!e8xhSǀ HS()BfF&, 9DR5BX||JW c L tj2D,ȓg G*J ˗[~.&Rְ~9!R $orB@ʐQ`42Yag\~2XDdeHGª6!ldR)J~ ޿Wm&N8 򳧂%yšM2"JOJ%!h)pt_@T%y ٵ8`2c(U)ҐعY]bsrb50oM":V j.n1G6i詔(K=9A G_98(L>g#z"MC@JQHQ1vϞL9̏jv((Üfr !C* |ܯ/,[XGsA֤4gڍfDiGE Ed\|nyLI[~7 eM6Xo*ؿ%ׯ_/%i^Gqp(g}ַ+ýߍ\J*7A'U}lPU#Nt@#1>9i0z &fﱧڮY nHEbBBʖ{=sr]ؽQԉb뚏"`tHATK^< HMUquykbf&&BX-Hi$$K]qXq}H_hjƒ `@.[džq> zŧf>|{=94a@~L4#!=O 5Rrt4" Q~ovd~T|/ &͌} .9:ffw4HZQ2Z+b}i1M9k}~~~G؆if}x^G@KbY5!.m!jK"r$m{p7<;\?e9M<@v[FS_d ȖĆ_sYk~ib6(,9ʉUlm4mc hG-JY>pBP2@`ňPcYQʓK@*59hJPʢ;Rpys/Ij HsPqI  ˚=Ll" e#J8LHi`f c,VqkYcdF8v*'Rxy!'vU3UG\~h,4].H u#pMcai9[WjMQ߷ L5m[-f#ZDd)HjQѣeY'-Z *ULZ!9~J&H~NHzdI2pwd J(42Ә0@ .TJ$GC;JDBW#nHs}IC98X'}XBdR"d:I!J!1H}rRDF k'JJ2DȽ& &HĐ(ΤbR `6Kd*haДLH&OO %jDl6 R hQCd#Ց!qXe j1[Rc^!`)bB-,"M*S@{fqV]_SHS 2Ĝ&#+E$2*E(PiJ`"lj<6$!^*<~򘥾k a FPOx)F{.PmHl)2͂>vZ K2=W/kr>@BC!MiakɐK%Olޓ(ÄlL Z{/YB 5< #̛ @}2cG)2ӧZ*[APHFf"f#1@[vm=Gێ2zRUQ(hCAcQ8 IQR$'sEXT J6X)"̠]DJIɑ0D/؅%qQAKEXH"bf`2!QB)KkDTlp %L"## ZBXQ%#"[dV8;c0p`8$,쩫ij$QUȈ21@*r$DǧLbTTi=%lY3i IE%443LpUpӗPɌ4&j JJf0#4h%pF"$E20 fo* ;AzyP@K# %41u9R ihn.{leA #01 =| ESω0:BѣD!M)[Rnۡ7[xŚ׎7= ֊hA"R'B If+M%!-+Ʊ3̼bO{9X&jӟPZVOl?TDK79~i%G ol`L͏zq~%h @,LĔn (jΎ's+.?S|HrGP{KbBEbd 9l~7>Y(*S@Qkbg#v/DГ>SR(tÎ_}R/oCU %Q=]Ǣ:>'Vm:Aeve#^cwgu#O^WMj⒫?7Ӗy`'\=WZ?ҜA^*O-4~ 櫊7߷1w9^1~΅"Gǽϩɣ&]FR e.>w# jIDZF{u8 Rme.(o,Sv6.3;cc *Yk;~s0,-7nypNetVdr`)"NH4<#0n&]TȮG[ţ9O= VTG wh` />ܑҌdF3ņq W+tHXZ@F0dDղ_Gί_t b& 8_Ɏ R9>?n^ssKjqBh (:گst&x$:!mQA^!jj2k e Ȓq -9]WH!(BߞƗZYZirQc,PDҽB7f5/v,[Om3%mi\Y(2J ~-#%njB* XӳZZchՄu`FP!nFBP1SJF;b3k^;–=bqz;VuFGRB*~dFlboct!?DʴGH ` ߣ1m@H%=şO4R<m"VtQg e:\N,"d鑕ۏ>|̲F'i#!4ZyCE̎2ILI>I'DQHьRϸxc5T*}7rƗ($g6Wi0i(Xk)B)h)ӈvG !-^qFAbʐZc"H$/el!&;~V!F;@!@P%3wDFiM v+(ų\H*%U(̆tzBFC $ERo&QLQDiv4ڴK^3;AֱP %B$2Ю]1PU&JО;3o%SIjk *' "(mPJw=@ʑ0(]Z;K֐"@$ *}0ό]5"{#-* B$De4%WQwQ-ڮ6|VsdfսRgrB,(Z[ф^$ Ee$DȌ)xCi$(B `y RyyETm]r4¥i@CjX'(>F"e"Fφd!eR"֨QCCۉ1th'hrf;Z3 1$S@{((]A6 M;8+sDiP %\i H*! M%BP2YA4ec"()!r!Fl8!);AJ:r>3k'<]GB9Us͈!{pb|ѡ`t.DJ1` XiI[YGoBTEkX11QaFV!9ϨXd[M 1ðQd?"B"TȒca 'JZF"t\1 5ce@*!N ` ?Cu*#KΞFN9Vl«K'ksjgJ xY4>n?qP5A'<wHo醄2E`(W%Uh]X [Vg9XsP$L%;AR,[J@(SYg=aqtgҼ#`D͗{w?2\\Sہv-_|W<\6,& I<?7Is,jD"ד/8K7<_Z'Z8Ilk,^?{Lj"֒:`癘Gw~S/T6~xIB)XϾ(_io_~n ]+1t}COqsfC iBh,oJQ'yR/~w<pzn=c}Y,=zDB32V0!ƳÇӑ#Hق˖.͟|K^[w$)Sb1wYϬڱ89xS8^Ro|#1|.{F݂O~v,Iˁޡ۾-PyWk' F /$R+5 5J|"kl{g]p\|z=|< S ,_)fqO|) ?[2QRvf&yy/;q/6MĈ[Gy? bwJVa!9dJ`%S`cJP]VS7"9MI$2!W$dP:Kn;rH1Cv0e$YZGBӒ'vY" )ERbDo_I﬈f7ȸvv5ifABk6=U-nN.)$Z Q eX5(d.dIQ2sڂO$XaY[+1ȂR&D%J+& 'duJ"p2b*M1 882bN mJi $?a'TBJ-H䉽?ߏl;Kᜅ0֢j'jS1lD7CCTy@"e ҄6ԝ+AHU?bO I=w43;,BZ1V̰F2LL-~gcf ZRNycf!5#qvVSGr['\"1Hk ^3Lw۬IubiHLEEH9ݐSէk7;T\=e#jQւ~ Wu̗=! dLSXi 4u`nr@pCd&⪀VFD2%CLC e~:oSqsHpp~?x 3bŲa6STmJ ꕥi:l) "n._Xah31_z$ % J gy9CQ\r9y޼^q+f*)B5&x+7[!ry.~&N<*nʀi3F 98B jLEUC<%WR:)b::3 T 0U$JJ&l8 u*  #j<&aYV-`Qm ^VX{-)9\+Kk=||<4Lg18ԉh'&S4:T<5WT bpctd<5t&<3.CRݓÀ躢n ͪV8ip.3RDD W^YL`VM4GG!e\N\Hi{hwwl1?V`L0Lwo2WaZ"(~l z~uÕ[V^0Qc:t[ .ZT8| D4}HMvhdΎ'HCFcI=1$ChVtOhLXH;K~!:<@h<5LKJg<[.Ӵsb|H5J , -nC Γt^p~|"lA/ 0rzu(='[89cS$ƌGOۭ'Sb"WMH y&I[~onzjΔP⃡_(M#J\h|B 3M[3~}& 6~P~eh{=!ί,pY \VZڅW,c4SYxxcm5TE!lݕGB u}{bMKh+7oŹo-?2 jNo^c25a+r,W|D?{ Yy#-;vۅxE"9p~P+vf48TYf]"h"yx 8q1a(3x» +iIͤ2:eD!_2XjPJ6O6TRF*""Bnկ2ׯXG3&,U_4rx^-hK2Lw'yh~Ҁ *H*LtڒӅ'=Nh#c< {=Ru2Qƒ}֘5KP%r?se8+|^躞'm`iX#|\z}iԂMCgWeӎOtf/5kZM6Lw,TLʠsrX'- H= i OdɅ2T6<~yfsxfkq& cսnU}ǁ,;@5QZ䐲r8--qqt(\k[vx 1U`(y :N%qxң>Dہþ']zk޵k4ߏ||Xy+u7%C>RQbg)ISř:W[C-*MpB0e ide-HZ ˔(yKXyZoyD8N{}g1Vp9LqțGܟAyDUljdrha[~Ѽ`DyfN'w#:jYsuU f\ kVbe=?{s21wnk Ճ4t/`q F6]b9Ubg:St=k-F|5)).ta-J,[r*b(PA#L<}E6M&h] 0anu^B,+N m̲Wtx4mMz{{9}o\h Va"2X,.ڰaCo3kEdW\}""&N(#e'"A*q܌y]S,6m{^zC/bѺ:.]ʎ;ijjb477GommեKrvyq0W}ucď0< v]vyyӹF+#+#:-yW>6*-ȹ x@Ӂ /O^^{_w-G`]|#U}ٗzF{W+sT`C5\O:}@ {w>lw@%.gw)~X?5| ;otFI4HjYog89Î&#y^B(^'O:]u6 SdEzҔ%&!=m9YD<~`݁7{6໮'])9/n9w|N0LEUv$aܝ*,$#beQ3|{w!prz&@Q T`]VKޖGrPBi᧪L`h5aIMggTrgOqt'{D~8rif!I:s3j^-U:}s=zgb<{ϴ*F&._ ZJ0ÃT̾T1);{4#\8b_LѹMs=UdvK؎yxrFR{+*g~;TVšZsARsTW3}O_jz>2E-ّiXUMu_z{`egx}V@voESUKE3z٧$IФ`zEQ塃~+-xe S62Sn-^M=G'2m9o®YQ9oFxdf\;EJEL/ P,_RJ,H<.ծd.hWLݞq%Z[$uI!r/Uܶ5+ry';+}0||^5Trv[*ZZ1IZܶe㏞]>o;IS4au\P3Z&3YcGH7nR[v߽:h>'~ C Tį'b47kQU0O|5F/H7'Wکq76$%LWE1bOxq5a߼bG۠@LoE憏+k@P k/ =u $S: $RyZ>{8Ry\zb`#leT\qbPB a >*:jGn^`@0 /^S3R? O$lathlb)-/B^ɦo|{^ssBp>A8zlE!"~j րI0vNm$FI>~V )miwsT" tu@!YZ<|Xj-敍RkO)}ϔ{okݲۢ1s< Sx~#&ٽ{-u ֪(I$]`PP{uW-Z/_M/?巆*o; mpxUꅡA@$`$,7I0*lH(wE<@Xkyux\s޻Od =Mʪ=EEKw jHƟ6= 'R#ƀ) MCB^u"v5ww0ew^٠v KLmABD}5ES1ȩ` ^t0x"Ivl^+~>{wսks ï kWxfӳ(I("YGZD xn;}NvC>$)!lF؆b߮D}D;b!+?4gT2,I*BR1ň a!t6Qȣ9th "x".Y3FJBbHe 4UPU=oiR.{r̚(6]L)PSJ:+eea(!t+ݮƑX?$,Γv,D!L!a(!mݻrWt.{Fଜɮf5, PHIEHfծ7W/#ec.4Z&5n L(a$D^۳GdټoΐZw LǒW*4k[џ'y_ S :x׏?']^a9ng:X<}-yyvIENDB`yagf-0.9.3.2/src/images/deskew2.png0000664000175000017500000000622702263216100017440 0ustar parallelsparallelsPNG  IHDR00WsRGBbKGD pHYs11(RtIME /K'f IDAThݙ{l\U~?>xl֎w (e#%yJAIJ?݊-UD b]*-TT[Dh&pI6Rӄy8vbgl̝;s̝<{wUm{{Ϲ~߹PTBEE)ih*"EmGB3!oWc;@HP0Ϗ!T-.t +mADSH#/!AD8%uOD@rZTiLB TH -ɤ6PU퀝D~@|*jӟ@WWuEŸ:!9U\i#‘Ԟ_?%oWaG:Pd]/@WW߼+'|Rk휌׮VLEQp]:l?Qs~((J V}^f%iʧMk/i18% #DfLr"/L.m!DB8p&J^`EQ %Gs”&+P39.(EQ*Rrt:͛oO (#R֮UKƳ /Q$8eW˲ʕ+į^% (JES_}J)Bt%|)eB{FʫgU/@n𫱀(5R%m4f% #|LxضM6U׀@,e 數IN8$xP(Dk[vݺ fkСC D m۶QWWWd2'|iR__]wŽދUz"B2DFbLOO3<=ʣ>JKK /_ѣl߾[*iºujB"!E"\pضhnn.<ロ5kPt__} G4-w]i%m9kOMMsyt]/LNQ+-4IDн:Xh44dT*E24Mfggb u4uEޮѱ1lV8۷ogͬ]9^z%4M#H r1&''1 ̵`288333466233_|:8'XF/.u!`޽qnfiokzk+;, TUEu4MCuTU%\|kJVBr`ypܴa7tcfA@ Tk׮5k~z u%s*‘#Gd2%J׫J;s p!^~e2 @JK6?߿;wK[[X6z{{9u;w#8ߏaAD"ylٲ[oټy3?06lh4(DQ裏O?Ixy'bJ&@@< pvs;v[9H0;;BcDcc#ض,TX,HQ:Z[[ijj*!۶ҥK$IQDhmm-=pчzH) S_ E$-kVxw8"Ԁ,_@qR_B#߃H-Rr̵}ű}U+\Rc (.TMꋋLp ajp])eEK)qv¶@>/ZՒZ ]׭<.mcv8^\P dj&8UBml6eYA:mbYYl03;;_/T ! :6m&`WYZZ"NXӺ˳۶8|Dىs Щfٳgth4J; B>swZ28_Zšm+ ܧE,*eYd28f&6RJR$gFFܧ~.] 0K e^NBcE, n)GW GҶ R~3jj/t#"L0 ώ;#30 d??Bλr%`lۡk?>޷A lFQJ0|ocƹ _ 릔^UUwպVU}0W~ngsK|xhP>͡_O ruN7x㱦HD\,".U_J̬Յ (p $}rY11|h +4 @)|w}Y gCG SPe "]z ) @Y?q:tŷ/_|m$(H\beeׯ ;R%zjR1~I<>r/Mu,Ɋ'N3 Y:?^Q a#<(̌@k` ÀkE W/c؝K#ڕZy/ނ^R@ p9J;n}ea<ٽɯF;PLU3sn`@C,s@pj\0&a7pX`& V43psFX`BXK4YUBBZPsU9)…B H{ VbX$T2b p>N?e7g9J8Zcl2 qSeHY Dts6qƛaDeL I(91f!hP%Žw Vu\r5:i\|^WMdDx)le5sgN,1o3Av.gs}퍗_M@H"ϔ'dx.нv%o@zsomFgC& V|Ĥ/d `=Psus&/ @ i1|B@ģD<GG6Ձ\ ՟-S!bl"lIt7tMmT@Ffeȱ}}tjПêZ:T^: y?K1TEHb%rPKhB–ne+p~,'ט+Sfq  HyEA""%д0㔑1(h.DuO"NWT$܅ָ4qNΛK'zk=hMNHB1@+@\Yd\91JsIi G>MZ"[;]U!!@_є>< y,N)E:3 m!aHb9 :E>L#&{G\)`T]4AQ!)jH=n =%7j<~7l>d]=9^-"yzU5rYJ+0"\Rn#kiZDr@ݱgE")&4_۶ހ`AcVSI(g_xwۏ:̋Z~?~"?%<%I ]uϣ'?}ZZ{wWlm3M`Ukut, ]n۳u:~sPdDp|2>{m;/p=ò16ǻB TUf͚2o\d"8[ZQ {5p DhtDgExLOv~vlX\ŭ/{jeYh&BAnhhɲ4LO? #؆H\| \[?#`?hY%W4`0hcY-xsL&uZ%,2 tTD@#{l')^`wT 8LN4Ea]DXeI%qɒ*cw#jԦ0IϛR㯥D6Yߎ@0a/@#Ϩ .\ L Xk,O9K15m+}y`MӡiCD냸3:ȈGOx}xv#e>sDesgQ(y(`hIiP1a,G.ǒKUBAH~S#A2RAQ,WcdYy\]tLlh uJ'Z#i(@uEeKD^X2Jd N`͟b2գ-ʄuGPg D#zו9}[ Vu^-KDزJt+G@HDZo5鳠aZv{.t_Wmb @ȏ׏0C,!13SOb6|_߂ >6W67>ɳU)'+1E*Щލ򊕠$yjLD+ħn޾ܔ̗ƙs [NJ\ir2mSi6b[`:¡H^ȲpҜ3&X9'Kb^#׏xQt^!^y*k Bm7ZL#cHhx[̼+>DfsP< Ti2ar7L^A8ƶ;U% +ؐXnJ|l,*V!4$T5qO`p`Ets$|/+E$=sVU A:(Y])WJ2,@PNat?Eog(td".S `wK0]Ks@@ܭ}΄VNBl)ڷořu{qŅUZ\ -L}SJã߲av;PaL%۹P(ރ@ɶʍX8@8ن|n^O=έN  fv 5IZDD30I:" ̼k P_ڎSZ aQCi{?[%{5 ;۞f?RbRo^FR1!pc#9 ߜǜ#*FCge[Lˊ"q͏e4Ńh:R:>chhEڲkqy LqӍ̨yeg 8J({}gPk۹w>}GdqDP qpO9 \6 q>>c=kGgLEkmD{j&-bݺ/WX`ɗqA޷[],|ݟ3%dRT⺶H2**LTT1纻p;r~87Wxj5k_`s o}}"R경L9D4'ܪ-17|/I"2:u.&|m3O4`^45/0֍'YǰihG۝et qNL'ZԼI7ʊ ؎eJ4 [Ɖl[ZPzjȏWse|cޖř-lnkIBa<{&pvq8C}y%D>aHrO0 <^㾥8 Vǰ8'" )dNާ%0ұ4z}Wɦp<{7ăbH_.m4Š#)^.K, _g/{R)6; %}wqgcZf5WDuB^ŤzvQTUq>pD,Pz+&aQFxtӻ6m17bbX'DM&(^cGGGVb AN䵱{:tRk>|̇}g=Z'?GUe?&1AlHqdc[GtV<(qL?XzW(&5ѕabKGD pHYs7\7\ǤtIME'v\iTXtCommentCreated with GIMPd.e IDATxytS?ڬݶ`͎@Ґ4iN̴gi'tN{:iOӤif,rL,+0`ے-Y-Y%ٿ?}+ɒ13{=|UƵoi߶O-)sDz' G6Ar@vN$[},jݒUr BU<B{pb$(of}xW'8=gXÏ' 166F(`P|xb/l{{e`tbF-C^|5Y&.Ed2q=S4ccc8Bf'?ɷVk'-EDDLF].O"K{XD+  Ť~b~^PRPTXTkJRB@&122"߸{M6m@/,\@9AY=eC,Rއt_ѿ}m `0$.OB=z1}s $p. f?Ou_kr,qx%|B8ŷ} M[?bpu.A"ssD^bE3|>淿+aDp,O8P^Kx嗯 WW,Ҭ!$&- RF7j J4^JSqT%[pj/EEEko"@Dϟŋqk!XE>KH 0:88H `0v^`0xUj"x`e"P$BAjDn4fΞ=ˊ+(..<-pA˜.Y}."ZVt:]̟?l&|>l6,YDE5)zJ6$ "îL ձpBΝKRRx<Eӹ?L%ƒ @R\.4M4'D0#p5ٳvpBhlljjxa,jAjQo:;wM6_ҽ򗿤NJJʤvzz {As% j@vIHH/¬"*x<:::X`A$*,ZK.lo>[&j9cϞ=d2'-jkkA.p8Xn7*juϊ`F8@<ݥK(**BRMVRRBmm-uuulڴi$Ν;Gss3k׮  lذ9cӦM;v R^'''6t:* իt BV^-;nRN:d2Q266ʕ+b nzTWWSPPzaa!ZA!JKKX,` ~?T*4 .]BVs=k.9s 捻z RI___'蠯FٳgJGh(..Dq:rJ<#l7jjjxqݔs̙_{{;~CgggD?x<.hkkc…XVJKKɡj,YnjbQ=znP(FFGGgxx~~R(((6p\ =D me-9{ot8FJ%x<L&Sl0::JRRr\鄄z%V|TUUe˖E#111BS7>>A\.8 @ d2vqF~?^χ0<<w'@ (~_<^ГO>J;H~3̨@L&Jq#!!A t^d2 \.d2sRRXqMM ֭}FCRRRL/##CW0WO&DXƥ< >eьtBӹj͛INN7x5¡!vU5ߌF"1 `]|9eP(4/w:{,Ӳ(REUtMS t:ٷo ƍvs=`۩AV3|=`@ףh())KRVVFjj*w`|+_j244N !++v۷o'))`0ѣGٴiϗMWW b Xp! ZZZhkkc9s}w1 '|J3DiJUՕ//`ƍx< |{/555;n:⫫ΦP(DMM j5jT ٻw/_yp(JX`>l***Č޿?O?4Jfo_Z͓O>s=ƍ9<dffrJ~?nFJKK;vȲe"555"Aӑ` %%!LxKKKUyÈ)&o``AB]]]tvvrET/n: //Grr2UUUR[[KII gΜ!f3*())tc=ᅬBnzq8,Y'V\IGGft"6 $Sܹs&//OZ^'oii)"hX^ I/"{/eeelݺU c+J࠸rQ*) jc- x^|X,Bx^FFFapp\N0$H#1)(%//9s _מ E N`HIAAs̹~3t.<0 2:::i=>O8H!eX2DR($$$HDdrDឿSmlܸq?uT kJP($Htwwsܹ) FG]ƥбIRVEMڥ$$$r9XVWrssOLo-^7VUUEZZ${x;L&<õ:/_fhhHx2;8uK*;NZFc00 hZz=:^V .KHHc5w x{S܀% <$Lo?=555_ӧO }lK.QPPOcc#6lߧB~mΝ;Gzz:*˖-_BMM .q).\F Kkk+===t:`||zDX,r***P*TWW HMMnsi, Ǐc6l8NF#j;Frrn™3gh4ގ\.\ PJ)--]zȑCܠpXYYɽx<+=Cnn8100 $55%KPUU%[ݍRN.^H(37--jݻQTVtuuq̙Ӆ 8|0~\ŋٳdeep8(((f @Ra6INNfҥ RSSIII!''ZMOO&\jl6ER122իץ(.pf188J^HNA *++ikk#%%ٌPRB $%% $'5dee NuҞR$11L)//'++***PմO}r!ڵ"^/.|!ZZZ0 v; F@ @GG:*t^DӱBP(o,u$6~Qj;d.AXpPѠq:hڈ>x].W%XVA ) !A~?r?i&***?.v\UTAZ-pmL1<P(p\_a⚗'xp,ڗZm7+ˈ4pNS,"?F)љJ6`p1I?25/1C4 `qnDrSåqJJJ"77W$P(tuu1s(j eUt"J1L@duC$0$L&# ϋk;R8n<}4q#­~ӟ"4x:*J'$ I(WV -H:ٕSKM3ڵ~;3#G`җD?uuu߿ڿ?Pm۶qajpݻw/ ;wԩSh4ؿ?̤*}Qj5~ hiia\pm۶ vj*=f+W^!%%Vˉ'?~,X@EEv~P(zq\R1 I9#(nDVdARxe/xu\3 o"`~Ν;ǚ5k8r_I琘6 ZNl6{(=N 1LXVRRR&% 6| $M%uNX 77e˖ŋD +QeSe8 2fvڇ%~?-_]gg'gϞߖmXzu. /TAǏgttTH:3g ^JeeU|$wEWW6xxאd|g}Jmy(O -˙?P{NK g inhh7䡇jGדEee%<q=C]]<ϟz=sxؽ{7>ロ}@rr2tvv";f >1!Gh4vލg999tvvO~'NvZ;m{mFii)rd27nŋT*я~'> Bw捻dR@ 7K#Xh˗/?tcŊ8q!:::Z ]L&`0PXXlFѰe^z%a[t)6mJ֯_?͛z/^oR,n[qF*++INNfժU"D688fW_`0PQQAYY2$n׮]u]zJYoʕ$%% ?D  lllҥKlٲKrr2 8$99&l6ZF#TTTl2!K{EP8N!K蠮LΝ;innFדJh4FX-3 =i999&X$&&RXXHvvkꫯb4H0}!nΟ?/,Z7|J%K,1Zq), ͆j@bb@ Աl\|)vI/pIl6===bKIpb- d|ȑ#d>9rB666t:tww ʕ+%99YX/IIIrvAkk+tJ%ϧfaͼ#g W ^5jҺf;N C47xŋ ZYFX-cZ)% &%%%&:l$%%ߗ@ RhRSSq~L&SLPPP0{aD7w#TY,N>MaaP.]OӁ ;w !)4MǓ9NRvm|>*Js+]mT[N ]6,|7'Ax}?% *++$7 _$'j"k`t{K_Νr}a0(++SXXjt2|QzMMMn4 p%dQ4?0---l߾ۋd|RbLVeѢE;w{ǼKY|;9viiilٲE$GyqZZZ(,,?ȼy󄥰'NPTTŋY~====~qsKYb`^/yj[__bfJ%Wxطo_D?'Ov BTUUR;餥9 /@ii)Ǐ| _`߾}8NBgΜh4RZZ*GKhB9Ѐ^'=="r9NE(ųD٩8AtYDk$k`r/_\o=IDAT )//g޼yT*.]Drr2haJB6N'6l?ٰa^f8 㡥jOOODL@VV*!łfرcjZZZ@Ѱ`:h`0VE|^8ۺu+`?z^etwwuֈ(ph%R&)d+Õ7RT_7VdstL&pOLg@tɼ }}  b֭|{n6n܈lfժU'<|%jii$[o|_"Css3/ýp$$$ŋIMMRib{%q)v>$b(:JV?Wr蠽ӉCTŋt:X, _YYZF#jޞad2---vm1Ru` i~B94m Vԇ4p%#IxѦd$5zl6lo<ѣGy{0K, E@0 SY bY>L^^VP;v O \3.u-[g1Z(4]瞈. ^[)Xz9G|,{H3PJj0f``F#tҠI(X. o)Y}xFxƣY-Doq FUV]*̙#foxx)*3%Vi`yHHH ӧIJJbӦM. T,^brrrZg?O=>㗢GFFҢT*' \"V)ܙRp]]]餸˗ l۶#GL֭[E}o͗e9}4EEE޽{E_fBAKK > v;ΩS322¾}tBFKJ7|~?wZ  D*8CBicAJw^n7/r/ X,.1/_._"߶m2sk~yng>sdt:cٲel޼Gׯ!9O?1v)VEų~BCCCYLe>\N@*/ )tbZ?>PXXHYYͤ'Zl6яpr8%"#<ˮ]Zx^NaY|9+V`ɒ%\5kְ~zrrrWʦM"âtM n8sH(?'K42@˵w*N8L ߬V+ZY]СCd2fҥ3>>wA~~>YYY( ⽣&d2233ED`'xBBRZZJEEwy(#hooT2L'pDMI[xax\%^?,Q*q㡻IOOl6Fjj* 9~8zzh4qt:8s :NΜ9Czz:QWWhK.EP-Rŏ Nox=%._O_Ws"|e2^CRRhZyGEFЪ*?WMHe2˖-XDQ\\ҥK9rrLv>Rٳ hѢ&֬bԈo~# ?(n!Nے 6::*>;xhmmell RInn.jE7|L~amܹ\A, 8~?Osyjjjr% xٱcbƍ>|կbXx"{j2سg`;vp4 7oF&[oLN\KOOgϞ=|_>aTTvRSSE|FFFرcرcr^{6ΝΝ;ꢴn1'?IKK ȋ/^'77WT夥HJJɓ8qcccx<^QL&tERqIcǎHXbRRRs8Gww7MMM8rss1Lkg`nˀcZ#0`*>i3| CDN)Sh8׾Ʈ]PT$%%sN |̙3q^u(((ȑ#$&&`~ (iXVVX ^lHNNpHBB###|>֭['aEsU8{a`*]2%5T[8q +J'fdd t:qtwwcl6F;wxtx<222 8wB/\d }}}89w˖-d2r#11-[HCCIgB)K@鹡}F> .R熆f3z<sKZ@ Ud2z=dffatt\Ĺ@ 0uuu"#T/(z eAp%h… z(:ڤ+ .{,0ӟd~222BGGZQ9{vNc|;ߡ R]]͂ hiiAVl2v;V\N__&|>222DU4 Q؈n1jnIh0|2o۷oȑ#Y,t:999cك'///;/qMJh6Yv-n^{M\1,%jPczz:?Qs\Nvv6@~,X@qq1N6o'OD׋E;iiiall`0(*I,S\\0vΝ;cŋYr%?яHOO'//JկCގjb裏r x vwz4҄)۷s W Bpr9K(UQQ%KDZ-VUuijjb޼yB!|>j^H D5kp1A08 7._Rd֭a2P(g_"dՓi4._|@0Di'/^(`n:/_jEDj0䮻y}"MMM~^sm: .bnlQd:)) BZZuuu]&e2d:`Iw0X^ky'ػw/DQx02-- 'p8Da~?###={3gΠRXjk׮333Nkk+v?KII heʕ={VRSSYnYYY?ǃE"I.]"++d1Q`2H%O$. ʕ+Z|ӟJ0jfߍ?%FFFT*t_Q|>jZ tBQ$ٹsXB(T*)))~|>;HB.Ӄ` AQJ!;]YA!:'> n1-. Aaї[h6lVw ׯGPHii)WFKggPD!#h4#RIBEUf\۫hnnץxT1P7W(-331VΑr9<wy'˗/XF@ղvZV+x^,XΝ;cxxJ\.GӑJ(U v]]f9=nA[PUU?//E>bjW;jywx뭷dn77H*m*ǯRN `llLSSSYZZmmmr|>###lܸJ__0JjQQJZN3>>0z gϞr\.XrnTWWӃ$//lQ3@GΤ pӄ@i)**)Tx^Z")T"JV+.ЁۜN'Νl6OzRp8$%%!˅__()mx8{,YF$q `ppDF#UUUF%ׯn@2lbV0p$K`,_?lZ'^Υ#Bj3g(^z%Ξ=+*|>Qf ÇBMM,O8!VRB|>.]ʋ/8'Qz0p*I_0Ʋ.J^~wf$v"//OVׯFh4@bb" SYYwߍnɨA#tYE zAQQ~_d 4q3:Sj^Ӯ544$I##GpqJlۋcrr2:/^dddDdGS*|>.\Ȇ $!!AfQ۸ 0gV y=f𯴴R-vVmiiJ,zR)0{(@ '$$CVVJR|_nx</_.httTp࠘SN mjjvGd6)"ʡъ{3}߸޶7"S KuS F]-4b7kr([<-L"n?gg},npKފKM~)!e3E3A[zQUЮB,7iLBf@8!\]ox ?>fHCM< c@+u'ٻad.Nx߉>{1fOG@ Fq\?>ral$zL@*$NOOv~& h<`Wk% zHix(wo+xϾ\PJkLopµJ=qxM h l`C fMli&q }]e]1b ob4C׫Q" 4oᖂhB\h5 IENDB`yagf-0.9.3.2/src/images/edit_paste.png0000664000175000017500000001133402263216100020210 0ustar parallelsparallelsPNG  IHDR``w8 pHYsHHFk>bKGDNIDATx][lWz ,ɑ|QlY,n1$.n o }KQh_苁h mbo:1"YwYw(g3 *s9y-ml+`{ij'$rW_}ai\.={lt׮]MOl%G\'O.B(1uر_pxhbpҥ?;MӺ[[[AUU@%ͻ!;vxܹVI(֍H--- {l$w)Iy<7 A_|yr7AANOOܹaޮpsO'''wmnnNx㍧r>iZ@Kg(7oη>FhA ;ST!322AE|G? AG}իW/a2<nDPP p֭8xϏ9A Jlx9AFR*$[믿 zARmc MI Ii|ePaiaʓ‰D'|rjc#1GЁmqXǏ׭oOKѣɀMB8F;poM֖ n"Xxokk_F?̵k ~5 w(JKX-l f(*(٦8LFC|3 |?3g\9q:SKު)!z |?v_v4h )#5FtR+9eЕNZjc݂h4 [EyTXȼp*'7n|oKovҭ\+ÍPsEG€[vp\)䘗t&O.VY B^ˢ$k(fV^P`OۏR_={vÍ\+A ;:{0o(%S[A,*dg Z Ai88xXway0C"G"@E.m̍: tn{?!b(}Fa8p=̠g O^F,ӈEUľ]5/ a O+/ CZm3y饗`7dQh ## xݾ}wzzq(dy;Tvuxw/y ,+2{r!A^xqj4Jڃ~O91pAye[*XNm000wޅ%܌/ ;v ::: a6 nsQB8ǨEM…h`C<˺BCN>LJy P[4 &+ZxѼ6 9QYp~*~߭]MD vm/ 9ˣu4Љ Z[[2IIxm~0T@ϢGpJ~X}!QcTHZ3 ʻH[%B.)re"oj_$>KqfDXa4ʪ_:u¯8 M1WDH|DwDŽ@0B;-ȸScn؇'⢞Z >+Rje<ѡ{C byk`@Oh&VjAtY=|V; 'lj +Ct\:)>+]jₚњh`Dʨhf{B檔hjdF\Ov؇"sB>{+P.!5X ^(^+)Ѝ<YD}Szc2$ZfŨj$ a c[H^߄$J]l~1U*L=-WVʯE+$XDQ&X[M1|h!yMȓN*Jir&edds a8? +c_Bk LaRkbS`) '\an:N 1d˶;OўN.- }a6?Bwђ֕CM+T@ϞiݑpV7eٵ ska>#OFݡvttypa#Gү?W]3Vm$`PCg-,44˭6&0?sΕSN)_>>UEmׂp07,B&&SD:%Յhtu1N0((憆q 1Ou2[("ws O.M cό'o6(dE9O8tl^3dvvVC/h#H`<|3)Dޅ{~2ڋtϲ8oX%/f! yFV{F8g7i MU`>Q'T x8}혅fm" !'΅(If:{B+ lJpAGs-;'ȳu(Xu>.6*D=`! FJB>\Nj*}W({ A"N%*ʃ>۶}N!}:DR%Hr,,$7ŠͬKT*\.#!/кtbX[__¹o,2^\|X77NI@8Am_=qCO .^bm(|*B ]]]=ԋ}/:yL&3`! &|VtBXPomm@~qy/`Νo|gcH !dYl߿XrYM&r[ %H$Bf}wywTJH@6ŁOVJΥKy~%Ƈ>~&G^?̷7< `uu ر?ʵB{r @\#Ay$\v~/~t?U-WIij- U>sxϧzr(cUYֽJHD:sg_k=Gπ;922Z4?=P]nߚ0Mx"!W羺pmԳO2|ԩowb΃%ߖ֍/&룑=ȥEuwg eNzz|V"pߒ[癆vCq% V"%C=sj.#y>5XC.0wΌkt}q0\DQ0MY_!x?vhW>í{1&L@TՋcsT@XoP.kŦ1dW:NvF'`(a/!24. bckϐ 0¯ꇑ=CgHFqB8E_> =T2AdIޕ/\rM͟_!$f' _}m(g!89?E T[De8 v!_6B.s'Mr-EzB~zΠ 閕`o ̼fpJ*җXw>P!?̛ t¼D ^;AˉZGp(@b#<anT4][n^{Ġ>=нk^a.q.[A)qz7LPTP;?Ï99;wuÅɿqxh]G8_6h+nr $C$qf$PxY<{TT=,Ku >;`)!+JG >;ZOygLŶV_v"aP8Ú/c!ON$lzB3ێ@'`YD"6a7m;^z("4Ǿ'pq 5?n.Wi]Gxmٗhŭ=8"+^co\eh`yT;g ޾.d)~o$[.Z< +s|$P$mk=ì⊱ +ýP[9?v;ι7k_У)-|+ nskȠ^UF"rL@{~qa\ґУOK%pWx.σMMO=pcK߬x\x;@z[ZL|} G4WOr(s ~Ep҅Uuϡ5,׳+//wQcͫ̐Liցqm8(Û,䓁p snLȦOK1@Tw7z5_K`\e.EK, rA!Ƙ r}xd!orPVY&" J$J 0H'\o5 ,˖%r|sBAR) zD^e!%, Tgos~'!e 9tG3T.hs~?[l%iP!ALPǶ`d0MRosNG |]>`X!o,p 'S8֭Xq`[lBf` Țx+`e`XoBmHC>`i7.lg" s$G OTU~]{BCwZBB3aiH 88s rM*Pb&X.JP DbR"3J\EX;0c48V`@s]7/LFEl:8 YPYZ=AoLIP"zy @b t QUo[ 0J@ @9#ٿoK&솚+/];/3Jatq'!"i!A9GΏ뮻={1ǔOdM|͍02IĒ0 j@(LÂ[D c(/ زڤF' pJ7)nYcvPY-޻o:&Ao䬽Q1x7Ϣu76e˖Bգ;x陸ʯp)}mhb.1סRj@FW4 ѱ v%͢3Np$L;LYjKV R6bq*fM2}yUe9'JYxXN4osmh.8.vRu->poڴrr'ɵe;ŃD&bYѸ D 9A?I~ [FO0ݭ5S&#w8(+;e{8OppyLCg|ޞj/Kx.0qpdž$R"$RPYB% T::ao$OD/߹j:2iͪ-~:4vGw8OJTNٺ; F֏׵pd˥ݶW Զ-ɶP^Ɣ1y9/]=ԢHet0%f`3I"@(A,(-a m:1:S-{Sg"94 Za3D(-@(R:cz2a@8lsG(q(L~rycsBiii!ehT72s mN v ۆqiL5#rt$IGN.ۖ,]LF-&G &”\%yȬ_o䆇+[slܯٔUd9}Hi^Ҍ(\< ~ƠD{S,^ h>C~u<IENDB`yagf-0.9.3.2/src/images/filefind.png0000664000175000017500000001101002263216100017636 0ustar parallelsparallelsPNG  IHDR00WsRGBbKGD.Ni pHYsu85tIME1`tEXtCommentCreated with GIMPWcIDAThՙfUy?s}^g3 Pc,#P@gP,J411&jdFI Ta1U a`hfmm]zp{XV{>gC2?r]R _{8x( giJecpa!B[:bݏ ;89kg(rr[j&ͬ| !tZUBDq .x|>844p,84uB/DryddyRrm6۩?2AJˣeᬥ|7n7}_Rv $o`졇!<^L_t~itO-hFJ! OJXܸTԸSf}%.~Phm[:y>K;O_~Sf,%lXO+4'4B 'RIy Z.kC1q$ #ky%)Ug{pѧ=tb$pѦ`}^}p?:|}Xew=?70vqfD '"ܽ\q˹BKƽ9Gy8r$1kU sOOV{ڝ߿ڡW^}-ʫD|0$0@yɝp$'vX8paO} !&xjԗjw3 h0Ʋ yM!~$w" H,R s7ճu`vWXEpgڢe?IԼ~|ww*__޷/km/ ]RwA $sAjZ8NΑ~rqwa#NRrmJ9\3'_C0/`_gii{ ?2ӟ254Pd9NҶ}'=ph[,=4ڀ@u s!%Zc!%֚jBJIXkXkl $1$I+PyLR$JD'` ;)R^5)R!ZPAuyŽTk.90222B3#={1oۘXڙvs;\ 6E7c]`-~Yz> ӳyjOt ‚i2'f֙V@UX_W0:H"(Y9(2+tkE82ciRJ֫3ê,dyR4,Οjn<[xiD+ưzଵuE-H,:rB@JoV(Ҕr|,d)ki[Cs+{S2,J(bE4Ylf|G4 -E1fk-ks$iJec vI&)%KKKhc癙)1`1P `3˓F3}GOZS ՛_0eYFexv2]XQ c,Ie(50gԚex^As,//y^/u:ey9Gt6ߗzot98h%j%XRT5 OA VvLdžz XG ?'wbxn.~CV[\+%ztdhO u=#SC:(qB;0N`xH!`^AJI@fwDvRt\> vmcu{26iټ^^?4!%SPiQ׉(,G("xR6BRB c¯AfwI!GaRzXDQi3TC5I ' ;U>+3g<~zW(hEK.nmã~p^83aSU GH!('KԪ0$}R)+?)J(OR.8S~׮S>}}}T*UYt?gu](r$vۭ׏ nh,Τ$8G EtFq-(Rg≣\}:+\@x+/zݱc|ߟ81~ %5Aj%Bfq"#9߸;9ϠXd:*J)Ft<cLy^wȑ#My^ˎ(N[;vp ~xRVEhGmp4MҔٓhY<#R1ΛXWST'IGxGf~~%,;ÅoٳnjR#z=$)vmyN$(0Ʋ@$Iq޽{hPThZq{ݭGizg8۷/ݻwM7[Ivɏ{uc#lX7Z"'<녹O(TW & 02P^Rg||d~~(h,//jpp<,|.,BJԱ=(aٰvzr)|EZ"=jGdڏgcPcb|Ap099 Q155ŦMzZVJ1zjJ=8Ws3Bؿ?Zay饗 ZK,..fxxܭŷ:1(ؼy37o9G$"8a!rU9غu+{RPi4LNN2>>ZJV,gdYv*="썮+[k{tV<\qrI9B]"s;y)P~J=[p7SVyǹkgzz),cڵcT*!8tBYt]w/:txT*<\{lڴG2==Me@J%ޑ wt3J&s)J<\wulݺ^{'NZ,k֬9x~~5Ƙ=}JR *{1J +\~eq 7m6^yNfq^9,4I0 [BQ >v=p9gܥKH|6Dz(g.G%пB $,+?3ꖣ>W9-K77k/bk5wö&b#'|If X7/yèsn„R5ÿw? z iRש}\nD%EmcPKE&6g: "vbtxװ&%+Æk1y zoCj}f:zT $volmkͱ偹[uAngϯw3\~Sc{<`t/|#QРjh-K뉤BHƶ̟AEL΍ŹUTA\չrxO6~ziʡ&/N2JR!qBi;̈́Bm'62`YGRzŧСVήE% &]$ Qp1Pjټ 5Ri2c;8Dp4 B&FlՓXkI8yIDyl>=3 CR!Fj+F<Ϟ; =,t󮷟AOr=[vq |bG9Dfez4ڻyxDr,^<}r_| JH`WJMr[^EXoo\1eΗ?#GvV BC)V 5Rj>Q9XkGc\yJ$i{oB#clVcFˊ2|g tWw4 <'c>zn_ PaG>xMTB<uk?[SB+1~0 8z&t7kt r"ٰ6vm9fo r;_Fe+yIaa[sժUTy9}\i֝cZ<;1G@j }5H6~G f?MJ\Pst;~r<"Q> vH)v$In/xژg_V{sO?ȍ~:ʳ/% x}(Js믿>JB+3΂YxYŏ[n;,4ir兓B, GD*ّr{nRvc:1ErѪ9h3ma|6lzOꗾme_^?d|8o~yŻ_tSHwFnh$I'ZJKm{_uw:* Ω?_|u H#wT1.zj|S..?'.̨R>>5yhiWǥRV?ٲnȲ$IZlQYWlZRFYt`{JaOII\St_&T+MwhnXUj[3diF٤hl6I,:۠,#s$,+m<79;J_J$i}?dDeC wnYjʢEK!rLdY1vGpSjYᘆ\Ҳ0'c Z(VQTh43ty2NR?HZ%m"K&+s`z%Wݲ~ԤZ-k׮ecHӴvvj/p(:~mcVCٜIdWgs]`o,Mydk?E#׃֚(}枞ppNx1!Y+nD -Z(Rʂt=tރ⺯·qJcz_mOɾfqt 2Җ=y^31si,vw>&2qCV3aV)8fͣnù~v]u{W,;֫RhfY!5cjG$p9 ؘlF05=mf}w|+-,,#cj*Yfs,4S4_^B`\ZǮZ3'j^[VׄRPKo k)AI)Z ,N>ͻ":gfdΝccnG@Hb4˳{Eka X4JO>9Z|yZ0 U2J)!BVJ)yNa!"B,Zg[n޲eKOJ 6A~KPm[#WT\RqA B8! )R:RZkOZ;c},Fٳرcsн5FNIENDB`yagf-0.9.3.2/src/images/fileopen.png0000664000175000017500000000362102263216100017670 0ustar parallelsparallelsPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org<IDATh՚ϫdG?T7! J`fEtnEN2HFpDp 2BDtV2+31/3o|?׽UŽ}w{:)hnߺu>0(9)YZ(.ׯ? (-H"ݸqmo@eu'_|0iV|}N5fUz)%)-o^Ͱeߗ7Km2ED^ŰӶfe2sߛVN s t On_{w)g7u@~cB_> rA`x lX#Qz?hx`AYD6:4j!̌"٠!"BBGo m _|>sK}wrK ~Ʒ>~ `5Cj|Zi@4#%u1d~'3Ξ}/޽ث7[⁙bl)tY?'OxҒEâ6P𛀷1(IK,Xb5 `?`Ut [)b- (J|Y`FQؔ'~Z$xn|67KAsm`0<̦u0Dxƅ[0@ d=b {|3@Jjs ӭQ?C%W^c:i1y| &P~Dܩ,?MQT%rƛ;woM`F9g#nⳆLG sZ!:S{{֭Ҩd\X"o& *PawyLGLw;482!Y`YB%ߌy~of!xLNHDܡBŬf(0Bl6 mgs63nQ8|:?/!UED4~g1jS6Qj. ݬNl [cZeL)M S) hܞfnw0DY(/ffB^U_s,S˽}H)Rp^A]w_#8S4@! t^J}61\cyQ-1Aw9]T# fԎ"INx93+IYu,sX2@z!,i*wJ']N^&I[!,::Ł< 28_A0Frם wn,wQN&/'!2-b15r]A?GtCfMPAFyi@t߻2-u3!՗EBn$ xS1`f8', 2 ;w]dAD[hjV\kx(( 9|W]Н>P%p4^?:bcfF$URD4x _K" GmV9DqޥڠSHh'^̍SBLK*D}L1z3$%1T[-7N̚%Z?U;ʀg.܍G\~2(0ʀK;3"|  ];7_{᫯GsIYD󃛿}g/O<*. O  _!c3ja([9-+IENDB`yagf-0.9.3.2/src/images/forward.png0000664000175000017500000000457102263216100017540 0ustar parallelsparallelsPNG  IHDR00WbKGD pHYs^tIME  eP IDATxY{.XkFkRikMc6REJGZ"*Ea ]]w;3L?Ι;sﮡ IY~~;s~NI;6[]Q&\!ĉ#`LGWឳNy^eJ:cQ+׽qg8VM^K~}So^<?d-;*(ong?:\|V]z=MAqĆq\Z7l($_D n٣,~p&u-T_7G;\\/4e865f20xGa%@2Z@B>@g~H,^4 ~̺PzAMs;2B!/y. ]? Y1"6Ma! P΀ (Ɵֈ;okzG*!ܮ>?0Lz-c#0rٴLa+{rQPz{zK.Oȓ %$>̱zF!^r=.>egQĦNW@D&"JE^#B!3g1)6n=q/9(s1D.&("H*AM) , Y (SlUpKͦ;UfB&!Mf^X BQ;>MFPSHa8e[%{0:?O.Qh#ϸ05bK-Dۊ <_`6i#> b[$S.y#nJ!>o&\4@nVBCzsã#FBJǜ"*YP"@H(@RUO&S Jf;HV{U;Q͏̔0PO/GY@7fslLtNʁcgD:AweComr_U Xt@:ع(~*U\7 aŽ”Qm,Ky\GT@qMX*􄰒_qPPB`U?ۑ4q .c[f[ $"FRUP"+1@}} ){KR{; Ǽ@h^[5eğ0eSM*>NC:B@{8zWo\5jCKBeX$"m8QTg@BNɛ*U#=+_m|oКSƌ2D,:S"p!H>ui62o;;m`W`z)}1=-b&N9]L(ۙ%L30'!F+U+}Ń(@# /̀,@MĹbDt.!lٴ}6DvD<鸮[^'t@?H>c>wλHOR晬,S7ڶw[ T3rA;*'8!ǁoGj-W,3AT&7J/T!@f9ݝ֍=eDv=jcڷ(N4:JFjQ9Ԃw׼,_}So˲.|sp&@!׈x[^.(R)Y`[samY@;> )ݠ@lӱ f![_܊*;^ZPMPe4V^Kx|K%Ў-[XDȏ9=xuR1O%fBkЖ%?`7xV]o| cG|uú6K[`iz ڰd1nXtbe ʚv"l:y2/l^ z:)#ܠuX `|h57>uWO>uu]tQ8i'ıcĠ=:IENDB`yagf-0.9.3.2/src/images/info.png0000664000175000017500000000735402263216100017031 0ustar parallelsparallelsPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org<iIDATh՚{lTW~?{g3/!)@h6Z4JVۖ QRZu[UP#RKԮVZ.dMZ%Ym-8cl3f<}?I6IZGؾ{wFh|bB _6 (5YB@(@:=@-u*PjZk3}% h`CX; l>fvj:_.R:>>>{رo)`RkT@?7bɓ'(J7tp[Zy]tжmkZRtɓog}'@`]Çv !88C<({|N葑3ӟO>! lr=z={<`8m/pAGGZkɓ~*ʫZk3B{m'N<߿Q)8s9~+ 4P$*dر)O̲w `ttt011qi߾}{Z7?5?x.ǥ8}*\Bl6êLڒXR`{ZIe)ξ΍i*NEZ-?/?~E?>m fc;v,JiBD__5plY˦tqߦ1*g>ͺKiƥS|x:krI޻iHXEOOZ;~ ۙӊݲm۶^koo;)%Bƿ#sS/w'ȥ-ϱ{K>zGF/]M4.Or_?H0RJ6l@^/?Ϟ?Eཕ[F3]\'NAJlg7E7o[OX\d [&!dljSg.!#)WcHIlWa: ?m3&)LBIJ2 8zzr|! SxZSmxF]*wueZSl/%P(5R)Jf"-F S ]ػkEZ.#xJALWA)E^gڵk? |}q}eBXm4JGaCi0 㶣hndJZbܤTڏhINR"˅cw9ҀքaOJɕRK3s =H6S\kаaYЙ5a>QXl ϏD-Gqڢ\i eLPJ8k+=pC ly_.M[ *}|C !8P{T7*p?SZT-RP(dK5Yq"@[R 7/TRnp@ A”$-ɗv[7X 62|h@2=W{G> l68N>o6HDd ))nKiM/xJG7/>L5#? t<$Dۊ gwFO$M3x5,/Q4</hG!4h/. [_BvhZ] 566F^>\+4Aߦ .?}yse2qO܍X^fu^zswK|0[Gc~P4W.~lXN2JKH,:q~z dř7\BJXߑo*amvadH&GIJ#&!5==LVTx0 l6K:F)?s#!ZT** Qr;edHӤR)|1k znnN߸qCJm;|u80jVl'U@ڂRrMׁ9`p@ nlC𵄈@`C D |=$kϊ@zľd@M k.mwX4.ݸ]@ | RIENDB`yagf-0.9.3.2/src/images/installed.png0000664000175000017500000003164102263216100020051 0ustar parallelsparallelsPNG  IHDR>a pHYsHHFk> vpAg01bKGDC3,IDATxy]u]{;/=_%/I'i'Lƀè3$TTTuk9{9wR`@:~k^GTf+X"X"X"X"X"X"X"X"X"X"xxe b R @EQ*{n"31"R\;go/v9S:;>~<ɭd$Xhz%5ԁF!.>'JGgP^,7OĆS/;K\ 'y3ɰDwFf@! d@CEU*P"E=DJPr@ >,g^QBU|qĠKx[~[<-@AZz_)i6;|! OE,X"~+-.>U׶3V*aTuި;XශZ}VEu ;p.853-\Nt56-H GO4_IDz/]VLlt.u r|kp _}Sl $h1b Au`e_z-`6E>ڬ6trbGLf*O>=}TZ{]G**:,ܟ!/mdS1a)R|S?G6#vg)՗ /?3}Fċ3^/H4? n1Hl^Ғպ|&OZ<5:sݔgޱ%m2/^EuVQZDsIDV{7F:ۜhN᧓NFUz[g ىCZfuǚėF #S$NU<-"7^ 2WgMud=T)k-9r3aFgG*htAˑ~`@$Rf .x[ҊJUc "b9EESQ<k,#3#:r_JX22ubwhIRAKxkd6yhGQx;Fqpbt59gd)sȇ_Vψ҉҅Қ~IDgmM>N)C$jB7c,rpb<>JQm8}Y3t]I1=6 5Ջ=FdS{Nsy~'#KTb%P\\"͎s\bʥ GsQ܋jmx wD{ 8|w{ (o=tՆ P)%c]/pxGYO5$d"ib%3;JD7?_My0ZMD5~}@ك'I`hj'>Υ]ƚ |8#yI5,_g"&VZ#?T+ r^+āa;JJ,_b{M;ÍgXc׆ՠOGvӡ&[& ӣhXKWdQF%bsW*D I=VlVjocӘ3wM$PM#OmgavzJ5ݚGy MuKY~?3o $O-z)_?ǟƤ,u:6!Ri47obY283#Gf7Է髓E8}n7ۊ3nT}#紟;xw_]7q7V#MEI ~_k'DgqVt.'Qɇlmi}07y$PLOߣC Gj>w6JB{~Ok u%Z^`9y'$Dzɕ_IO/GA G>r èNqU`F Ci2nA.pQjsn<xg$ߔD4\ ;y&ߘWJmX<r|gRT{;=l6Da0\C{S5MҤm(AV!. ,/Q&sN_Ƙ^l۬N ^Dž.慉MzxPPH ~a/Fz߃Sϊ0{2?7xfȃm' s@Z +*6KF[FD@ZVNQ&FGSL , B( Ӌ&fn8:.[a^܅hԝ@X~aG1sui4hn~4be VM[3Y`{69fW[g^ǥ]8V1-̇67PxS; G4Fh_ŪZ =<<)W 5>.RPMVjdF ,D<xizuZ;XѵO6ON僪:^~J՗PB b$6{ [l 5apN|bȧsxH#Yb1bs8ފ`aWۃ)$܌Я@S+V|'#|I58w"(`+6z'6XM_t^wڹf"8uXEߑඟ0Ӱ(AEF耪R@?vft|nAb&^}̆ {U]D9*\3<'ʭ Y`1D"zL'h 6bzMFx"p|xU<_moqAS$L+gvsZB8W7}LO͑Jd?!ǝwuQB WMbdZ$w_f0#m~A =;-P,ϒiEܸI>0Uћ +9UI>?^>5bz69/{?z>1/<#FojH75Z*4]0s8V31?[Aǽw^Fu7k"@+dDU@l/"C_?y.^?⫎AB.UqI_©W":́b>5M n&DGIba'^|,uuϳb+=35| >(K?"o[Ar2]h?bx;?UPƀ7HZ?mĶZ=l 5V_㫎Be*w%Z!n:鋨E ItS"+@5]x1QƘMι"'W_ SeK 5{f_b d_ŲXٱ^|~*|?2fh}1Ɗ5bVlk`Muэ YE*T|bGtqL#-$I')RV9č;+w콇|F>)ejDSlB ͈ݺ<|BgklRw45{]#5c_AH#R#?տic+6%eS ۫F/}5?UdFg7SIf9DHv${4_'V94{_;7c_L@RU4Y1,,1b6K]C[9._v%/N?cO2/_ν_u+}fЦx7~:1b$Ʀ6ݚ 2tG vʊU$>|ߢP`.,s^}ID|SR4ΒUai7|k)I־x$X6bzNfE|g?®>_8'5~SV,᥹I9&؋~Jrm_"AJhsA<"?!8ZߌyhRtڦ[sA%ڃȦ|z 9Oq)cx$?׶:՗@BȡUr8į%d1>;DD_;/B+*="lr6# BŕԚrEXx^ þ^.Ɩ_[y5__q GJ1)6PbC7"@#" L5ks=^xɚ}uc *s\ \1FFDTH60dePNE<*wBHA~ S<cާͅQW&A3^cen3Џ@WjCWk ͗ҕh {ylS=}vo=JseR-`c?p<-`I@άv Oٯ~dUKU8ooAB_%p@˗/̜7AKCSWށ{hI2Nݍ sm6 6҂-$㾂PU\s||/oYbO@5{h=1=Yw)V@p~(O!痎 䌚kWoX#l{ϡ/AB ֵӥVp-5kˣZ,Ґ!~_A3x oNٔLSLy2nAzT4-ʕ+>ɾ^Fq(cT9Rk/(žn bj^]+=cf*`J\h`X~O/NiKI[0LLïE&G˾6,/qE{5hmZd:4ej'eS3e "Ĉ1bd,Պ6b{69}aE-W ILG^kM9PKXcqՒcve/W2l)MW:ٵvG~1'LD sE.'fh?& 3|`҇F*7RՐ3zϊ3W nZI 5k5VV嘉fR" y1!^`2bpۋ1ƈM[c[S&+":\?Vp{X*[zk/xgxO4YvC'٭A~@#8 |*FhܘxM99/C<\v)_<72}Bw+6,骾-FMc͓'5,q݉"6`>z|B#gY}XgGLZC8>cJcCekKM]NG?,ZVnҁE\9ᄶud{$8ߴ'8N{=Y7ƞ/'$0y l-Pówš_Z4©/5kwI,_~C6__emCW[-c[b6-[}k j>5Y[BKWbm|O8U*TWwɶgڙ,Lb)V,_K3L@6arYNhYӘ%=YD9N(sn=gnR6zY-UO F jt*`u<7"iDja2\`gر C̍Y+D[γ[mFC'7~ *ci6#L>uY6ia0ݍ5L0'%hm¼i`cDEbϜeC&zν%`HŎLMeM>̕бLQ*;_񢶟/Y|sL_*~xǃ)m;?fŇq/~BW8E+NL<Zq/L*K&W3;H%YZt_a_E[Py(k8#a~n稸J*A`j3Os(b:J[#Ih+#)!ۖcddfxrV;PaP_{CHW4TȇS46])}l߱mjbfƆk~NZp7x`;TtZj@#oz (7U˯bZ"TGE*\8 ^.۩iHs+|XF l@`: _H؉L`v[f@t|'{٦ŏAZR\lM-vһ8[>'kOYqJGC'Xw:Jg: )i+J!'e:B_5zlhy?݁/=Fav֞ȆU2T Rz,Q[l^܉w`8ȁX~_;o|Wf0^HZ$M5,>.t#>) jG h5Qo&k랯t9@XtJRvZtWU \zٚљ?k=5%}@iTÄY.龂 :/Ċ%j=O0, ?2VAyb>7&jYYA;ߔ cxfiab4 n}myT`E.#.rjICjUVPv.a3y.,Ra<=:Q$3%SSiAv8U+?X^kKP|őh`ڶI: =M3HR̸)"87uFK#1qn7PtK{l XX_Fj:]w42SEt"?00hA4iqԕ7Eb &&ӑپ|eM{}MXKHI>rG b%XP jT輔3ZHr?h&3 np,54 q;-p(f0܇Yt=Zq7׋-d{9o݁kk-J8hEn4rQ]UU*ZrHUbDM* &՞\:5ߺZ -VUSe}̺~$q浪ZdSŴ6ZMjir4y &7W6UjozN#¡phk߫~;oHՐN隋z9S?QEs.t޹wZ&e ,Νք5&e;R$Eȟ{G%h DҝႶX=1~E6Z֚.42+Z[WFAFX3o;V3YGo;{a|])]sat>\ Q>΅N}UyW"Xڜ TZNd3a;S։H_Hhy$)IhٜrY!԰hB|=B!Iʅuw"fcux=>Lᅴ#gȶetSZ 8EQEEW:ixMbg*W#uЅJT-Ł̀Og]{(QϷQ9FҤ6 <>Iaw4vjAM{˘'oYx}sYrcG0!,'_rk%{_x$0M?S%RTJT,V>v.m2_n4PႮME\k- ˴wy L&Ue1 dW?#ԡ8cRL,|Fe#:԰z{eƢF=Mc҆a\Kvw:0$H-;ng&o(W_c*Q60Uo>z'g9|0 j_z`26ä?“?ܛl0$?/XCӘIҖe\G zzX ;_o3:֓3^5>~E 4K"ВUS7hH~g'}`|sLb}yf`67܋  ~^4 ɗF8A`<;g7Ӌn}.F#ڕ׳u`ɇPJ Ӡ^PAM=&U&Y~.Xq֒)R6)Fx1zvM3ƈ/Z.dzem]-zmg[C{7Z'AH\XPtL񃑏513e4Tnη80gGmDvE(ѢXzB~U<=|IZzmoxC" 2*"`NDޕav焉%EH-{ Nw1(gUW558cv16Mj`rX1K5Bol- ki3ښiyة /koFGe+ޥUHeSҺ*px wDe; \ɞKN kR>ìoVHA88x׼^fgܺ. S6 ߖ \-ӲVDdwP1;1RWEΤh[av:;;s-^tJ/EŲsXuαҬUQ8 ? [;g~07Ww15>|{ ;g[grX2 7+ P F.2lڴc5Lʞ6כrE7i"qT9 *9@ ,I۹VO7}ȝƦ0bwexG:[+ʰX3HPr5"x(ryJWJT&[w9{-^t*Kwր{f Ef0IyS+y2fJ%ۙ-`0 7nʶoFs/ԕy;w1|&c:5剨#*;n#vg\|2c-FZu+Q.)GnebduY^/­َm2!o, <>rN2+ݧ2gws\YP-ܸo9Iuty{9|F m|֝R"2({){REDΑwz;nҘvznnk8uz ]4AM5EIE7SG1K/֝RJ9(E= Vbd[ph=fVwqkJ#uۿy7k8w 3t ey_L׏デ:W՗_7\87`D 2mo% JAj"l&ؕzt#ז*.Tn^ˆ30 &4@O[|I.N=&wk?teޭ3̰sPtxI I*)(rdZ~ͅ}2kJ>vXcxF9xs{Ye}Pf2,KG˷DӃAq2HADJTE= [MfQ|5É\\Str001f qDX%"FXz}Uvi75h4>KMuai-'_?8Y)1pə`zl/ЛEOD٣ΡWF +׮_W]b fEŔ^֬95C ۰pipEx (J']&Ǿ+NXZ/l]Ye CfT)|A7࿝"ͫa|[3.Y+2p\ܡ% GN 4:cp!$N^߸'fm3d1)ABgǮA p(ߚq'ޝLQ('CTf 衏B4>' XO዗2'mhs'dD映 (0F@k[s.;l{fJgWne ,^(D{AY'7_k[WeV?\_(ZK^jLPKQm-fUԽ/~vni_FWC~CVRw_$eR_{ʥڗ:̐` 2{jIݮo\mZ,Ɓj5n{Ϟ}_?] $[yƏ[+^Ċa2[uzLe($p)j=/;,,/=/+?sGQKw?e^25mzܖͤ€`( 5I`ScpLj5]o82kSUuX{7`)" L`Lk)#9\): :hK.&Q$1fNɉkqJ"u/4޵ *"c*HcXoDLؔu{5%K$"c >)DRk;4SHjPDTbO|)Y=Ky\̏ku",}KXKXKXKXKXKXKXKXKXKX6}IENDB`yagf-0.9.3.2/src/images/langs.png0000664000175000017500000000531302263216100017173 0ustar parallelsparallelsPNG  IHDR00WbKGD pHYs  tIME4098 tEXtCommentCreated with GIMPW 3IDATh͚i\U3L2BHX$.(F@d)pAJDT(eVQB)Q( %I!0{w{zL2#p9.ܘڐԵhT;qlS (;w.9 q}0c``RD.[\TȞa"/Y=؃! hP:{lVXA\>Ѿ>p@yܔx,0-\.aCӥFf2U=Zmz"2HBU^EHY]u3Yccқ ^Ρf$Mb"}2`9TMmD DnR֨ `OoMqZ,3 x%k;~8֥Bƌ D$fVWFjQ 8kXդVൄ* d9Tu\&}{D1ckh6d VBO\x 89DUE;ϝ,(ȦN%ʷ-S k!tAל3uqm\raOg#Aj]m!~%? mmY0S0(KP%*`w/f@J>;'(gI|Hpg5z-$SQYҒYJNf.\Haa:2M7*-[ew^eE=Q/aHݜ}akfg5wc`kB>C[ =;־2gBH|1ooe&Rّe\pA,zWldiv8F3j3ٻk.: N9~ mm!$Lv@APr!N) g1qIf5ͦOȈa^d^iy=_;sO؏[i0}#:4:#r C*]98~ 瞸?lz k=$7gNi8aQ}qml8_Cuެ?8#|iY/$ma@TexS r82|GrdwʱsY\`-b=P{y##ό[o=+bbH+= #|+dUG|CfW?6T̚Ձ")A]JĂ3efi\i O%F`Ca[sg2f#9g: pɆy@|N*T:ѯdUʪq`)03D5O #;#Ym]Rfcn}1 N,yȤ'(j2H@L`wa {N;w݃QRˎƿmo!TUm=ph).&l o(A1JFcUg,Uہĵx g$I3rM}߀:e*Bwv><>,qZ߇&&ߚ*Q$nIENDB`yagf-0.9.3.2/src/images/notinst.png0000664000175000017500000005537702263216100017604 0ustar parallelsparallelsPNG  IHDR>a pHYsHHFk> vpAg01bKGDCZ\IDAT Y}wVhF+$$A!`lCpQ$.;STq`[S;P)c;I*d "hi4z}t{yrg($!@'Ϋ^W)zES^My+W4Uhʫ^єW)zES^My+W4Uhʫ^єW)zES^My+ZyY(L3\=2h2ܲw0p,++u:u g =HӖ|72(@ʌF\|p;<ņ3=V=LO\;[5G++o#yvTl>i Ri|9aҹuPYv <09} .Hv>ivX_gx؇4 n+@ u0!JCKJ×SA"/bWo֝N4. p'0>8`>. C]Pͭ+gO7mf_/sjw̝9{G7-ysGpOQj߾m_EF;6w:ˬnm2\`>kU;y3NN'K_~=SWW@*y3%6O_>} ϬNL=@ w3ʣld<'t@P/_+ q Z]ș'e}mI:FOuboݼ}2=?YWu;z}+X䫑@09npZ7mS\^4C:/~|!Njݩ#]ht./W.ɵCY\Tcnzݧ#^m^>Abp{R9~ ?4 *"BXg/w|x llFFC't 5'S0G1툫9Q;ίzҳrd}TZy'N=eTD k+dINw>}YVLOS#P̈p51G0 h׿j> BBʅPp1p bqqpDAͱov;uEYY~S?v0>s^8{fիU<ﻨyvXNq-ΗeVf荒3~WU`K|*5 {Έ^ j1q0)PwL1&'pi/_)7lJgohԙlm^YDd:m`%cV(L`"X!-Qm#cIUҾ7W(3^سo8 *_vep>9i]Tu!P Z V!bxp$bCvDpL ܝvb${G== 6r{ot.)eL1sDL< RʼnuJ䔂h &ԞLcIc!|O=٪yv:r[IV:dp/1sA̱f8v8#p9诟?R˭{GM8"I1͊5cRYq ! cERpMBn0 },6aϭ/67Č繺Mbfe> ~IF^@@!YC5ubJ``bo~OmE2}@嵿I>ZR9:L:@F(d%#^b@!B(jա)X w@'g h]p gene%zԬLB RJ9SZy2ys߯r8}rG6쎪ŕ7.GHO<4C9stqRu/dD$RlaxA q5";+%A}Kv<9.󠑵xLJGakݫWOO Ouw e`\pt*.^:SgVz=sO][:sĿ h˴egD*@2* (@PϠPhGĐ䐝w0$܀t-à˽`#))krc1tV=w9>u{?'>?sr+L>+S10|0='ƹlZ=-orʡo\9` e%EM<'_6?qC"$E4H@FHd1x(8 ƂeC R20@Q@oĩGPⳃ$ם#Ѻݺ+!ּZ>LaS%v <|:w˙|jc֮?wE/ߨo9ڮSܝ-]uMѬ۟W'N2Etag7zR2|sq} w=LakqdH@½`d n  QB7ܝ2u,8R9!8n㭓g`ّJq&{`-H,,)UmMū< &?'0;D"es+9}1?ݽv挞}͝;P]x`PExZ䶲1%eө6ͧ).4vmƩ?@Ӿ <Ϝ/Ei^{{ߗԺ^g7tdNBH( H2-)^ ^@2JՐp/@A(s74:rDo od'btܠ911At˚{9}ITQQ hq7;iw0^?5ciV%xɧ0QKB lNw;_MKKdpnv&'N| {iwa&*80κGWIþ#9w;V?A)1y)3a0-gt]?؈!QJd@PL g%9fW5̱Xr!:crvƑdH-Hv\9lHG主O+2<0{i_u4[hnNB̥G=ã&IۭfNFDu6>\.Mkض1w\ݺBq/-n'} 1K NyY$@O8O~2ˏ+qN)'-BH@2 fJ)Ҥ7FP0TCq qw<R; 8;V ͡L3 GG`ܝ MB:;F rx G\b1 !zΎov6|ҎR1H4˲W_JlO=.Ȃݎeܜtb4X^IٕɨfYZ&u@K;17Z/&c!h)ԝc~SoOݝu{ZC׫ԑ ꀑp<Yq+@qRˎX\@ 豰М6M.w ;AQ5C0<7ŅnX\:7yѴ{~ZbM%]:Cz0FGCkLBL p|b+,;a:f%A*ȇy"/U8į ~4EI5|HoAВHLn2q)SԚo3oc9ޯO¿XXʡ-F7w(IF) 2X눃 :8P HI;D `]A) x̹xTD5]̰Wmw\;Ѹs0vww6vz3[S'׫ISMLJsh˝ܪ9q^ ^ %&2Jme S ~!{pqĥ'F Ph`m)@RmZ_zOeET(HqV&sfIS]dFFx!YT @2(3^bVXp$5`.f# F!-dDAWťWzo0+oڿBh {Sѱ_o1\<ʇGq%4'AD͐ j^Notu~|N)FD (@T!7\ LLq\dU -."J'w!%Gyn+H:RĊh?Xꗮ qb .ŕnRW=yKyڏO۸0_goT23f$4H^{/{N6WE-?M;}~͍R/M]{{O?>k{NA<:!'I ð0\prcrjsu 9cThh\x  1\B)BɅ(y !`xX"k (`X|/lt𔪈@&ǭ?ą|˹W+6a$$@eˬ)KՅBrvF15\ Q jI9Uo,/7M>k<'S-_s_g0~):}>R}-8^# A֭SeSUk!PŊZUUTA"Q*(Rр Jn3n-/%[Aqsu $ 4xVY_oRzjGP@>MT|䱒J+y䱒J+ydw;O۷ bLo*c =Sw-\yQ.:֯gs ֯/n|=zJ.f˷~sO||˟= mXҷw7&昀+PPL BŋaDp[T\( xN@(. r,3q rcqIzC:٢ =sl޽1Z;|)/Ax;?&`]& @&/0;8QO5ǽ1[*dDTB>sgk[W#d5PwyI鍖£>8Nr= xj|]s׶8k4繃`wp/ 8 &hp@7@Q@C,8Q4 "BH<  (KM[P,A`۲t|}`Xցu`Xցu`v~L8A݅˻)-L4*@-=LG˟v ~T"HUBɠ Ko^‰_ܲr}f7kUwWBh}ޣ|6=hmNzj 7yOM6w,.Q] x0<㘁b8jH4FD wG! "`*(E *֎*Q /~x颏-hQ1W*UVڵg.O@܀q 7^ $@@۹?w'-ndA6ꅬXS&MfrqiIWqw(g.D>^Wᄽ1H|bp0;Ͱ4w=w~]_[K[ﺏ[ުVž=\2y{wҺw{U6#^O8c ο& D,IA1ʨڌI!#𯸆,VJ0%-K7=8^$˫s7c:^"vCVH?lY=L3Kxw+e\D:^ݹ뙰j1܌ G]YW<_UrYtڎ^9=9b|:Ʊ =FNAaD|_ݯm9?rSl_kcOߏ'Nǣ[t8tcgVY1s0<3GU ;P N 8!2AlV]jb$B1DqAđ'dc<^_FEX*깿EE~7 =<}.%pD ]!åTXn3np uPJ PD1 hBdzQh"Ln)w(Lǂpppj#o{}>۾qivt[6nMM+GƵU&M64ZRKIAG!R:5ĈRCk F d81k_'ŧFsq5˥@D9Z!.AŃ8C=`ܤ,xyx>a :-n^ǜP;Ak8U)nFRxdZ Bhx"cg =h.48>ska q\A[ S~֖>w;k_{K#۾tMz~Z^jW.NN0'wPwrnsXp|7}ls׹_04TCk5,X`%*h SD+D(%F)澱BrsUA SתV~QDx1[XYi[^wcٹ%>"S㻡`4$0xɕ nc"<ώfy+:H!g4=ގ d 8` WGqL Qrogo>=uGԽ篜N8TOiG4IMb@PN-$p!0&!9e7N\e`A p'91,a;. k |l|Lߡ߿.BhVHM㴉Rw ֗] O.PD ʋ jK_keuŶ:sTB]:QY۸Ǔ)CGLEW\CsX9P&h\2Mbm&Ibrt >>7F^0&5&5u:UVNѝM:7^1J(gSF2V瑵řɍ+crU-MsWW!)ngN'yAq(( -fh fv/ag>=Qwt41qJ ,j2ʠ:{(Ϊ~ATh1 1@)"NxApء#nGB"`n@P"4)Mj<f1Ⅶqrie'5-F ꘁ 8B.3UK]E;kuo~Q` k.RUmc4 G74&SRAH.O>fUXh*g m^DyMa~ 17VB.@tjw fzr k8 +֙~xhM_֤:u:uAsEbt,5iKOj QJ@ 'xFm635TUW}]_ު,r4|*ϼsX_;c3x=4`nxy`/ܠw cP8X9Zř._8X 6'g1)M3]³K/p)UENB&lA6lI#=aj ,ܱ;pﴄ$wR3Iތ`LZ-vӞyI"R[;?qtspp=|3jxD\rsa0'ff8:{\~$0 qDdI|+IbUD@l+̸z JljYpLE@{441OcCWi볋sici9(֒[M .A UQN"yɃw2juR丹ˋӶ8?K]mLM]uG?~6ChQހ˒,4D6ʆ <xDe-G9$0E n^Vs Φ,Zd╙Lg6ŀm,ՁeA tlLƖd&'I.'گΗ>7g P sn,qTӑ:`c3 X-cS ^'5.(RT\I|}wӱ=y2vJ0T9|閟=^}xFϩg xzp }>~>X@jFx{T2VlL 8Ly|B!u*2F ̢&Omc :6I `5`]J6-HM ,{!<9Jdn34'G~4joGd\y f]Lꉧp6LZ96pI"IT:Pbnrb9L6cޥ(/r໧cyy1T_o\_|?/}6xˉXN,ΨO-xwPNlw0p  =j)$lKl'ʻf Zb5C< ((\ZqʘE s؉j@v\v&W{~8:BPYĭ 3 pZ%(rNQPT_C6қH` GG6VVK߼}ēw/ʵ-s_xO}႟/pwF'(Y!qކo7=(z'+(łK`j;X  af Q@Dc0b0i@hX!8 iI\3&(ty.IM[m7M͵D!$3D?tdlcXrݕ2nGcG[†m\ҤH%㓛J!|/k{HM~ ܗW~.ns!߃{pw#OX>xzNн;p~܃amі˧xPTbw$cԡ@;{v<ls-й62pM\HLd`CST.fby2=hl}tiqk6dmlAAӹ JC3بS$ƒ)Pnwu{$lQءUNYFEd B~tqq2{s(b/>O7?r }xwp{nd'{g'g}Mxoaò2!e2;Չ()ę z2#i #H2!^< BQ؍P򌔬|}Qpclۀ 5:6׌mqLN YX*@khmTNmIwEdG : I=y.meOLmĭH\Kӝ">SWf`=叽=lhù?oK+h Wpt͆ [2rT7.gLhɀJQ % Ԡ\P d0Tl ŵҙne=zﴞtϲ& wt6ʄc-n3m;jCr-[j7_yޑn E7.\ (δ2to _?1\k#}S޶o>'^._JhTfɮ$$\N+4L-i0'NyF$JAkgluD0lD$ܢFc3)imLLd1LlA:BD& lcRPﳲ zc'I01 D.awN~*߇1%S+N^, E2_޿bo3oI #,^}DgOwٶD DuAd3ϯ.K ˗[Ϸp[_Qr3˱͋> Iڦg*n̹%UTa*w:MZt vbs6 S0-zOVh$ O:(LccZH:m6鉴 yۣ/t6m6kBBZ (T ! p;;9Vz2 vb4P@J3B ! I1.u[iBiJZ䲟ꚦ+oKF.& `HΞ}UW~cj&ރk"Na'ĀNZY~ k{>|kߗUY,c4bbpZ }ΰX?:};קIY.Bn 8DCbw jR#*HN6`E݅'xaГS0/3ЀSU*sMFk0e_};~>lZZkD:ԥbQCY R\lw vvfcGmjc\ tB4 %0uH@ûgڃDD9Z)6BnLa3iLfuŵ6hևħ^_ Ӵd3} 2HOg|+؃>R?0?rMj%E.J[nx:.˛o|鞎ZC|xD CA-yJ2$餖`.*d\mƉ&nޑ5K4 ,hX J$ke-XCj+n6,6SHm&¸+ 37~=7xrviH466-xpyv&d$ )x|i#sɡy@+|vgp\ʵ߳ kiSCSzfQ.kft#^y9>1qB٭Zbli5!l:$r0YJ`&9{3;$QK,U=Hn|qqljŚvP̶UԨSJfxS^ AR`7398\q/֏ksz2 3 Ϥ$״` ,^lDYtb::|+`\ɵ5{Ctu }he '0.yv~N "̶bIoM #Lfk96(!(6:n&i2Ѡ0x N#l*wo=X W*$y2{#c[+~3P0s&!z!8dzU)9 CG<Yh^9y`讜k%>b 4 +hp{O,,}5,ߎUUF4mfif;wT̽4rlA r $QUD怫QvrnLԾA{[eIPyһ6K $?98 ̴-w{>oz&x߼s]glQ'wNO/` @:ЁεʵS~`j{|Zs%jT*[Fo{{|xo.zYԵbl yVOUes!:FMt&@iaA3!AQq$COT*X$ Tۉ7p&31q9;x)O" ~/ݙˬwCOxŊDE%1& $L/;o+@@$0#f`\6K~`unl.vBX fy4m|ѝZ}˻ߟ^\ YK )(B]CT@aDt~!E"%D`hn ;i8L3/7/㗙g1lc'+ַUY-YhKv/?byJp.%ߨEw*31ga@S ̭>2y=I`6;WARx3)=TԔZV mgJA.AxQ?y0Oq~ŗ[o8ZE)91w.L5@D(HN3 d5J7Th`SX$~Wpq#K} o=Ml{erc}~yQ\]5E|IyUDU*krjc"vMj>o[o ,3p ΀3`ʵ]9Eʔ.Yd He绻㮏}x^7_.98txo՗E(N^:8P80@&* ) %y賡؀y. Ifs~ 1vBx睧|+xKNNۆS\{_Ó *qPU,E؏JgFzR^a~,w  =` *;~&w[VTr`ռ,Ǐsk ,z%<0r<\~׿S½_Nj{wkF6Av6ItVdILvNe vJj8ی5ISͰERj*>ɛ~_|˯XO{BrvQ❭ey}aRqh*"2NU)kO=]   µʵ+жMrC+4d9̹vB#sgA }g(Ad!]0/_>ʪ,tkOX}՛P8D&iB(t˅LsKfIoIKCئmgvO991~|+?{wd}'+ ˃{aL~9ld2Ei vna0  QZsצWխ'}oKz5Jj՚>VI:`.(@*o>\ʡ/_o ̙ѻ1:7Ln{W,eaYd\X-W+'wxZ^?_oWq{fC4Y|ePRD!bDH͇zzz3`~'QEne>aNbGsкFu<%i!2СO@4s`X_Mo_PP֝}ާ>ukqaLS֘>tv<>m8Z Z uvfMrĢJގ9_~ 䲻,<,ӕvq3εaػ4;IJwT M\}mS`f@03+87GSt铲7OBmDtLI$ 'Hу?ٯB,w_}qNGG=Ɇ=FͺWWWK|G9pޞ>ݴ /]~KM6OEU]p1rݧiYan9-I9$܌ήp]ͧt~@# 4`v#0Y--zO'z7R .2ـE#mMp`" J@6af@{;} m^W{qo;xFM , x FȐiTsG5T#j6w[_ pqrx>s>^ܽ9ԣ O-^.]k}Ω/$FH@  IF 1 $bHH @1 n!('`Ď8q{WUS/kϮ: ީvgz e16Nur47vv5.(Y2PZ6w><6,T[PA i}v+n q+찠ҷ_2o/~OO~*}%3txBOAUg sS2E w֟?'0-sHrm_,/b !q-f,'6Wib}tz>d3WGU1Ly>0j C\⩞.-DR܊w8*[_ۗ^ͥ /CM_p225#!o<֗8FlksmܬU,W򵟹px{ {qkV-j{|ˏjqVCNOjX e?h=0.2RKtԡK(źTBUGxBʭBtO>coW9Ԁ‚%[D ~]528hSCiZ62ao|k\E0c78C6.łNk[^?7lɒqa^fQjAsc J"MzWrTR%JP"# ;tGԀO[i_ 1Q' :N?YU2ZoPMTԲIoj̾Não~/+[{^x[LXktSRbMƿpr×ףqܮMftDF~jĭ f|IKoXdKUeHhZJwÛ_C]aDб=qxom<%X&$~?}Ozһ,*Qy*E#J-Xw jJ U$Tы]Ahzg_l~k[3 CHQSFJTT35hGdմRjxk^_^ xx qk1aی&/lLpN>N[j?밨hPB-d$)zuOT hJO1bvzO E !AYlzmoΌ?"XE iޛֽDIAaly\_?1cƌv%;,XFUAU._:Lptk,K_h:z-RQPT),F+%Tsnbqe~ekȩ#Vrf UP%"ckjzc5<U{}0]| 3&pk58`A1 5:XqڲL )BDhQKSsj(HwZ*EB*Oڮk'C+v֚z'=용-iX֫yźBlJZhpm/W~L8W9.vcB?}aݛoh8ggxr8tCJDW-TokBkUJ*JPCU 1*ZHX!e5MScӗFUAT~HNѤ%CKj4>{;[od5 vWcq1awat /U41,~~9\ɽfdi^zT+HT뢉R)Pb)zJKi+8w)F2F@x_wG LQ/9,][j6-cFN6kI啯7![\%[p@aA4:ZYndmoWooӫν?ӆ6NT}jZJR!)=(JRT4IJUIVZcɪ,xqT*f ovid6P-MUTIjG9j`l۬ǯn_?²}nб[\ %q-8`(U%6왿9w4M0U+RJ)G)BJZPV< ;É U6fsq\ac9kϷe_} /^뿈װ-q-`A)U%}oΞ˲<|Wu2t'JQy*jn i!t$w&< x;o>w7gw;w^|8`7 n5v tRUknu>frȕe8ee"2j%)BRArJ@GͻB`}'2魃~(<A4p}o|}|uxKy}va`-vc,(t$]!(t|Dڏ<2||ЏEtFHyR(GՔ(mlNdz*R!ђqur]o~iz˘:`vc&L8`Acmw*ɏ6w>]mDuڗENtR %)OTDP()$(TlNթNKahWq%ٟ̻5,hpXp„BG6JUITCZzɬ7W':^jYJ-K)$* բ -6Vq%2otyvXcƄ3,ѱQhRU0cqkk8~r9Ti?yQug'j⨑p?.wyekxVnut,X`AQnu*I|inV; _ߧC2<+6S5cRuRo ;,XaBGZHUI{X~ntP\o> "zTXtSoftwarexsLOJUMLO JML/Ԯ MIENDB`yagf-0.9.3.2/src/images/rccw.png0000664000175000017500000000665302263216100017035 0ustar parallelsparallelsPNG  IHDR^c6 pHYs4: MiCCPPhotoshop ICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/%ҟ3gAMA|Q cHRMz%u0`:o_FIDATx;hSaGOҔPEV"E[ p,E N./A :(*((7Eqȵyw5 ͑^/P:3vs\JЗ@ˮ4^tq jѩdtu 0qeTH`R ^z)!B PЇB } Iae[v{苀žsٲK9AW$pU#޺7@).{6[q@^nn[d;VZ>*{Y=⍭.zGaޚ_T] R"}jG ^V(zg@Nf;d3 T~FJ'-i1>5Q/xwWwhQxxxkGxGk5] ^hkkGk5]k3]k3Q?};O'{U_:AE`84;>v~6Oc>􉳹tE~kUe / *w11$>׀~ X!dܘ9ǣ4=CZ#)NIENDB`yagf-0.9.3.2/src/images/larger.png0000664000175000017500000000611702263216100017346 0ustar parallelsparallelsPNG  IHDR "> MiCCPPhotoshop ICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/%ҟ3bKGD pHYs4:tIME / HIDATXWNA9Ĩ@ I@ʋ4VTVeSU|ﻙLsT% H,G( $#I!,N `$$o,S6K↬oit2!袰 4im{\+nx}O$|JcA 2 `^c#(;hvjf߂M 'WSf\ &5 n=eeXNkQˆVb y#}`m$2;Kx@Ʊ):],Qm81^ߗvŶd!TC37Mk{ܹ) ;CSI;_ ,iK_yoIENDB`yagf-0.9.3.2/src/images/rcw.png0000664000175000017500000000600402263216100016660 0ustar parallelsparallelsPNG  IHDR "> pHYs4: MiCCPPhotoshop ICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/%ҟ3gAMA|Q cHRMz%u0`:o_F!IDATx֡JQߌ[4 (dg0Xf/h۠]EdE ,.Ü?߽n3l43Kpw>G >M)3oXNK$vRLp~y{EY\K=P( ׆T^fvz},v|BB19NS0+ډ, %@zhD|&nD8ۀN0%Xs)Ǫ!kw8Bca~DIENDB`yagf-0.9.3.2/src/images/recblocks.png0000664000175000017500000000266202263216100020042 0ustar parallelsparallelsPNG  IHDR*ԠsRGB pHYs  tIME /BgiTXtCommentCreated with GIMPd.eIDATHmKl\988SۃIEU!)YQJ)e RXfS)^h,Ґ.BD\SZc{I3qؾ{9,N2"|ҧ{wJ x}.C6> 䁷9+$ow)N(RБJp?gfvrt`|k][HobvJyJߒlke^'u- T*3BGɌ`d(CiED ces%7;K~~GOb` q(>ADn4^O5pG|9P? ,J ZX%5Mvx P) 83=#bUTXI@BkAcG Wtw?39&tBDPcn[Mwwbq`g]Vٴ1u ?5ІuCG-L6/y\""L]bCTҧŧ%kRK-5Z啀XvmM57π$;R)t$=WkGh֍hXnH,-БJl+`_OO/R-Zkiכ5RH} 2==prd8KyVF#DAt=_+6'(32p`擶r3Jxj{:.\՝ghkmap0[M&ؕIT1?(7"(7Ngl/?$СCdd+d+mC)0NI_۠ݒ\~S3fbvrquJ kb<}{+/%^x9>&:yɧO=T* ZvXk  VWWH&oyk?rSgzAw_zCRTp}fu1DQ$QEѰ1ZdMaa+ιRz> ;vlgvw^RTrMcjQZFa(rabq AsD"Zk^y%v|~{9>di}})>dP9Zki*U5`% ƘF48I),..x?QVy"2闥KIENDB`yagf-0.9.3.2/src/images/earth.png0000664000175000017500000127130202263216100017176 0ustar parallelsparallelsPNG  IHDR  phbKGD pHYs  tIME n iTXtCommentCreated with GIMPd.e IDATxM-v4jsν?I!!6&M1!ACdX 94h Hh$4PѠd@BB`F2{skU7蝹=~SSjתUk}cjժU7~Eϧ?.. [;6!"dCzGx/g#{3K#DA* p& P· !AT :UUlS~hFaf }mFdF04ķd~ rB` CMbṄXYLG~$֓2[VZߘԪU72`M'dsծݓ43eu Wʼsy|>׫֪U7oZj/ʟ73C_÷= |t'= ݀p~t=©g8u3tџ"BMDH!#%`>t(I3?dtl!+&D@CF3:21 `Yg7 €Ȑ‚`vh,Jg@S#s107M59)rP?߈<  ȿתUV jժ_?NOv }miH7PA4AA!ǥB˸T0"! g~(~%玡!U-LWGQƭFAWQAe(o̠c0(! P #V 4>#EG1Y 0 E50"ǀ!NJ9aE|MY\l@6J&|y9 ,)-*?NA9Bɂ*3wC?ZjURVZ_Y)#T| (m96"m "%Ejj(Mbh"CJ )2DE!&2* @Yl }0d:$i9:`fzExAm@h͇ L`"3#D5pdG8ak5q-H.̅@ԁ(rP FT1gACъ"+!dR@AQ#0̅5D@-@2 &ĠG5?Zj}+֏VZ ,(o}h"}H! 7BAiQ!$Ġ! E *Q A$@sf"0d!3 s^gO(#c׋xb#A! t@*djHtJ?*@P2ha?.c[.B/Xf,]Jco=`DDze]!=pY,_ (@:8@~-X9lL}SGZjURVZfp!;qx) ETEE,#PQQ A8AA H|K9V( ## G08CrHh2vE)oY1cXUl!Q!bBp dG"G@T21JW dd;D毋@5LYP?Dz 4B5j`l=q /(,cFw.-p1(zN BjժUHZj}q.COZ·#}ǏyAOHDŽ$42"R HQ :" "2 ARe,G  # #B !;Ñ f:dQsǯ|'80A jDP@aJȕ*$BG\q0d&MedQf X=!A "F wWRV/]Zj.9-YGlͷؼi(}@FiڀMhF4!0&E615i"SH)ЁJD # E30@K(cU@4H.,cY? T9@,,嘲A&RX8wBjd?F-Jr*~ѧJ&0|0cT=3֛S*::>];HJ裆>O8}}bbZ*UVoV&h߱2L=2DII F [4A@EPz#V D&F$1Tb` -aP`͠f,GF 3uȐ~ Ya1| 4g6|(Qtq9W Td4=Y*'Q?2XMȄ 2%y qY ,Okz؏=1z8tuVDNUzl 0 `6`>E%AAck(9<X$~ʬԪUZjZ+OA_ O,/gn0F!)"P2$&@H9$!Q9$D irb >DH D) ̏X˱`Q- Gt|c`1:c(cZX IP`t֣74t鲯^6.EE1Y@)ojii(b9]ν57Ef2&lÅ}I!Z%R>fAIܝ~w" !09@d~DZ:L:tfJљhK 2i~ j$ ZjURVZZ_b8@O >.[0sFTU m@8$cb84>6džMdž0ʈTl HF 9@hH4IƲMDL((*bTA1_|;8QUb(k%j2n/y"%Œ, H8,K1:H:wǮz'HrYaZIua^̑ !]DIL 1@R JR4!R#Ƒ.82gp!bwur@BBs#})}lO:UVoɪ"Zj}Kֿ @ 2>c)*PhbdMXE5#[##6:C.<4Ԓb2HJ6m9\ƴ1nMK2yk:KpRRO2$Vdz처"ƺFsmY2jO-`aA@tel GZ33s!ggi;3ﻫ,HZ媎`ժU[c=3SnO]c6H8@pPQph"MࡉhCph#Cöm}iZܤɝ*(F(W@w*nS}i)~Rk e[nG3(~O\shArѫt]Y@_ y{b&7*a٘G8'k ٌG].ϵ(`[V#`lӕ-/>.x.âDNdDGݼJzJ ?LYY A LD>xD @Nzh{}\ԪUZj3M66fhmۡpG˼+("81$bࡍǨ/ix̠!ų.c%2qo68 <ߗFW=bbt=)KёC | )t'/^H>;`Y 33(DEG~2"jժVZW'un$ː]S:wҨMb<$&mP*hAF<<8 8@xC8!gER`ئȦqy#wr^@d$k0XS: ѥ02q, XV^m[l012Pbtͯ#I@uѫ蕖9iJW>/bHQ\[+pD\ur@LH'!~y0[[ !4J^F֣9ҙNtp]ƲsQ@&iO'HlA(TJ0i?Z%jj@jժuw˿A %dH 1&zL^M6m hS58$A**0B`mPʺ ^\QQߗTas<ߞO25 *Vt۰|mp,KZW6kp7s?2ݕdg*g\[Q2=!E"E CK,PE_l(kg `Uc],|p,Ͻz- /e*k Zܷs2"e"nD.V˵0 8dɉ%9ǯΜǰxF:5*L6"AJhn4/=5;ԪUV j_ݞd8)+ ( 4$DC1CfA hCPb41M (>=\40Ee8;[1  AHG Ԝ/2$0;?VY}. ke :<8JCV#L25VF(P^?6tnă1H(Gƭ+2e{_+vY8` ;ou9R5eW5heaaS˼w>+j+c#'66:_q9#L|@ W,Jl < ܁FW-qq:P:DAӤ:UV @jժQ$O3!5CkD *Qy0A 8sqw+ HMdJ)26IMdh̍q*()skA ɺ' C03 |^>8s\EzcYE/tӉ)&Bg' (D g<ILx*rnB~G~֪UZj}g@C ϰ]DAs9!Y3F4h}tG pN{{Fܙ~0pOTxc8Deܱ*+4Hh4q):cĦ4ٵ2^CpꀗNpS;W:4ı%Z!IHdz&')lg5%t#Ha+h9pdQ\>(AѰ^{Wp~ǥ|\uҖfג|%z=}}tݲM,DL9⠅Ja;s>p bx$'<|p_^xCd,6 RbNqt}ǮlV/pZC'OϯA^=@00Ș!< 1(TqpwnNGwgy(ug+10"D`4B6S#Wp\/|[@xS;IH @dea88~LA~뢱T@G x_w݆%R&B1m-o1PD!,b:l\lnd k>^6ۗVK02pڮ}3ۅwѮm+{d)Yb X5=/,A9jQ5h֠B @jV,,6|ԪUHZo_Mh!@KZ4"h {U Dk3AyEx!mA:T_RT4bUFHHGXܩ8+ѥQ/i3rV` !EC'*YOƐ5;2kn*EY ァgrn+Nw7zGf%g<$ے!6#T׺Gn76#GZ}]qٱ # G#`c@|G# [6h&RDJ)%<"MAGa{<};¨( Ya^{*\h?^<6@e/c7 qiKgg|xo-&to $,cF7]j '3ʨ','S/[ IDAT>IF> a09_~oOV&V/hUzZ_p臉S}<ċl8 َI=P P-qH|A@Rܜ$&ihb;dv1ǣnGq>|>gܗ--Wh-`(ï9$ץH V}T):%i{dI0@"}B& ҃Y"/EO!)1w)A;,r~r^rfkUe@j;:K6w|1@-#_q x{Uţ Eģ91$xPwb8A)(O1)Vm"Uʅ1Q'[[ڜ< @s4 s—/O/§V G * )P9;LQ,MJ ν~(fQx2)e}˔uj^dIfw+ܴ~t\U}0q;:0kpa w+ ^n/;kWؚkl%p]:8 LfwYNh$@rvCac6{92 7@lc"(?\*}~_U^V jfW" \>*o=܈hD.@pp/;Ep*2<|2apǫ 8e+@ǦHmjcNG02́PlqڟtK]3xNxV'g;ε3 E..~E1+Tړ"xqrQ/G\@L  -ۋ1--Jsq4oӾwAS' ᨍ/P`X:`]~͝j̑k,kÝHu\|2[T6#H_'Z3;;f`fC9sgd b\b>zf 0hd@ `ulV @jժ-9!̳H $ (lhhY$|q@AN0x%,1 Uتܫd5(SbX7ajgwbs<:@|S/'aa%#_:;&_XޫI.tG̶)ڣGy ;bG4gvpu]/+]-yk$k^&_(2;,u}|l-W@"ok_\sMPprRAT9QD^ ^H{6s6{6g3'Z΀Ep ;_sVURV jV(3(­g8˝ ܑfb.&o-ҳa )F6x0_f9rUbXH6UܭR-ue xqwG|J."\d>j(0kAP$ͬM5 $C Q2Y"B_g+(.c=Zm nV[q%"z;zjgWֻ99H~|: ^ ! mr+^ѫ'އRTDf88H;qvcœuY/ܵƅ `ժUHZ!#-VF;Gz#Xf7}&h<fC2c4"QQ1( cPjm ena4fttB`G}_^<q*[Rm2go&4>$ cnΧ\oٶv3ؐKC.t\` 7/li mK\^m/ǾDZZN{bؿWX~.u{6>') D I&]56cȔ.{w= 1`A6wPY"c)cvWm80.Xj`N/f&zUX4iyyjj7%ys|Ms. kbuu/qf{8?w+o̷?>Շikf/gROkri " Ȏ2|JS3eZ~~??VAHZԪUYN1t=t!!6 8]$.7ސxk3<H={+֌a-|HpLᆳQgFisp 8Rx(YcKcPLsܭ"3|VxR{Q? _:a y%pMG D0HC4 E 1 6WD+V&)ҭw!JTJFwlH@vmo$Kȶ䦁}^4v}3ruŊ)6}m'ko:x^+V2zuˁj'R|o{<ȝ/yu0\Dp3`FAf,)OD=|9GG[$h4J$4_'Ff&*dժUHZ>~(Kwлzl !!mD1*[I붺[Mc55mA+{cy;aF!"dd )-c\:4n|pw1g@^D9 gW2L@-[OV @jժuQ? II*8IѨE"FNȃ$T> EyPH d!mCʎ`zlM%(mB !IϹ 4pO F,uC̽1f036nUcx i3"; <@7@o4~\\VXlAgszm½fdmˋfwoLADHSvCl4w+$\% x1g@0RPZ*UE#wǻmC xxPQ"xxG G#flhH4&#D42!!Va9vOoul#(_Woǝ9yFG!k`.:ǫQ!z9k@= wpQ{ E=?Gjժ-qVZE ǷO1mY>Joۄ7M!QGwc0dccD@b(t!4JinP JD]-NWcXmo3 4[zv%d)M,E( 賰]t.()dd<^ q 쳳F  @L ؚ81qcMYBH!/ q";DvZ+_& 4nWVwˮ5Ǫ =,9R=ewwk) 8~7̫ߟѮ/Am_ɋ晓&8eyHFK)w$> O@| w 'ǟAنӟ/Vo~UVoR.U#b 1 w"x>RǪ8(ަbQ*>O+ђH$p;+-ׄ Qz2oʝ}F%;G1m'+ݢ0c@a&EN2{BM3 eqG'|t dWG&hL+R=ۆrh mCM2I(KѺ0jR8/ rk 82]W o/sKƼ؄5^hI\]} |x7E~zwMM ;kLn|Z9M&Ѕr]s-pBF rZ 9<@ЂhG.S@]l|+.0O?VDzjժVs?k! $AOM) EAx'GG|,xoUQP|$7"|^-d:|+:dziK)%c\;Qlv#"Hʬ@[b0L%Ţ]A!Uׁ€{g;ƑS/`ȎoOJh3/Kbrh8$`t\XY4Y;8at]ꚝu+<ѭ 0پ='v{ $e?cjN^~mAq]xmo /7?i+5w@L,4& r( -hDD 2 "wE zE_s9=$h$h NjժV[?_aȈFhB&**hpP!(b}xoMPUx~GfHBN#D@M d;Vg>JX_v7O7Y+#KrixT كCG0q1p0x̀jb+]֪Q_W7үi7~kķꩯsGu6|aj;IeBV^;`El쿭|ײwֶH٭ke _;zrTj5d:oOѶs?aN_i>6~䄭{IF}?k3UV j}{0@@A4"8hqN"|JAqۄW=>]θ3ah)&0 y6[v"C)6*M#16ٓѻc3 ( 0xa Pz8A]qܰ̓A!UܙIh'i[m&f D]wG֬@h|}j^cc%eh\>g{N9셯6oWT:BېJS\017!n=`yv%?; 9r-ҷ,= +)byHqIk5ȪUZ~? Ckc>v<]/R:o9 "3xrw,YѬZjUR/W~MP 1#|>jʋ6(}TC@m H1 0Hd@0e0Ja>Y=&A3.'ArTnU.Nܹ^ъ?Ly 2(VPQ`(ɀхA ۡUHiILR4ɤܴ)Abn&Xe V4~,&bdgH8pWȠX%/u^^7 <{ qCbK6cy=6.^ \v>uk_7dF%pZmv̂{{C֧>|BA Ht7@bO}'V6j.w/H//O=>ʆ/ۂ&&KM—ȏSG)1EܷAjcbd!Smost `%\>|x c"⮥ܵ@brK@JG0b9խv>d5#GkL)~`y<v2 of?VՄԪUHZ'"%m &hL *4 < ȏSė yw1m"c!1O(}뉾 X 0'$IcC(}m] pĤ0ޓ-Z?20}Y9Hk>9&q!сH@Cb07K*]tOAxx\X~ws}שw_t'tlj]޽ūXյ =='ж%MsF@d$ɵא/4s F";2y#bd?.V @j/Hx8"ĄQ DC>1QGQqP~#ަM&m6F<}PB}Ԉɢt֣~9@X6*>#h"q׫17C8M.e;xǿ3b?d~ȕXeR,DU5 峤2f0"jPH7a7c,n&xz}xR~X_K^+׬5noF^ei& IDAT`vw;_mX|- >WP/1 _NrqY\1_XVaAddA>+@i|"D>SY/pBO'08o+Uj@jW!#DFw <(Sě&Mۏ)$7vul*;0dL>G7Lg>242/ B;r6 -&QRGf~qa㡐`E.t&0:'94ȼsR;rIPCa7$*1 {:hsVuZݽڗc[)HV^0A!9Id"[/A $"ȃ1‘l!" dő}c_xH^"?E lkKw?{}_VuWW{U}ÆV[E r\p [B`ߡ"q^)Pg?:Gs*M͜P@kAC@$&_:KNVck50?]1S28;L%|SSiM2`x5Rh @Y%G <1YfJrX:vMM09/䏧D;vXsCֱco~oAsY+ cU ٳG$H\l:hljl6MQdnt[xy'%@x$ww$s Sbv*i"O9tj u`ꌯ9Gɭ9_Wpu,/ט,;)rufAe1 ׉+h-$^Ts2}ͧȆSЏ!(+fv^eǫ"8;/17*Hm.N]8ݸwG;0gB8vbpk!".RoHR,>+m4 N XҘ=~d?kn;]Q&w*._9 m&E.QbIz"%N^,Cd h' z@5j{xqtx!;lOW!aPd*UJ_xRĐ(% iHljxJ:E<&ZԖ4sf)c!:_rz5]MϥZ>/*|yp@v&`$6p$0 NQ/#01ASO~~mZ:`cDiط} w>D ՃYPW@l(ڮxvA# tY\ljl/6 [b+m* ѝ Lj>~/2u`flsx PQa{7n"}ڰ~zfffjoB]ub<0b &Mm"mlC9/F/rNSM+L $mmWUPÇ_77:,ľ-:CaWC%P/H×@Ǩq[R2:.ZTH];<J:fI1ǃ3ns_S+euX>? Q)# j}Ļ}N޺hE1h+/oPW.ؐi cCWxJ#ֽ! +7n kw+Y:aDm5j94.&?$qIx`3\qa`:36mb\ݦm]tkݗ7WWt?v{(`E_ƦR+ }k);PyyPb]ĺ<9\:x+ 41:q!G'DC0IӑrkTlR]+ո,EnچX Ǵ0+̵XE!e b_x խbieW.Zn;+-oMV3a{}zy=^&[̀@]gpָ;m*:p SKT_li`kNf9saIi1(N IHt͉VeIrQQN-_ML:T@Ot1'1?&eQkt K?xv0TcZGKvEzIo>K{ր+p*cDR?Dwg.]ި$K}-{_).@,0D#l4ILY-:$<ּG%)](%q) E1ؑ(R"J\s=z =f7'JAÝVs.O=2 Z)0ӂfgzt լPY/0@Kq{qcɂ$ AG NgWNW+ v#E΄l;\h  zў^E\:Vu|f@?zHl6pE#3\u uUpU@S\2R؟3 Imo+ϾSmW;+-usW77n+MlM#l!Mwdq95^!sFmM* m:#'Ol= *A-Z >JV `9 s {뇲/T;(^eȈL,y|XdBTėi`ig(yןMP=_tQ;xTupNcԥ(&Cd 0;f= G>!Թf}fw?Y~/}C_X :Ư+wzEwk“m Ϸ66xP:uUXXUE 09c# ԷoK~{ nޖƬȚg7GDNWh\*bC.}f&@` =u5KR-Y%fBVr3r=w-D*[jvZhIG>Ur@t YhZhZb=cXyф/q^SS,d^6E. שx4rQ~;%sq+w?&8$zv.Yp"=$ ? $׶nwŋy_?y?_p`mZXu|݌?߫"PZ4Axǀw+;u[u::.!@Ugot Ù0#i#nW{vB@IK1M-lGӷZ5 dv.Q|Ѝ/A8-qǨ8z 9(؅Y9Кs`&ZE*MZH .SHZRX9EJg>Z U9zGIQt|-O Kzz,^U:xA}/ e_qچWe>@nY-.V϶g[{>VKp gBudZ?߻QWhD-3<&6+cە:ɦUS㪩 *uW*|vnk@ zl#o} Q pAKuI޵\9ȹZ"C,ucPdidk; z Ĵ1YJI Tt: <6ЏUrl;01|$mɾj Nq 8- 秘 3̮NdWJBGF8N[?t;e+\ע4drbn"Oo z{H!l϶ٷwW_Ms{~qq{yy{mk:>cmZgrB۠ZM4l[%#q eڏ'x+oU'MMM k >Y <ۭġRB@ nK+fGEBM9c(XUbL EZVCE+Ÿ"FLx,$,=L5ea5vJ*.aAgp>pN9B_D"(iA_ǘKEg(^heZCW9i :ep6)lS$Nt @xrqΒV(X ǫ堟,kyM'(\ <C?Cs=aA|?a{mw߶wwml? ǕUݼeꛪn=8θVYgr@lD8'.#\UUe ׵Ả;] JgMauh]dm[$T-ն~O[۝'W%v%ŪSM2dL, Z?G_Fiqq5YN͵k 3ݚc -pȚk=[M3,FFT ,rhZ9BI5GIVj20*;G\`UDzL g|,Klk;Z8]@ؐ>p}»df&ZZ.gelUNةɈr6>am=@U_ӨSO̞|_>|#\kudG4 K<#p Λ¶+uk*SU}8p[];Vu%j#͎|k=bL<и/*bS{yӸcZn7X@$] Sԡ>BcZ(h',@!cT,(f>>49X/l4k^&%бuq=ym uh`|coEaiXw)xBu] d \hݥG(cq:)9|['Oٟײ8"w'ǧABEb j[H^g"Sx{tzAQjoS:ik :>s/ Uklu#D!WŦ٦f[TUPՕ[#SG{~@L#[뾼!nn<<fB&v:rtB`НkJK3ݱ/18mф9BZ"rfFۍ)+l:!92 flYңyHw @`<=OTtS}D1`<~fn~17V^Ŗ1B Pjv<לN[T] IDAT"pQs<*N_gj :>S?Dp!OKm \pnDmuk :SSbBl1 ZD@1 1nu{{BHz,&6Ml*o i6( xtJ#Ӥi㓢KZXЧCˌʬ=[횅NW`(s-uCcY]>PȗۯX (Wv0K_ PZBZ+by+}_O~U |d ؏M$-=i7$^HzgFj'go*H_ @ֱbufV`uM"M-nRGWlȦvf$y'jB0 Xf5xK͊ecbɪ))R>)cV3ޠØb>t$خPBWm"zPE/"˅dHmw%͘Gp _7`0wV;x1/\}ag9O, h=7pDxntolHCm>K pV_~q~4!?K_f?k0:>]cmZǧnU@Ol$ x Zc oBH<'W75mljw[\kQdL2B`3('1u̶nwކuv;(F)UpÁH]9(Ȧ*2$G`1ezm}!MԽTP.P Cłn.6,hS}Bv)Ov>0e>zq,-"eqTLXMk[D"h\Fgi̢\ e_ ,y[ӽ5*k9<1'|St`́Ozq>EH.;^n/%wS'>G=zJc/Ij޶u|ʀS7a l!Û.K@Ý>Ah4+Ih ܃Pk*8:jǂ$6mˑ"'L|q$'eI߼jr 9y*U1k~Kɵ9&i\́Y't7՗4S@,veWq֠ yUyD&Hr ݘ9d?G:zdeYzZؿE#Rܚu &K6_y=x 芮3 1"hUlSr" |M0- ;8BD`RР-VI1#@ vc gBU,Ԛ%1.GbB4.Ktm D%14gc1[{V!0_oi~>tj]; CR9GP"0$DtQ=U*ŮF7>v:XJmW:>[ RTj\s\2 6;n䍘~C裤oNݚ]=8$0N 7$6_>Z Sdu~/gU ^#Msv}/0CXU=Vu|FX:>M/ [.H\x ot{Y\qGeIwH]R'}oԮ{bN#`*xZlnmiX0R̓#%8-X2KQ#B|hB;L'k KY.M+r}ulW$@)u. >4SI \}Rd)*Ĩ$/c1D7?.sC娉{bqLLGӅ@A f{$ưbaSyN֖Gt908~ldo֭3B68KeKpgL*p Ik|o~"c2 T1f6$[t ,f 04W^PF_{ * ڬRlLLIkdFj}1h{%ɭt 2sG~E&g=LJD'G6ULȪYЃ1-3C&$N9(>p|fg9"3Pf4G:6݋0bxbR- C1zRzd6^:be1h4}aVΧocN~5M=td# bnG fr-]8$<}Ѯ%wRT 3IJK HBt ҟ+0$-(#]F; þ9drH+r y/CcI3q̅ +s"|%AE헾?j Dֱu|/Ph9\l%+ pOC Q㠶LL Km*@PkT @Eh>dX:;Xu.VM햺$.Jg~b7Un<|cFXC1ml {$&Y u8-6spa;O]PԦ:j4H_s33"[xG5R3p ߽:ec loF_-"gَGx G \ ruMaY?-}e](#D2D"dkr>MuznLt;GӢAdsλVAqR5nRS_,8p7&<4y <4\k)ڕ[WOB),Q~I0!:2Hއi]Q~!Rp33 b++wX1dqo `l` Ql B,1Ŏ_hጵzQBB((Z+e؆Ũ^F2 dQ{3ۇ[GNcJ#I B!(d,m X\< =M 9  P %NL؅JBxTx9AH ʹ_. V& }A!1ĨmcHK@(T!d15@?] uܤ|:NtE$J", A=ڈUZJ $t,HrSHLn4uqPF4kh f)DbTg5c;wY4!!|NDG,8?!f{Y 0ehcz!.mng"V_gT>u|bV#ňsۭ\K!|3wF:â1*6Vm*Xʺ3&+ WUue@YK5"2gXӇr6FOyoBԃ)9Q ricu!:=H_g7t55x64pzFP#!FsꋽKo)$ݍK; 221ҫ\w"D;;^xN#-L?. _ᣳ/ d++?p4soz -8.W AYٻG&_ػGo0H튛F4xTaVmUio44 4jab9#Ka3w.ZX*q>Դ>U u:q!>Ua`Q,(&@ZҩޙelH(>&\Btn!ẏK$YKLa9!sʼ MPU`,0A[ Ao\.X*/Qa{qXhcn %1~av }|b>$:\2E2?tdXih9 qwu_,_; ץ.['"2X-F7gBb&ؑ󌼷# 1CADm)OǛ91h:V!D*[!uiA\>4d>EZIܮZZϖ0 c.x7pĀyI~Rhu؃bg̔x˰M#Ei7uc6~-($ޔ%:HBux ` 9?_~6ۛC_!k+Yǧ-u#.ac#ɝ.Ͱ]zvCH.V;WAuZQr*`U3;B]yզ#"'6NWLZ7% j-KEc1՘g8fiЬjtwOq2X K%dr|2(%1X 1pvJKg!G}',vIGwFD'*mQ#Ak2#صYg%XV׀: N ,n]$ f'h>0ŧ2t̴9QʁBӵ7<>Å7Zg"[I5m:nV~N^Ѕ#ׂ$\s`TBQ鬃{y/sޣZtM!c=IRr4hB~8p)`1&\ ~|Ӷ-5C;&^>t "oɢ gnOY @)9 lWAz- or%ׄTN@5rJzAdzG@(kTN_'0@`lؚݥdHBGM12Wxt`̇Veqe0"% V{~n.n~FZj"fܓlؒٺx1ͫå_o<\a K.wPY6ٷqj!j:c{d9]#8%,_Y߾%E'5_';9칈BD^Li | IN_fE3DYHrDH0t*ڼ:\h0>fR0sPC~Ao@\tͮ- g !':dc+Yd.aK!<RǹԱ \9vsz<Tж6 i:]45TB]ԡdkt[{1F[lo3}c4{?dڳůљutXDj B !v466HsT Cp0tR#gIs@n2$I: I/?|#Ƕgi:VME38 n &\y^$R^ ng<{m2goQsB5sw:ɳ$‚???+Y @ۆ .k<ނw|.'ɆLa Qg>8kԁ& ˛ DpA" S=&FРD3@Z4wd&~q` ܠ " {1!GP4M}}Y P\՗0>fۥCR0 $KE4)1D/ڷu~6m>2poU!2 RA䖺3F%M6}*8tXT- 鹽#Sד%cˀ9I#t{cҀ]X`7kc}x{+( @#u(Dۮm}exYv!-i;ϧNG$:zho䢸.;gcŬE3 Uo+gҵi kJlIŀ i@n|M% Uqz9u{W0 D 5xD.H2@@(s׷Q> IDATuc t=.xW>@=45*"&14B@$i*x`5`?ܫ.NЁ|R`X(lLqe8>fV1%2>J\%T$h"Z W؟%f܏-i`\d9۪ݵu۶Mk..Ն㖘ZkYTfo:":rkUCǪmcݶ ۑ#h1vt0^(7bg6`?-+ނ~w][ Mky |^!I;(~U']`iɔ9j!9hbL=MH('vN|e|$@Cܷn HLf}" $$^popK ag6&&eB mt,1gw٦Z0'E +/Zc T۪^Ħm J!n!o| {/66d3BJG9lt{ݦrqc1b*"-f=\1M8 MrqasYFKf6sرT 12>C њt(*1@ZXMHNT1:hcjط $&dR1f-CXKID p)|)FV)BT]`jrYq_Kx+ר̸ q3W Dh'M2ϷCU ] ԥ{y.%k~Ah1A5}dr}1Cal2IG {7龅;ciskHtѾqu{vud_~RJ+l!\4A ]y O#Zrẏ6V8g[ |4BӅ$0O1}94!׺GYk"5hJL k EyYbJSKBG0tg!!%MAֲ`w촒u."?Z'.nw 2W.Vmۦ ,h J)!FZd40B++t)ya,A5,T@V̭>u) ݆`M m v^"}Dw䱢r1̏zl1ӈ}یh؀脇1bQ!ʈZou{fwv%!S։nt H d=D$6&څ4EF0ap4#7<+~ x";6c&9#0A7D >mlrqo OnKVדl@l/a`d+Y'?`hB |%<ݼ [k 3 gtq&\ltMk=@|H-Zw\0h@}q^V^#T-&#QUY>=58uЪUfC _Ev81m3aݨMXvQ=۱iw{oڷUb,s[mLD"mhaB0;T&͜ #dLX&Zƴ!SrHQKckf f*M0n`wro~jsl8+ wGfK3~Κ~%gm&ĝތfaI0yu}koځL:0;n*4=+64e2"QN#ĈԵG mOml#P(]Hȓྔ?f.`ccO׿2ȭQi(NN> -!pFЇ"C:Vlez80;\YgJ,"/ltP$Xlv#mgW{a~fBFij".>OƱ9EW̎*)] W( i>UǐXf6][wԂCl];ϣ)wNJe)(P!2#Lc9, ft<(K3cH>wb!pWM&ׁtW3kpѻ@BMC.Mnw95M=4Bk8E'xChjzW`cF·qk}`gІd(Nc㠃yXJ_%$s5}' `DHj;kj]']˙4`{9US 8P28KC.c'&zӚX6=wYȭ?un%B@KwMN_ @Mj!]e0B

931kb[Dd >f-tDžfql҄ PY'" L~Dh< {`jDb `Gʂc D_IY+6n >K]6U` өR hd[ḢY٠qyjlLv}:r ]JXc^mAn1<^Y*li"[vJN 3NnjBh&{A" "ۺ%Fƶñ'w2IR7&8?HiLcYѺTмIiopXAGbC*U92pCK/kduZ @zq O<@nvAڴ4MNh`~'0&}TUiR[dd{,%{sѶeǜ~pQjs lE"\sa?CfHSJl'ÜMٙL |,Gbs![fFwr0=}㾭[4p>mr?ڪmcmז3(FS+HCwI VyZz" r[vh$5 ɕ1Fފ2B)*}HG?m@a|-aa͜?`[ra3x^DKBiх͎,]r|^P;Kq^6YY.,;Fȉb3 U$8YyR3NY 翻bTE1+̽4ȗeEw]Jap]AR;צ=fw+Xݹ&բd@( S! 9d0a5E;"['K%y3ZԈ vfBHPRIXfC=ýSǭd[RF4R0}=vт$ױH@{:l{YU]Ubӆ xmiSd04aEO54`H=,1ՙ|{yww72*-DFd<ܽz *bT5cbՀpUN{df[Y ;hN(ԃ1W 3?ns1?ܒ4ytXu-atw./#*.E)7 'w~F~AG;28]q }Og^+H!R\}WcJ4={%D'(!Pr*3D†&䓣m:PFR|1{v;B)%_$f8sOx4\iBC8<O%A|\~\8 >vӟr ]6g`Z#٪[U@|>vb]=ZPOr4 (]C 惡a/|y"(x;UP]qY5=-wrb`g򀌓 CMHz"UaeA2 _wg%>g~!E\ cO6P2#,ܽTT\ kt[ik( F ªo.@fhkf WsI0+ͼϘ~9o*X@}3 2, )z濹2 d!ŅѲ&#n[]H[,XK?˅%S؆OAH<`,ɠ|D/DO)\?]^G;F&,gz|&J! p2՟$-W<\nZ1vQ[gΑ?B4HKf6h5cI!@p٘`cfB [*A;DڊME}yT{m`\}vĄ\ qܼ#-,8NU/uFU2)ZÐVVe˘*p570(J87,9Q rYɾh\:yCu|@3EH.ɬ\>`29qZ))"| AUN0 +mCh;&b6,܌䗌 viVj&|Opϟ9Mw.v}YR.zl^{&)wUԹY2{2;/T`隦DAT4L]a]AݭxdhnR5Ѱ !n"u|O Ci K*CƂ*-њHk^0 4OM׈%eMp5_+/9~Sqp0|?Aȧv䕢6!e8:=gCXʭ-Tk qwD->thC *)CƩTQnЮ3%u3d[W}JV] XgMeC;b~[gq>gI~zOy47{, 2 Zf'-yC̶:%=*$$e0MN~L+̥"{da>B- i@X^IcҴbC1 @Hg5b*I(HPeDkEb A,"ń#%W! Ifc@sr?\ԅT,T֨8UW'*!M4_ҊTQy1k.DV$ap]NyuK"kDBRAii?ۈE98gcipq#b┏Nׄq k(n擊)IW~iW߂^ 'Uk"`9ՠ O4y`fsQ.xeYpl6?Z؋\f7Kڅ7R8?Mŧ|J0Y#Y,:K L"s`={f8eˠXKC yXۦ{mٗd Ldx.Sⓟj:4sW^= X%z=`,wC׮d@Ԗ2˕TZQfi /uFʵ rbRņRU88ytTLNMXlÊi#j5q+{ŏ`_>dz5 :lfJCDsbt9x[>s Ր̫d {N) >$(_?%owVqOxF (1 W̋d@Vc4C&b./tWY>Ŷt[zj$f^aT"y3~Y 휗b<џK1l! ZI(|h5pǀKL6U1`ܞ ܜ '^q ~a9z6cO z.4IVTͧZ4Th>U[dX-QFX1pa.bc.j^|:zy|9 /`?ws%=M赤#|cJj-^N4{uM~hHH+™A(2P /:4ljNvsF/r=*%BfE՜ōB-&I";)XG. qi=TJ&^εvЋ&Y՚PZCzJ̊K+QaN]p)Bzee5b gLd>뼝& {i*ٻS)]DHHЌnDq)9b2 2M %1f6!^^7Y]8GQIc+o#c- $y絴LH{ _ǬCg7"F ؀~~) @_Q$nܑG$]}@]xP 6ckg p,z{ ` wz~'R ht`٢k_䬃II`k&R:U8Rơ}*i,>67N3XPe(JŜFHa|+;<֠@U @~=!c,¶I*R"ajllxɹ_]yiX_#) R b@OEh+(Gt U*i/>7Rg"|,Kҵ]1P4!;2L" O镋Jc!JJeȵsqaH,+oIT{ce!tWmpL2D?f(1pRAmKRqeR+8̭ p(0C&N1]e` 4@|6gG\6x37Ua/р ﰌ֏.z%AA=>.QdJvESGcdG~N\F\y% xMFo {1 s_0Gb=Gl$<$x^݉ A% kU#jKeJeenMNa$Hé~cco:NIYTBR EN53) 8wr#rAܰ+~(68lu> ;׾K7"3[p.D|?\}a2WC%-otȫaD:+w ,d#+T⤗b4Cw IDAT”0*b784k(ɜ&#'sGtER oL 5;AY"x*h?dkǫ0A̼8Nft\z8{S{$ü-*`'Lu;bt[ Œh1m?/.XVLUOz醚-Y5LgGRm2hSN~p"mk>%><Xmyg=;˷P/•kB\F?A~'IW ,Bai.%lw]{DW: r~+_C=Vl^ hUyEV+~>|Ɛ^XۧS}47iOT+=PJqک枑ˠ/nV8}aV;5E ^g4P D#JB羢LQb1Y"o+|2c xTEv7+jt(|z>Z)zh5t-!F{z!B9yًgYtZ}p撵vdDX1Y .hT"J4-x0%bTrҖ3ڑ-yLٖ}=!NJX8k1U*"L!Sd|ϯyC)w拠J t*5J[]-n.?/ɮ̄KjnMiC$d@58inRfת%I/ ^y&0aOo**v7W{bYx\fPoQj|GtWn8?ʂ\tx{쇀OFs,iW # ' E~x%|{Yg>sܞx}~x}' wMuK26KxkDٙEҫӸ6 u%I}1WOSǧK0 R|Hy(&s|2'}a0vzXK}209H:}J21m_kjlb*Qugc}yXo_9`笜%0ץɲ]t@q`ChH F7\mHHZIYf?q}G[?&JR1!%7Pqoϫ6uHrWz"4?ߎSW($fQ,VXJb))+^؂IsC|%G> K߷_1ZEi*nHRK15ժ,\:zVW~=oT 28Է4gc8ӴNk ^dXe3,B{3(L~o%QKlEHzZj "a5;gOW.yq"Xəc,b$dm6ncYup rzӺY0{jfՋME^$NXY+c2VJ!eC_1n|I"nnGo6R􊫇 sHT]^s9iFmO@ѿHR~Op߸ʲzp?p3[w||O Ё\+7?`6q3ґLN-"&yg eKNιpdeDHř8sh^-^~eܼv/4 1f7 @2b*>?d]+<4f&TO\Ƿh6pAM⇯%ڟ]sGnOzܝ&^?[F8)AY'΁KoJQZ{:<509U@hGѱ$\ظ$s@sjqYF6`. Ll2֩UH'Cwhy"aJ&퍬DQӇ9\}ugR5`s3Kza_9?~| -) WtS ?$We- @/[]DpA1K H;Nj!ōʺ̀`i8g{ nWCHޏ&jҫ!} fhu+gzW*`kc"q2w% HSR9ڬ kf?gLT#&h-8\0^4U K^Ŋ!)ٴKB˅FS{ BfpWrAgD 0~Tz7Tx, ogY KtA5[Qa*| 鐉IHJEJlƸO$7ndqO.@dxSf Kvba^PNO B["S1 a88M6Zzu/h+\ "Rx2Jl' Y ܕٸ-.[f;(Dwy1THZ4rra,ƩUQ4khU1̬tu#o mޢgOӶc1s۬w}(q ?rJ.?u Sx@BE!/Za،=r W2_CͿsBK^ƅ҃)߷s*O~Zc $W«Ln3t> %WǫSM"`(Ê)S)RM1N4ROSg|i2wQTO b1NK]߉OL%DP,"y,cxزET-z©ʧ:XK C9`k&Q`@# {\2ȽD]!H^w,N4Mff)tSȰdr:'\(|4<ǵ+O|v~UXR|ٯ{/Z ~81G#'KF{µ# @ǯ=٧741(>#~HH Ke P${YMfz:p{{+{O$J0ŶK^X:Q+0Ubq4='DZq4=Oi2ljUO]ImnFL_"m)bDߥ-7T5R c5/S񑥒% 3DUcs e.QvҺVwp?լi(2ş2Q +]$lH,UQoI^}e~mzۉ&.˕A o_-FIyu2#C Y_~Uu WDHi#Hn$Z6tY35ԖlG]Y 9.?G3$󱀏 /Xd l0 ^B"dVMo>?(}Z/.) 3C1dδs@*&:vM`lȬ8C:M; *Xh}4fIU^aiSk/W\mtEa5{ȥv8b_\xn4iV s 8R$/^|a|hV/^5Х]Bn^_YJC6v7Y?1ЦQNtWu l7?\؇rGa(ԁJƙ lIqx\ ׹ۣ#]Y]QhwkgxFK}Ћ |lc5gwdҦƿvA @%ye]e<~٠3s>tH6ρ])dWUPHGma=J ܜ=.X v4C b+CR LJ-S*;7 M&h DG1X_3?c=-uyFVa`7:Mfj&(ߑwIOtqDŽ(*6:ХcfQ#/ayxj-lZڥznI=nѼ7Xno ނ@fbDC^Z_>.,/~o_|B6[@O6o\e;<|g3<0 v%|*sW X[ \̓sRŬQ-5}cv z1D9Uwɀ~^w7s- "tj Lg+0U`ɯ8}Z oCDZСS%3)+ٗIobCHYPJqdnmY=!(hb3$C5? ņZR&+4zͲ,V:hEn@/l.G=;9v} Kw/sZ:kIژ_cXG++iӼS"IZ4/kŅ*;6jN lF *e8X1XDX'nf+BeY\%'c>3YZ< s@YVw¼XU`I'/o]wNn勈p $@+0Kȕ/ec UQyBV#vKNsL!˿)uIǕE?G#{2ȍϻl4;Hgnx+!2E|{$BRR78 ! ?s@p|q ނzMշHʤS^#<Ֆd##ePJ 8:Ɋ( >nl݄F3yHr>|YٯQ:%N{njHZx%+094Mq'4A=qT :@ލB7IaPsaU)V4UX B"K97mUHWq)C5;RZPJ= u,C)fV8 V(oCfkB^@ajHQ%8ɾm%/6$c0I6psM_l^%W!#ҁ,lh lom^$|)@/*HCl4afZ4P/8pox롗=fRd0!^ BJ k\OdM\_kC!xfȮ>Nǧ.|⎏p/Яڦ}i%W0>dl}PA.],KO t$@]΃ |d T GbBX)QKC0dGDŽ07o8 Lو"Ϊ^,6uRgЙx y >jr.Suc8jCyJчR| 6YɌs2c8 |bqA IDAT67̪ЁHjdlL^|7QWH+& b&O;eIZ+uDlvfF( AcZF4Q:dDM48+sk2ZD ų<&^֮L!э5d.`BTTT0J$ \Z$cYMhmϷ { 7Nܨ:WT: ;eNRŮxx3%y<[4HZGFD^a`C|l>aNWx|PTo!6Zb>צ.;4MH}4y<&2ޏ~FWjrhPղ3g]iʥi``(C)L"fW_-ǨB;^`L,TZ|sJ)fC1@ʘ.c*ҨVۺڻ//}Zy#`r7dyzuB"%CkiŌ2D9T& B e0 Ł4Bq%f9_?Q R㗻YQm^dU5OH*PP! >Xm[#u2Dkidq5R`RurU* EĴ>^RHr/ń$g%B#nyGUQ&jHL}Պ(h[RvE|ޏO<@P0ivBvuw^Q9>3 L0eky%Xð Ot&J sCy$X`Al lP{' @Gͣ*r辉MΥq.zy7d4 ƉVjxh@H"i2.sS,)3SMCA,=%:H,X Eʱ b6нƕGף'Zؔg6ڹzeI&GG 3p kJ4ߘE#̻ `5V9d-I ,&^BY:%3t3ZI/Q@= %V^ρOXy<4 %{[X4MfiKmEe6/òh)v,m\fl"ё'HGLN~ sUחzΌv ͭZZ,:/ !:݌\U&#p#ѱ\$&a[(<.5ޏ^~țe @[D:}|AgijIqQpLʈ\&2R']y.=>W\}.3J$n3w~5j?V㥢UR(raUm ڹZ3^ Yp4V#ܐ$%Hǀ,@$' \a ߇VڰU08Yv0Ikr?i(T4OLECB HkR\@O\_X"gHv-1Ӫ5LL Hًg_5EkPѼE"e}"i$KHeŜREg 3lv爋Z IԄt@HXDpzW_ ^c)S7>97qR/ FO7~㚎u > 'u?ED~_w|*5EUILܞ4&Qx@PͦqRNxf1|;TtHyU\-B&U-,Hsfn!oK8jRw9ܟ+9 0REn@73Pܰ7]/WH%D!jwGtbGøݔ5<4,[vN:4tR[1@H )UK q )C^sЂ}`E7k+uo)D_)W~|υ("xgUmtI,>W Fo;0CYQbm$_>Ƃ]1HփxtBF GyW):M{8hF'`BH u!%P`Yb1[p2鬣sdm^S 4ǡ8`b%n^{ z4n(QQ@Oyܪѫku e,^rj 4KDqƁ@Db8U|3+]u:5_|>+># ~=9ǯQL&oң^a0^4iPBe{'|L |$6Y8Eo|0eh@ئ_F # M2"Bj(AW l>| PM^"'@%ujtuLgtQѫijL3""KFs,6'@$mǑ;;jTn; 9zm큎'Pk6R]~OkHv8=#k7XLzl#ddd<,*HcQl}-6KP M2,Khco1~p' @/ɫlri!ZXhaVI&QA)(r ۶c!C|<ƱS1T3N%n-2~y񲱎VMt#&h59+[]uw[$J2%Ʈ&do:P`k[0']vm\f|~fDnV1*׎_~i&(4T &PWr=Op&q|| w:_G ~%΅3Z`?Dtuw S{=ɫ`AnfsB"Q2B\ dD 1=& ɯթ%Hvoey-lҔL6 V8)r-M\z} s59h i+^Y5͎kijE\FuRmZ lܣ W0l[g6]f(߼ыD«R?ocxC>LS~>!ON3q83+ ̀df"w$̬ _-d+\:bR"$()G@ѰJ?nO7W׹u3fTȝ TqnJ?U!ha/`K*M)a?%`͓dm w% lS A& ~W |jhɱ>80>~ BT oGr ?ߢoqnpO+v|O|+ 7w67[pVxpwćNKu@`؜g+_d J2N|e 4ӳeebE2 nDq] 98NQB(3}bp5gjMv*f|*UI*Z׮Z6L(2kb2f^RqX~lH[S]U<xcv[ 9{>$SEǹV?klq΄ 4 02"`E(̊g^<liv]PUh)i}ֱ"NBPz1/~:ewK>U\t[#ObWTH[5oLܮS!0#c!x(|L*ahbfc~&g'ݪ﬋C6Gr8.c/ߢZ/#:[U<4Asyi>p|+ s>v[u oNv?'ÿs\*L ap"qGoLg>S ~V{#pjGv|<w{::q:jB$.99I1WNտMdcP~\3%|o+il-Wfp6P~8vy7&2U0T¤en YPXXL'ffWRv:UgC/gP@+w2YdUg1s| LlvU%/ 3b51_-+YAΕluhHP^`9JQpx" |djr{l7`)\0ms-6?[Jc)g~=ӭXU!RN54EVK= P UHW|<B&Z>؎s&ǰA垛*

|2'o?bCC` @{@,6 A|w|[>(- j0?h`=7q]gHr.}]OI]Xc f(o:5G۶s&+؏ƩMpȗ Wu.~H^+V5d^X$(NVXTKj^|tSuSD 7:Wl뺋]:9UC΀e9Dլ ߻ KIN9{ Q\OSLeeh&{uxp !Bkpc;( fiV!RrՒVo ggZfKp>!jČN db,A ^\V܇U'W X Yb(ÀRJTlPٹMc hR-Û1Ƴq5:g0w.[jn .ĉϩ1:BL Y}9-MoCcy"9yaMpZSSƬV|O0Ժ82w[wQ$`=؂ z)|J-"{^ٲ.;hv7ߙ_-:%S ʏr-Ӱ,7hB%$w  MB{2E˼yO5k'"ι'o~;"vs]\3r#qM5BYy|.K? Dངkv-)T*nWW1Vq%OP5fY&y̦vBWgtЧ0Ԡвw)u SdWAL*g9f źX&wSp8_ a8>d7sRG7|ˁc6'c6"0qnve׬b ˻ u`Jd7R桃X8q)$&;awܙݚvz c5 G'`n2zZ TL,aV"ةdPJ7ɩ0YP7,X*AoiPj7nW&SΣ_=jI>9<{+&YfT$%\"-Ob $A8: l4a@1M;g]1YSmL#ETYsYuC[Nn`1f?&jӃR !N" A@Xx,\e9G'~"<7n^_QW UL>o_l?09PBҮ*!$ұnG^_"\h:n'Tg5©AIe wcf* d<$O6ڐd,[PMf`67mH'wO>f+$9Ø@J-B#k3[L]I"v;8-CcEҁ>dj%6jj[Aͭ'W1Yoi9yqE^]SvZ,nT8|lӎVOPrۦs!ywCܝ +N=c#Z#eFp?AUcEA<.yVq+>i 4K%В T~K뚓Ӕ(&IM6RE]65`f= m(z2]X ,Mћ5/o3rQ(5ˤeKmPWɦ+"_?  | w(2 wc"2Bx(CXLK.\Si p'`q$j`JU!j7 RZ~u ի;ͽ>hX5ꪊα xG7x&!UvXJ̡.@b9ivDh Bё 5ʲ'=&%nҘ<f&C%AL36K>,y͔G6,Q |TS0ZѬ&[9dmut-MT|η$𩶉B:)RT{5m{h8 黂XpW73w @V7B)LlY(fvUq7m3 -6_JQ>T5HkXd Ŵh@C8#YEXb - $meD 1+2 IDAT|*bĨxw#6AiV^qe URZ N1'QJHlcu7@] B.S{Md0 봯3w?&s{什W;n_vz6TP[Dero;Jq Нj@sHo]cJR5s-Tcq_(XB~}lMԫ7Oz>c[cM?szX["9eړ5ѱV,e͜.*zFjwbPU1(2ߘg%HL%7OCUuʁq`(U5ߣevL9 k̇i,9qFg;dJ|y$YVP@RNF$,@&Ns厚`:O!լ x҇,힋ZE3>}1X^bS…9g$KOG;`Zqbߖ|VlNfƜaB3JcEɘEBb!&uU~C"˿|!rY֖_/dTiHik3ܑxRe9Bt~+a#yifU!FlBqw=M}6 Ǵ~4(]xZgnIޭsGFgS>gICt?{ѱQZg%MuUjY!(W_C [0mAy=_%\Ef p |RBHw:Gw,q ]oVfҮ6f]Ϻx,3!;}$TvOo4YRʘo)Cq*wGr:=[AZkӦkfL"n\ ^Qz(,>Ql.A+!ߠPUl~ܠy1Vʿk~ ;Mv:Ul-9hQL)VT~5\ NM$M-Mc"dp hƬyH ɭ*ӏ>U̎If}Ѿ8+`;*J$"n@L%PSGY Wk6Yq~:VЪyɶK}Nh1U*xbiJZ{ɻ"(ŷǴ.tZbj 1dӐ() Gr۾ȉ>k}p!!,|6@H*7<܆_pN(x\^* , ⺤: (+Ӑ7n{x\ h`q,w_b&: ĐK^/[{3&бVx5G2@ߠdfp\%א8׻ =2Xj^CBt5ip=֋qJ=x088aFϫtM}9X<2B2(?ݘ!lu9]l,4-*EPS[-t]=oWcSe%l/PoCY/"no)͚nK'yJ_0Tgĝ'IN~枕988=q5}1; j(LYW`e,va(6r ğ(r2}S >$:ً\ARq*yG3OB>M=:J)1+:mH+!p5nJsݡ4RUvw33[~8RiKm $&)BGR)LbHZ>奰@sq&L= Vd΅Y,aҝwUjOf4" %rΛq_KT 3KP[ɓrV Cv 2 %1C2-i| c5ѸBHx]PP9ɛ{f޸\k\[ꨵ=Cŝj6'ぢK]A3/)8Vt{~4_=D9lxd |B6F2R]uV$D;qi`mmd,md x"BgU'׏ hO,11;FSN|;eOwȏw{BEί7dsk[uwʄ~񝙽T~[GƘ~T/nWO$A$ 5I &~5Јzu{TYz);}}c= dSsG7U6qq]mF I"vt1Pdk*Zk^*a:)&)W=C@ F'/&PYfdENCyw}3!K&i69F!>䘄4 @2 &fcXNfi0cgn1ѶɸMEQI Vʔlb|WQ/t#:Js4V'LdX0*OHGhi,q4G6Gݓ'ot,t(CJKB)YiV`Rn)&wtִ"E9N,q+p)t >1oƭ_e99, I9ON?нv9#]S' ͰMDS;r8Z+}֙Eϫ<}2i)U|d"\Wm~åuWSæ?3MDvF!\%R4w>F:'2c06?e:ĝGRHv_oJr -#_O+/]@XXKXJRµ^)B@|*Kxo$jTo4K7tu3%^waIx[kZmM/0d,gw& 0dd3ܪJ]%C; st^Q~="]$Fr$˾Cs9B:_AG7HRnr&o=F[6[_HD{txI^nBAmNеJQM*y)g(dQ3ź>eќ(9֪ Nap1#9AQTyw{_ |v]|~?6hz^q#'G)]羾@DUhT\.܂+i];ۿ9Ga5@//{(w$3cXka-9~w{NDcy?}ֳPuFo?S Fo÷?_8c]|t&W >"9b |LԂݑfl԰a۽ G|LYWPbkMrkrЃ}ʥvI.%@_EHBP=H9f֣F5 zR1wu 4fx8c9y#[q$GOy'e옒E>5!jSAc5=OWt:a=][cNtR E|>ԃEb!"C()hG5s2Yjӑ]wHBc=fzAcTOB36gp ?v9kIe j'pӔU_[[{LD>>mIS^/rv[g[_"t}MzC+@~Y3H|prWЮܑؼN>cX^>j[mwS<&0|p]8OܣH.ua|Uh`1ܯv4m@brUs@iOrWw1ic|̃)uhfh7fOlȞD{^^MgsWo?j8H:{Ãέjdwf2gr1CHt%ܝ=u:Fm2C a$Dpl֚.>!DZG2 J=DxҶIͅ#yp?z9rО%!"MGh=>`h?^q i_2)gXxu2n+_W )!4cжb,4r6>J`Ϩ?;tX 2'`iDDDw R3O"70~(QJ9P~{И1S0fS@'@5FLw3g|NS n4#VV}1yqC ټLAB^gEd%SDm 'ȵ菜؊pffՊZ iE*MIYKzsLdBdajeĊ7"͜Y͐C"= 0aF̊cCVbjēx-ғ: yb씺lM{ y1Tṗ|dcie1o1͋#ӭ|G{KLcPdr yt!RV2PͨHwnReܮCh+p ~֏,_9 !Bw"ހD& $aɌDX\<>z [~~H熡 0T>b2?ےx^D9;4.%KBM?:@nX,@z] ڥbUaBreU]LA0zL?obMTӽ~' OZwIc<ِYfX&}scs2S-]2ks!obsufb{P^DNh<,-4 : #Q h^,k;`XCT"D]u [2 /ԅx˳ku$_{?Xqe#(<] U5]M ڟxrvKuх]D5X=W鸞e,|eR$]gb"3O^)n`އN?Z!J\~XEꉠC?|xa]ƶ8'| cE-+yNtwסx]G mH%!4,|vUYӀvCJܭ hP.ԫq[j;Rlf"g b@Rjy}$\K\gΔ'+`"Zy+WWɳ'm<0r6= &7h.t3s6F0m㈻à= jkLB UѫΎ%h!,bD;R%M9:؄ wIc(BObsĩz* _l< >LW%l̿2Q&2 aas B2gU 20ru-ldܙ3dg%K7w!9>O}sK{ |N55{cDSrt!}GhX_uݑxM2{|*c!#\.n *&:^Ul^ztƖ.WDcUd\bxx F7~PLU{`cwnYwișٔ-Y4 Q=OseM1dD/=/C HYvfrsJ\$.ܠ"ҢΘ~ $P@̊/z1Oh{ֽ%7tBvdDr!SR#ރlA}S =Nj}eyH=c%=ʴX,lt %te*4 Es*S4f&;LDeOtJRaj>}癮=|P٦zy ȱo@-B);h+]y/ǻT CnH&CtNsSc o\p#`Y>^uW~u7Y^ѹ0Xx7 iO;zGg|nbuENWWhq40+/jW OA?46RP\,*dd)K],XU4߿l]'o{||}vA$y-= wz/|f/G|r bwKėnsRȅ]ι}t,dur^iWJm۽ yJ./]o}_rkh4`׾;u1Qu7@m9&צ2YeX>;ee@ Sz IDAT'&ƔSAU'96a)E~usn.UE"1^&-ј14(FHq"A#F3f1R6Вh0( 2An2zF+Qa2%k'U`R[sItAip&|6#`|c>>'TjGVXpϫB'&c6C6"t AorM&uУ_@f|ykVm7 sS1k]9SirJl֙\wȚҁ2yX}z&^qRB ;cA\q;.[?/\t!X-{p\G8^}t|A+_h>Pxjk תBvo7ML>6itauL7GOr_<o}7ԉ~U(PPޠC 浖@]2 Lh*Zku惷]{hԴN.p/\t\nD+UK(#t}}G#=oa1)`\wXnVo@"q#5 @ЮDϿ 7(ak=8}RJuܮ^QM]~9հ<|*hZLMX)\6Wh"I,։Gгj@\;.0bE7&x52SfJB3q$1mNQdIλ*a*1dN,o(_|9+I)lH)`dr,3@4H[>0 VJ}IK!G6Qky5nO>?yb<=D>Eu:99r.%c`UH"==;= %ּ`ju\hg]Q@{*yYC61v/Md(̚gG>vqeRn_t z~s $Ej\#NeWFDe{Q >S nWz/e[9o \ݤSy}|}ǒfAG=KuՕ>PnrF'|;F愅#gCqbRc2aYwiT^:Xh8 㖑ӪpzmvWp$J5`@]fFG`!@H"ِRbx'ЁܭdSr#iɮ((TD',9sܶ}֮u@y\ыs2E{|As]vךgM1u^7㶦oJGpCl HV&9";urp1w'c󘀰_Q3uQbWѢ; aEb԰xJ|2}gƞ{jr%"!{@ sث'X99)8'8" @V=޾p*7u1]Uܶv,+g1(50 zÔN_Ocl>[u_Ft@5Ue3d6&ӘS=μeIk=R-hѪK%OxOOpǽFaVϴ `Mű1 $w^ KhNaBޝwaj"!D'd+%eBYgܴMAPEȭj?g$VqWG;=/Mg<&Hlt7ǡ@; BwwŞ@~oHW F#hWC>^Iqǵ:V0y۽:X5Weq nǚÕ|}*RJH{&kG&S6/{){3*~*a~ g+n^xdajv`j.,Te>8uf) Bbܺq4gP=fjg[rIbJ%sN3Bd4i_g~IAd N1 2%wMz﬷̂PiܜEUE&uאm1lL19E ~n{{+2z~Lgo'y_vAnCYg{s1{Vf]GA+%*L9*ptve"X*ssl7YvnԁT^fx1EN/rM Ҳ@<9 cHcnH0]Id6D(*}ND$#AfՈ;n-pq2*ar]G)|8H,*F1'~ЂOd3c#>[<>)WXd^@S^GJ1JGݽ+ۛ ~@cGIG:: ܮHʓнh=9~q_1L;14$ >0)E5WZV&= W9Ykbрjꄁ,I&)$&FsNBr 9&x'x(Õ"!*Mz1?" X  &.ݪkvq1xkg =N+xyDCrQ)9=nW3VLY;)5\+eJ㪈 +ϐ(HoHxVMG sHy'ϕ)2$%%%G8 |0rxtR]FJՎu*ȠBj~>} Dog}#|J=~JPyܷI12:[\ bXK?)_\G@,hGnvn{ZGb1 !6!G-Tcc>{\i>ϻnPG _(A/E0xCYr'f59#& N>;ٵv;\ЫV]9sq>~q5_'&̢ :m_? =M;^NQ7. [4,V!.צ!k\嚇O'*M<^J2BӘ/w\N]Y?)AnG5W#:]|hs +Rtfpp~>p}vgw rٺ¯:ZlQ8MK;'Hxt.X9o >v-QGwq"iTYV3O?ʤb^@2IVvYl*yPЌaѥ*qj |?=`됃J_#Wݱ>BPMl|-~9]e/ kw>"94"|R}I:/qsa_mzUlw:LJX>qE?tƮC!X(5Cr&sQn {E]5w7 R@S[² p B0׾;wKY#| |)}UY LpF&vNWu5C +j(V嫒c"}Cv 9L$Us$ @!E@zL&ip%Kd&ld`c!U;F_|ĺj}zř5|vpV,KnD_Xla M m/#Mٜc -HBUl#CM1ZSQmtK H8:Nvc-6l Y ;uD 3Vgȅ٥ dg[kpGsQVVh^C˟ {@)rII粿zGM_ _)rW>~\ ]qnoML? U+q!FszGROZ5E,w/t+u!M>0sXdYmoAt9 ْ'F ;Ջ;{ʎJwIl?pv}Թh3=0uRem&jn^4r/PH>wK+t U&YESbDbz$:3\%86&}r75wo\A]q-fs!btKp|B"HjL!|}HE&Wk4/?|6ecOo4WB oKI6Iٲ38Q$uKMZ'!|"y|H<۳{$?a4 ͞gU9#ƿ'opo^rA.뿒}#p+ 1+^:X>M0qZ!5&7q]eQI(DuG28fu?#@B_qRc=^J&b}Ka.8{-(T,: k{@ 2A @ȧUl#/Ǒu0L0XTGkV,}PbtU4ȑnRzF`.;qM8lЌ7*wʱU&9 1 @* E|;G-Nz*9vߏp N5`_UQ zGk s 2|F/^ ݜ8%0RRd٥1G&`BiFw©Bے:f@:gv֜7hfAǢdgW᳼˳g|佟8.ܔyeL|{k?~}ft_ te]u77B)O(| WUDL3u(6כW39X=s ( nv4t︪~Йq`,fNbzLV_HRzw3rѥyõA"nJyZ)E-NqPCŵB8gB5w:yK4Q_ܷl7j+t@]ώA2+1\EZ4*jH5nVaO  vDuC7с{< \J[V{XQE4☨{ !ڧYT IDAT*}TޯB3W/1 :4Q&anV:U垃25'wd275Q]lTЃX6hܴ"}aCYd?`!v9g{`xwd黄wG@. 56`زդs ׫/@|Rv!\՛v0tIbP6eqwIUl^UtcwE`xl vǸ-@@8k)Kņڲz'45Ub317ׇ4{ۂɈzj͐V&Li,QX QeWϏ^8yþjQ!k{dvˀ9%:^ɝ<{^ <VC6s |p%h;>Bts6"]_Xb  NMA&TV seQ[LGgjƉok򂑪fGvtnDpMQҚ]4jM=e ptC2s J 1զ!mlL\xg>61zgݛGJM/!?H4\b N%qd:թIF5Q*I' ̍PG:cyF?b8`ϭ'l(l-D"xᧀ;m/ y="\ ~R?&ppIVLꑼVCL;n&׫"8oS+eC#v;|:K^U+xsm:_fD;c'aǛ8FքNL N\Fy+)p{qСTe|]e1f a  2ȰEg%wjq]]Gdq;ξJb!-ʾ&R&L|?CC+UJѴ rLR†T>z)c6*[Lܪ=@\7b$M7 Gh)T#DƳQ**9ssHV9Dw٘iPR=G)3&k COJ.l~bIUQxxb pg24' bBg!x К\PW魷S'z1ꆪ3s}cxTr$Y'ݞ Ni< ij鄽7!LWL?|(k}*aw@abqCϡ}CW`1i]mL@6 :*RS۽ T_]jv% ۞ejs%K*fba"ƌ bb2`ɲ<]d>WuǕ\Auڕ[{{3rdN#39B?&D)W8.(V|qq1Ԏs!،壉J$`@d OA =\NBx.D1Ejt]W XAI)\$qL1F#* hq$UW)O$_Q sJI.r@aLÐ6.hSNa2ʤبu3p-(G>fSAs~UAb +t6qXpn؇)?7?1|peۄ&S&z‹j|ڮ}!k𱫦=*^'|s]lGXmvRw&oR@_ xSW/)|LZ1O쮁h8^UU~W &Hy[ۭ{Jj4竻AEk9*d[ϽyTS>BJb`4y*=g8H1S"ʧ՜ lY>$0X.,zҕ{^ [T};7\r?:jމRfS21:]LǾ4¥PĐL 0 ֡Z} p (zAֱ 4p*_olݯEdJn@Ѳ%ImYNDتNfy7VBL/rQ3۴=b UPrL&qdnG%ۦ`j-g7$')j'.zqϲ#%@*O8eÁnf.̠*ӿ{׬ V{fsG6G.1l[vsޙ=[ [ 4@`c6e[|lѠdl!={FZt=w,l0e$ ï޻;4Z֊͝'=['d1ǜ=G//u&5uګ|h(`A`' /+ v_/BHyd|:^T*͓˶zu/ncT,$# #K}bMk*1)#t`jo^t "-hDO[qqj\4 %SHdV&)|_! \ە&k2 4sCxUuJd2 -wP:JZ OL%AiF@⦁hi Wҍ d0]VyF;r3DD3!Z]tgcG.6C}<=(f33*:ieiD桖'h><- W9~(jD{gˋ޼S-H1L#KIgm| ?J/ Cho!qaVTU244NW E^Մ.DBL2&[N:(bD m "nT7 RwѦ]h =sAE3r7܃܃`^>>&19m]4c^Rl :HHRꎧ\t *% ]B%ǂ9%^br9SRs2sEs3 dp165k3J7c7;]7;'@l In\1,cN[ciS'k;*V7k܌2GGqD:۝fddW79#],y^7gUE쾐 N̖kc3xjaȣ2iy(;D^G~vOs,FR&ܬ~m_>~ !na/7x ⽈$>0*iB^K0$6ʵf~ X Y>قyJ1IQ˘@ le5PrrHAItLd IӘ38㉜nNh!*;V9$$~T xb h Š]<6*cx=T!mq[GB\:h>h(!Hl(H}!SǼ3ZڎfqȅU'nhQTq2/QpȜ=O3|gf`1c$W0%GW7wov H*iӠ5`!(OQ QgLӏG)Э9$ <&JC,  >L,Րsx$9qr-Dy*mTϬ./5ccϚi:hY zH1=x+gcx2$O&wH@g W~׾n+R?("#n-ѮމWH<7^)|4yl L 8`xdg >HxXa p C13$;RΞi)tK X>TS+w=ad=Sx89L \Ctm&Fmwѷ}3.X`6=h{ (L^I32}A!wDӍ0}K^HS'&PC1! na8Lv=Anm1 i$dnq2[3ڋ+*?cmicNO/bϹtˋC\iv{@2iAB>H෕B Eb8h_2 @3ʘB_x4O@|' p b 5W 4 2ѯ6үeQܮ:\=Y a,O7c_SW {#[+{8ܦHPN)ZQ,R UBҧb$1x FDJ BU(z5LLk"!`#ItyYm@DPN5W‰UrpdM{0ѭP,!810 &Dj hzifڮV+3vڒw&W]r.B|Vϋ\.!757$nsS w>i `z>`QE_ȗNv#xEp4-\}Z$}4EAhiU_O WL69e.xh?^gE$)Y}UcP|9kkDž+gx"iAחr1=RNS  >?{E#"b zU~nW2V N>F|wcf9n5u - NgirFD!O9P%60+bDs%*ݩN`HrX_@~PjmtvI6׌pxP7k^r>|r Jݷq,qtRg:j2ږs$-F?{~b7w|;-W[ܒxDz=5|<>%v˓(X^W?VGh@GDĈȈ(cL->ubL*U+WBr,b$g#Scn7~TpUӲnh*ʡd9IG:4nh-~UH(a@{dFn{GUar.ۉR#@I,Uk̆nf1׼IGc6pm L*Ev'ޅ*+j!@wB)-k,4s0i(O z!E߄&-Y2e0fiv\~ΟdD^W+pk<{rzx }u>IE筴zQaeH n~?)H?:;^>$sMv҈׬D|NIxN̬v3attCuj?&S׀+3#&8"MQ #Cpwhz>G|d-OFDtfټ.M#E/gmW[v0:vJKb|E$ IDAT{-H_d.P2LsڨFS9*//2tY UWfuź겳"|n`4kB,.$JIrka|rd:$Je|7|`R $:(E!s\-9͏C$u8#ueg3,ܷ?wv}k'FOpGl -Sj~ AkP 7~7--G/&"?*btưpCE ^[}Cyg6՗BL=&Qw`$`5-2ԡ ˜#F#`Sr[<%冊 6>C |L?ր1 g}W^xPX<ᦣ##/N?T;#+h44Ni9Y ;4FM@'`,kܷ@m_mJn2fSkuZ\RdܝDkJʞȜę?XIB\3U%*Z9++.9dzfUG$I.+4x=zim{|C(=;|Q%z{rIjL0|q FwvٿG c051풓nM;G_smPnl`R@ƀ2y UW8'˧F DE\[JN2noLg\dj(eM.CtU& Y< &/ףѣGqBz 1"棬SWY㶧m.<]䫬xHg+uYuB6>ܴw۬Ԋ}B4VHfn940Y%ߒa)E..d:OJ}-&D%ݼ+cμ̝FzbMcqoDD<\S0tUǻ HּrM o lǁ!IqD >~}>an4]ዯ%gO!O=i͐<+1zX5՝<*Ŕ& ˴- Y#ى(g4hn)аЉ.q%}|6m"W儠׶CGC$ \5ݫE$ZK) z5M>c&ӦӚ[--ˊYXhQivi]3@I`ϧaC'N`u3 wZG[[k%kðu`&o:]4ts@"K&WͳuDun'̀v Iݮaq*a~_@s /xc'RR[|Q}zN1|)S8KxotpA<~mxwV}orm5~ @~F-kO~ 1AR>`Iϛ-pf=2!(Q>=+2X<1MyҁI׭U ka@n'd1+ʙ ଼½C A]e0WvYwnQ~~LyߎBϢP}WCJ1i {); !Z?w]6 58lg,m VփޔEJ6| ; .{( dgX凞W9tx]I3ϋ|H1zrthyRT5=C}#4k[t1à#Cӟ8|kCx?>/c k˅!_#O| ?hB L.&?a@~tB]i ~Dڶv >nB L?:WaIJ tD!#Fh03 PukFQJ*;=(Ơ8[bХ>)_z^nWzoigCv=Ў37:k6vJ;2ekϛz^G: J,z|?@~HlID}4Lyy!߀e?9gԫcIm3?& ՟7N?2HV=4Υp vB2E7418>A1`lpuANSyQEQ+i`G<tIczgwvQipF(nD̉V4_UI:RT7@NGGCjAyd;2jp!&;^S͐r7j8 DOKkD%[kg<෰w?ФDqrEt& ̡c@i}qyﻗ·7kKSWJK?86 @gjE V)`/O 75 !>޼6Ì+%GXh?fRy^m~u@fSǶd=K&^7UU $}n R5wKnBY!7舻8v1 )N O!.Ml9 ~Ѓf+ࣥ]ijҎ}wZ܁F#C45#c*.U4^%DyLI3LbFw۴0ki̒n+&Sz|X~ +$!$N_9T+siLb)VݮINXzrqu|g-ُ9|{g;$O__ ֏A x 7HwH|MFt53˺ؖ Ȧgm}vs-STVr_VP}%ШnWIp<wwyhXLAi'Ov}tJ LW:Ot=|%G>L2^5S%.O8AMS)O jʚjbbTgpIToK57HX+zQ6Lb#Tr +7@9+yb$XͲKbu S ڏۯZPW_P>8y|"f>>.Y+gz#B@>w~ʑ+/ yϊπ 7ؼܦX#'BGA |TQܮ^l)H= P%|rv>3)ĠS>-b178mn7nOVMWv¾[~םSwPy߁rWSu|~)D{{ YB5k?W}rNqT[[IrE w~> @~{?M n-5J9;֩Kn(+jߊd  )t(W>*P-$:< :ZM]IܽE}ȓm7m8nOw4F2%{.G*/wG=۹]ʟe=hI༵y z|R]oʚf}<:Yy-Sw>- o'/-SdM'r 8ī+Vx)N&׾0ned43`1.!MEkJKӊy? #iϪ.=y&?n[߲G3_`؏_l>W.I}\fg`yF$qm|c O3! @`KKw>T+$]M>AgmQ z^D4GxXg9X'+#JR_5-[~-1*cqviwAq.h7y2ءO' ڨYGQul'reݥ eqES4ΝfOWsvU&O?6ZHkwJ$Mxk`y 5~w&`gm@ QWjc5|9T0Y+Bs!e0_zIВ=xz`UP@Qhi`` `Ft c! TN8x:O<ϯǥD+Ϲ9w^#Ng{G%_#rCැ [ly@[&G_$bJZ\ |&Yxh@Mx5j?>RPy3IҰf*lϧHm 2w}nnm㧻B ڍ*:Kn{ǒn<'mtnxlr:L>Щ>4[ r:G3GĽlB Hai`4GxPD#OVr};vN:I#)!2I QSoiyM>Qj?0%FBhhƐ@Hzc0 >VyI}{|Rb{45gQ1O/i|xU!h?ƀq$\90֝`}U<,uyM+^tk7Xbx5)O$tb95[JjZEDq>LZW_o~Oh2R8e'mg=%I([f݇G4F0q@D3ͲPN,ӮP~]xUU5t>a?}pqG8w,(&jg >t[U]4{;6$nAFg_-.4i)|/o6nwczUyW& Q-w'B,?S}*VP*ΞU*+ )Z&W!%|4h,~&\/d%iX_!u2FV]2ԣ|ϕkXRt?ӂ7$ЁZx5&c:NwpbYJʇ{h!$v8rLt&U t N!}L /گ!נo2*6;uL?ѪwyEb_}'~J#Ҹn5v\VW% zF*E, sa*jܮU}>q׊ϛ {cU]en]]yΩs+O u;c%aQ'E] f2lv)۱Fe^ 3[B:5 6DݛLf.+\uhrOZ3˹ӾwhioOs8 !D Ʊ XNO-O'M?{1k/0J4p<X^^r Tytl}|\C1]͗ zzԟR` *| U DryA| kB<~pRCt{P_YlodJ:vXVujJ%ۣXx -bK5!kۓ-t>1s|M C(S0d[7ǩ?#4I`Dsٻ$VP5VtcoΫMךp;ݧINF["d?@'I .# "xIPn~U Y3WS$0l*fZ6 mүK{Kg$FЀ*Thtpv^QMOvj4ʮW]G@m[U[uD>J=N:8kSEϤkq i.^E|M?gӠ)9^wisU&$}h4Y(W]rJ U|~])㺔^ctvs_ L{q$x>8XKmr-{"JV]Uy@ȳ &?!dOh' n_KKWM< |LYVݮ^dnU@G*>B{e4! JgBrj} >3/xfJ)l#?SDυh) ӱ_#WaM+"ޗv+Sͬv{WA>*r5A .ՂzU*ͨANЁ\=OK9llk{~vwljyH)H^z,OA&Uz,T=:Ssɇqy(=[ַG~>{RqL=t۲| g[KzBZ)X~/a/ ހxMamU6Yl:^6smPO>z5pp>*HUeIL݈ffF8!D IxlIk !Oq]jYh 0t vW#6c]J,ղ'fh],JI,LQŘ/N51f0RB 4_۟ <| O|/-u0ϮW%ӏ$nlV&M=O/o/2^^Hm|4SYAfI 4Ԅ Fz&Kw1s6g U|iD0"iA0j[^da ܩusbTb Vt |lݫE+wft&̘lw_+گ&R;Md qg'-& ZqZh>*& >{svqE~^ű;hk']qI3\BBSVI:K-fَԉ<;ZT9W?:?v|hhq&Hm:1-WJ>tBF[3/V~-`qKMN;'>ɇzs ~E&퐭v__d' U;x> E lDDw :@Iնgc4љ뎙M=傔s0c2ެ:<F3´Vk݉7\Mwdg5_u`*AeUVl_VuR xatJu+e>"תN,S*7~5$դ|h/0!>p{ ^S隓ã}G O<. 8l0{sMNlno1yv<9>!x#s 6x l  |'$~ RG[zhl,oxu y@ȋ|ǐ3 lӏyJ˚|_sjjl*9a& CJEL!,5(DLXΣh  }`FNT\C(kbXni23>MzݚQR9m;cuࣷk"FcEm>?(_8j[~V+sKyL#MA&*VB:݌\{KhUkvd]^:iw }b싴.X]+G{3Wל].H2[$M;_kNݥ}0 Jk^^< EA렺xkAEͨruuSv ˵ЁfKS.|^|O ;mrUQxhMe!#K7)&ٌ_&wSO;$/tD;o 'G$[/A@/4VB&O7n5`_T׫M;yP棂 s_o$ *Hfff3 48jj.AirR R@њrU|=庪`>ϜL_T`pހM0ZjD•2|8km)\,{}@>=ǨWXI'-!:H}1h"A"#5x?b=VOxJo/&>LF)}]? ! yC1Lo4 H0wVXSPs>^f˛>dp>3RB*zE#-P0!_ g4j+?rMIGR'H$Yu*Y|MHiSz~+ո STkO(z:[vY[)Z4J$($!N O}p%]Z1D˼MvK^xDtN^PaBliV84ftO&`}ʭ1TV,YxBӋs+K2w[iy?J(g|SO)=#:1|@|:_`$@ -SxWy/l,ujY4xqV_Ԕ*}.%EC*Q4'IX|̦jE^v@R'ȑFZ,N!"Ң4Rz.{ђ Ĭ=5PSizU3:8x T~F`& Dle&0KHR $Fk] CxdAb XJs)KLfa!P(ǸEAu_T聃zM삾\ {_|t<Ƙ&b!Z1 I#g6!;1X'Б0T$igfHݍ,Zy 4@*6VSBn'Sf, ؞B`~K@gd>7WnMکj ~bK_Y]8x*E?/޳Z:³..^>N|⾛g.aN]H$s)w@_@y C p7_>;?~w!pcLW.Xr [y9voKMݶvT 7z:ԿGgk .]֍tW|medL IHg<@D9.ǃuï/[uB k/⍧y^}RqH9XC~oAx 5ė nƷ/߻_فg@^߂A}`s`x'^IشEjB,Yx~[vs\j>}Pԍߗ:v5e{Yɬ ӛzjM+tI&-+@s?I n_tG*#,U A'V$fsv-(ve&0Z]8@`+A4&=M8&2J2w EItEcMt=$Ity$KY1]st4RixvvRȠ6N(" тŝ?O|g@6hD5K,O<|BؓWrm3_T>s]nW# *8D-*.3OA:}X@sz֊m[2[`J 4bef%!\RAM`۟7`"d zX|1  γ?WֶLcTb0al70!OA3"f< %kz+s@Ja̓`>p2aJρzsYUoRz\ R>u` XNYRf|M6I4\C e^3+״,/{3Hȇaz]n_`EOx\+[> }/dppl;VMzvuErGmGN8 $Fъ̵v WO*et J ]i*^ԱRC4s8qm i9 8 zPDH\5@`5D +a|'~J}dC]n ǐu¡8= @v"t/€Y*@lF`32"iN<aJ$Z_<3}Cx+ٳܵXJ: c0}σX`E`4Ou;)|s fc 4iPsP\MٜKipݳ9hNqvҹC{Nٟh!8j嗎TrI.Ӗ p6XD-nS.>'!3l?09-}@!7"bHL!_}F+?A ~zeӽի炤nB^mw_ު0 ᙑl{&1y"6/ࣣaͬ 4XT>V=蝳@kZ P#ΛV'6`RU58qѶaF1nD:jRΆ7̻sFfImDfv^f1C`Mু$"V#8ǐ*U`Ms p(Fv؄@l #H7rF ,Qb!Yq|ҴEy`RWģ{̡{V{G2ik-Eqвwc~`:1O:S$)3G4)bڢyl=3{yZ'..y}z^C/T&/ݴdQ%g d\/eG}l|O}qgpɘ餹ϣԄBd{^-/0>2G=[&)k/=M'τLf3G^m`y~}SSއ91N5t+- MwԤM {1z?QgcMv֫ |ѥF@1# 1e NKUwn>"]L>0YKj&06` @UOp7JdP!o<Э,e8qqADFr݃撹Rdk/sDhndaiI'NJRowL{NlONhiDKj:{ko7ϵYs`*sc,_Ck56(LS1!cNB炏k!2uV4S_ma䀬}$+MEPsz`6h2?&Wyz>x21-+6Z [{^S=Z*7P:ƖQ4̩T`i:AJ]g8*5fedڝB2]0n00!E4b1\BHdfn`EC!)h e.,4 !3) !i*_u;~&!Z_`[CL8YB5?`ܞYt^)#1%#d在W9rPkcr5qg}_|@q.u/8]a|g4)?|\moP%> 3@H| n\ݟ (ذocKokYxT]nf}&X6 S fX}q:2Xn:i+8 im)tNcLY26Ɛ)WCrPa."y~"w~w.ZN>lL )r.o"5k<!Sy @_26V\apnfݮ?jν!Clvֻk*5Cˮv:v%Cj <" 3h"%c"\&c`#Fs0}G|ܥ0ަdl̿7Q|:zOt%M 3ypܲm3눲hSLrM! &P Wpi[\<̪Rfw!RkYԩfTlqN͜XY^Ѱe`ZN&*_10!손! tlKֳE+;jףSJ29ht^x ?=aZZyF[>l|}Ǫ֗Oyp 6m$xfA|J37~o_LCK%< dU ϷbrЪjc7Blֻ0,!yiXfНe~,3@H4]6BYjfx^X$@K&#e ltFdF(Zik2mNm>3Nsw HtDU@LY嘄߭f:`XQZDŽ~ICCCtK %)Y2z!:ѵZ P/ kfu{N/vge:Fo7lQ={3\ 'cj@Mz"Y!a!elw1O@ ܃֊ƧH>HJꐇIΠ IDAT\SNJNQ/9OBV_ˇG( sۈ$SԹqdVΊ? >s'k^[$^3ُϏ,ݖ>,նGA0``ĀG0dri@x ~ﻷ2^ 9'"NdFfeխ}OVUVfDdĉkBrۧhMXkb?c!9|d:|v_kr*,CARp˜ 2gCv;4xrXѲ t @E L@'#[Cmw]CǨ< cE>wN5I^թU4k@W^I>дf9e-"8꽳9347Ex9Ѕéﺗ>SWlw7`|[{[-|u)UU`Z*oVUQuKW3/\MM՞\\i7g鰜߳qB/Wo ecuG i=o ;_{"Fs )!<)G]J#d_Cv*J|-fnWjXJ7M XXzn >VֺUfsY @­o|0wƪ4oHfN,)}Zv,Hi t`n˚`hc2`D` RJAȥu8$$X&Vb9,?T.ͥ]}t;]C3.RX]HZ*BFLN9o Ԋ/)~2kjr]݊MZ+R$LLṬUVFKKY fN\9\zsxsA|1$W-VhGpR;}敚n^yB|LQG79K@2MQ_O-o~@vOwA!1?xTa%:h|tCICW6y\&|ȑij1Ŏw+\gtjc>KX=<\̎ lL.H-F}1"N " "3,XgIBy5`8,D)zfAhh}Te:W_0KR,fl,^j3F\XG ExBC4 =f%7W#9ntd'F%E7f0*ڒ-Uu{{V|t3:0H:ؐE.]cAH&〺dنC,C8u|t]xC~:.(xk@̟FQ43. V@H^Puv{z1=_JNTOٟw nB+(uՄ.ļ')/OO<O@7G9 <`0t-f1"v/1˼#1] քNita*W0hb=JFnS,%Oo lx(X 8u]х>ة a ԗ(KEqMo3Z9P`ԧQUpvy]cn%R7w4÷>Ơ՝ra6sq}@j?4O. zJ4۪~7%ρL~>L~ >~c7Lcr*jGW\8]]T  롅f%B_Za͂1QgH]Ev6WLնFg,`,XɫD!DC4!4Q.ʝrAt7Gsⷧ=p!ڪJcؠf1[gCOv ``$J:Nps \bOkp),H PR$DhStuX rEĞ|ϡ*jHã9.[+TNSʟF[xLn|''0s)$)u=w;gH0.t}C}[3:hIѾYW_ߩ|z:}}v^<2;r}:&MXrśBG5MB.zʡA MN.՚|7?\~fosS"> "=#i/]7$5O3Sϑ?`3x7fy\C_e}d֣ ُe\ͳ>טݮjU7vW;P_h5^C@C4&m=;!ǻFRF ̼H4Cu!n !In*$Q-F#-Iai2`j fiA AC{m [G~ KG>8 G[dÖ_-q)U[;q F21 A`T.Rup)LV,Ί6ƅ $i*Y,BQq#&,'FJX1$, -/Z(yY)Xẙy#&|X`CvKo3A0X0tn0ojrfs]&TdGLӂ[bB8`.طckO~ )Δ+sIjasbsqv%I N1x A].\ ]vrG5 8F0sƈ~ tT eI;2H|b;8{LT!)lRxJO)L#5Ieυ\)vy*wc~.q~O`v )x[߅o}^`/Y B- sEZ<zk%Ḹ_׎҄+٣[muo $<:(u `V:wGwꯑ5G:٩`C;Pzu\mdn$q ]>:|>_Z~J҃ ~.%3JDl 8*i=9߂[#"A./#X1밋ܘϏtU@ȡOG=zu?|VU6t |T"`pfԾfЧ >4wšY.}xdiYeLɒRfϼch`1K9[2%!3yHԁC{NaO҅3!s !4tC73~~ɯ#_8$hEVri^9GJr}"1FIY$#hyr|2Uƨ ٚ7d70+dd6$#cbv!7b[Gx$a,Q:fyl. =iyXDV3}ǰ!Fub7xb]t^Xi[c0,Sg|ҙ%G݇HmbbVСkoέEG^yw !(uxw1]=!_  E7w},CvB~   +t]b:xxȎW=F7*c6>FD ~pιMU]of)U۳i&\`тd*$uJy#A̓sy婻=ىĨ@B4L"`|Bם^`/]ߺ`/[G_;K~dHZsg2.~J6>Vf+#Qy,SBsNRMHF'$01-+ASj;֚غmf>̍LJ,1^VV Xm ,M!ib,8bBq+x !`c-t{ʨ Vؗi+3޵U /s^܄͎vYPa0Nb  "H[b E^@dU4)=sqwcX7x}`^]"kJy?*C iOFGx*~g>xR  cQKQ&tr!1#آꡉT]ey#VժFjZ3ru 83+G >,تT8 && ÁtǐK[e]K:2f,4*R:p*֙t#O8e#u[ Ֆ%@X# p#'Lmk2 ]tݧQ1#ad6Ԙs'7\4& !9b 4)k!Y@]sOw5gUnvG=NA5?t<7ۿkB aȯNt$H'@W@Q$k.=cI(bf^{LQ>A 1mn:) \22؎fQc)TƱ'$Y` FctD7ьഀ$""Hb`b, t҅dnY1w÷.3{,i>:%/ !fuf"~>jAy-ꮂ4g>I@4Oe̚WfBL;e[]b1FǮ2` g~d7aȑ"Zf"%%|Xy!ŢCI$@hfnn&^d\_wrrwʲ*k>PpGY)aG#z4]몞Tr]1bb\5] nNߙx/Tt}6~+e/9 SIO~eZHiHkHHSOQw`}{g>uEޠpIM?ύsƮ"(+?s?0U zLi#D)}JT`!>|ϋ@j3Qk͇W8^e\!" h 'fiD'e p9g%k5<9mo$1_-,~UN\IFXc'fLq kO.S*I,F$qh=x<,Vb{#cYR7;;O\Y^lEC[r,8`m7kǫ;\v6 ZgddGW?HcJ0q*ڏ~c`e;Uao?%bCLJZ[fs 竭cxKE|f[b.F50͛Bf  .-n&)~dG !+#. U9@X pK K)|J^gam#yIxsm5|[nWTx=ؐP$~C3u8xc}tƑ+dP-:NE]UL!7 у䖆Z"Uw;0o/16ީpMy c#@{ڵH_"W>C,8 __0e)Lħ|I(Gsh?=k}<*=c'9A:=r֭jW|*\Uy屲U`AcܮVPqk| G[(A dmgVii3 Sr02f(! l܄4"$.9O=SYF!2>Jej;<s[kʴ[{@8c䥼9a,2}o~Ʋj,ցiT(Y%Lcb\bUFyK62g1q?sM3 ϯbgw^>ެ|Kz'=cK<'"%$6$mi!o<=B "j[Э;ARf /YI!%A# f8brGtv-c=vNq.v95Nonzs9wcc1sN(_dyto v+\HGfA2YU,8?NjvۖQ|^tnjou]39RcU|#Xpz&Z"H1͞ 1z{*د IDAT7)_u|s?.7TrMcHY_ /K忄wχ_:/$i 3 %lЋ`=tO'Ixz~uzcNIH~c |L djQ׮W.-sE溨°y,t#Q>mbLg4]T HYrU۵LF#L,jo1C c`LbeKW#BB]V"cH{ -Zzkim;@zpiLۛRϘHƇcò{XB_lhuwE]̶Y]6a8]GyCO+ǽLK K7lnsn D^~ssUnp/nPRN:]4>:wu1{G}RO,Hq9Hq{1p;U!O~wzEܸF~] n[jh|{?xr^/!Rt}_O;w f@='=7 OsȬ0 0ü^x8I ,t'PY1]i >FG N U/y1Tԫ1UkAkBOYPOY;p e mW&OhZ&w2J)#K*3xmp3 JU8]>pW_Ok6\@Lf6wNt]g.zwI#)nP! ,VI̞&`hlRǴwØ^O߫ț/7x+xCB.Y)EI6\Gc7ơhq"fL-=57qE|S(`)&C}N58=? rLjh{z㛮KWxI7揆:|zxx[?ŧLJ_߆a8=> >?ೄLuH+'+m!.: WId#b)ծ|X5/xun+oK-ܬy+a^؅m |G{uv=Fv|ln@\9N@5ZhHFAdHsd)69B>۟yfiՍ}FkDucIԺ@84Qux;[)T;MZM3QJ[.93Kt*t lMC܂ !JNBg0/Xà4 ƬOB1I0M.m1|m_[D^ճ ^? gtD^}]V4kYϵ}&-i1:Z5)x9-2DOw8i 48u N4a*[h^|?!{z}΋M  R=bwZR.4L=u0Z 6BȖD9 Y̜A~cNR]!x+l5˃c>΋4}q@)LE&>n^ٮqtk'kliÍc(8/]&(2՟C #rU6{i@ F,s Sb+!~<4 n!,tJB%,0ʤ+ѩ-ęYth,13ͫk}oږM0sshNʳg Fp{\6k Bkй|C QRMJQK- [ Q o B.=U֩# Txd|놪?>xp M6u e*O5>cौ́fG1[יc!HKyL ש(h Q3.Hj6|4ѧsZ5i٠%v kFSO}X[gnE +guX_'S ]b?wf ̃R;bQ&9Y >wѽb Ced ]7nhѝˊ=@ E `w 7+h!)`:c0.MfAA R]}ʗN4*(@žFwi3"F8U3 ̆ТXq|z(鯀Ҽ߹v{|W;`\G$ G0}ܿ -y|U]6C?`5bܽZ,*uN!'ă'ѯ@ȳ(du[4!<,<{py1 )^L,0I/X~~ib;JLb$aTHĩ& V55c0EDA@Sq!c$c FPF JY=;/}jkuhZB#I>D?B7?n: @c|Q!?@d[Q;ձOVe <q<xzQ+]`>0wj1r(&G^w9g-+,j[62B B4&!C u0_ipV kC,XC ?"m_-ȷwwk^_X00!i\x7_)K01b4?Y}Dw꒹G(Õ!gרv9wӼ~~j%9ϿΉr0k?6&df J-D]r*ӽFf`OYr 8#Qy1fr,pТQI7($h,ֹ |$]x~Dr:&Pr聾.^M{[WtUv%5G>4jwxL mţǯ|bY,}١*}wE>{ic9ŵ8:6bm Y1% >'wʧʗ㮃2\*Z&'+ݔa9n]$D`n!zqʠc, d BfY$d+Om?pYg]Mw3X;,UAhr˩AZ9XB$g{KQ0bCE !R,lsB[ 0e|CAnqi;:RyIzu}软y۵v}W])d@zko€0dQҀ(]xt%bLֹ!]D|-L$~}gs]~ߪųb&&={\8qF3$;FOzk}̻):L ?Oxt`Q6|ϙ9Vy[/3"&xh.Xű J硙|kV{%`ͻby-wϚz b1:mhGf}[ޗ&|/,t` vhLK qe5an/jL\9L}C-C8V mdKrI)g PtV!B 4Ђi+1!cנ$+n%۹gkG`X 7 gYN"V>tʔ,)CO92@8='-][saDh BE/N9݅.N0afJY!Nq4B מ!םnQʼ/ggOO |KJOF`zrɏ%<ȓ/Ưn؏ǜr>VcȣVj7Иdc kڒwqH#HYfqK2V乘H[/SÉ[6hFvwc_,ZiK+eCkS޴s @ߨ`x ܤ[ 77p1FxѹK[gJiD[s;E3SH4!*d 6隦FtcvײBiYk6ŖJCp `T!wYMJ.hVH9J.[d`BJN{p܎.?ǖTO] \o籵ӵ[UPk@apczDF?#Xz LS|"HH"0 pcb>4&?5]vVu áK kb>*ef $VߗQp^`{Ŧ/jǩ |ǯOւ\:.ThPge@`/lc=>q,T.߰%Fb >ءޘ__ ^W*b\0`zppCr @A g H<CgYx<]LQBnllёѵs^WU(#X>|>TbX|*6y!p~^е|W9<%3vZ;diW%&Zr,Ua- ڱʴU* egsKo~5hYpR6b KP >&ʃTJB)JI!&CtM1!EȓØڪ%Ԧm+<|ص}k~W\y0ݸ<%H5xbA}Nj$<{#|O$t8ad!ċࣲٞl+ H7-Syb= "8 pDJcԆ, m'+\u;-V{ iC.{joi>`kX@\ۿd?kdL\뱴k/qѿi4(nF7Y47vqf+}b?HJpԘz^#ܷr z(ĕF!,%Tpmby$ )]-䑅rY%)F7,7Py2p~5 +B-"D[st<ͣ|=%I%6Y#:D3tHRŃcokΨל}^HNXV] \-" ej>j*^16lnG^6[bvV3Ccj5 |CUϒQ%Hno&C^/y>DŽ\tu;w0lկ5J֢Yh YmFL%ףA!3PBBh621%H67l)DCƭ|P>p+I16X:#`@b|d;i1qBO楷XSSaZʲi_=lp.0OlXڮ搿*ڸR ٲ\*r9ojю\%w^׹Fw]3f{-_L Dw|9o_ћ $C BЋ|t)*f8p0  =U0 8j+5,gZ_iM%V.W>w#W\sy4R56BQ[_ӈRQɣf?mMdUVzVh~U 7 ܁EUbDU 302?hKһ8`1<.m<K{ܭ:3K7ًu5aHŏM% n1t}P]tb* tswiF>E]i`aHv+q} Z?tOPkBجsh9)YT4l:椻! 稹c#b %!sVZ^llpQ2+*wE7贔CBFs&&$˘b `Im6ڍqpdl\_-&ujf}k~%eyɉž{Vw <0O(3gB_?(Oy{g rJ!~2!xq ܻ=xyy*_BR8cCn,=.u_[}3ZjKHB ]$5 hfPT}L\ܬ鳑lHT( 'Ӭ|`© zq`' `٪W5>j?Wy ν/_Dxek&Z4=Cv%u^3Gc!@s'o$XR2v%مG&^DgL.V. xzv ] xSݪfj!`d8|yx4BQF}1e6>o|4$pim:XpVW~f(LhZ I>-B+KǒYXa7sJs,&qЍ_7f/|l+v=D40$ !N11 ('{C4C[]]ѻ(7IBhi\%4 IDAT1%>2]'Ϭc6ԎD_[[Ht9uѭh*j&xrmںWZ ˘De#dO g|dALLLbDhl 'GqJ_N$7 r57@%W~ |L4I5ϟCG!_up^)&vuBXOHy5".=1]+c!_ǙUbۡ >6gV.Uew!ʶ.8jUG@RC]+\& Җ¦~^VڐFAb?6F6ߩA\hC)ƌFa+8{5E.jy5,8_ >kh-N$?>EL9cD,7 f!1 bt=̣<~NFj5"*7#Nǎw\}G{ .|᝚6MW%Y0*9DKyܢdJ>He祥 J[*%nZ-d{0ik 1&]s7k5`"FUZ=c t=p+5#uG)ڿ6B N#RVKT%HgI~L!.Lx<܏ǣFq聮CpN|/S5} B&1 2Q[ ;7ɐXb\tȂ`'o,{=Zqh@%K{ۅ%$i^gVR>O*Bz -wZߴ x!6SpY0XX7Tc? MQNb [La,w;95T܍fZl sw'^p'! '\eli`:mgS#aևA(rv`˷dFEN &qnu6,M0b䒮๶cAG??i19dLvܜ۫;T |ÆuµNtwXMv8:+[[~o @QFI|"I<၆.ix8 xtItUX]PZܰbZ\>tq'-D+,k;کЭ_w{]3`3D/f&5Hj._hkn pkU~-3``VIh8.9eWw'6wwσ{oD&ϼz(&^F)Вs<%DuI!J JsՔpUq(]QJ{øǚA̘yAMFШ(#;Y!HXr3w N契Ȳ}R{ BufKF|?M{+ C 4dޒDI tiyLTXλFLŔуr@h@cx Ql/( HÿmEILisWF-\uuֳy' ?7VrNೀt|C %J'ɀg~av@du9tYq.B[xXa.hEUB @(8"䟥eBr\(B&;I834 +bs(Q=ځޘ\p<&ʮofwfl`i )_2G~ p!@i$ HH`4zu<$C$PFgYEi&-+V@j,ޚcZ\y cVkV4QU{ħ*ouhV_ {X#yY䷶OK S )0`/P3E v\G{;>M%rx{|e-֮˥j5nT(Fbȍ,]L>HSnRrK !$ f Es#RʰN3ݗЁ]nǥ+xߣhFz3`L(jCI""|ήH]>G 5%?a65[ Lj?q>5'$5 8Pxtg_|!و'3pcXC}8b8@Zu=-+3gVV#1e' UU> 'lܪ.]3T>&mifd w] 05YqxM c&F Nl[]D\]ͬ8w[x-^w ЫfݹhMQdD[j6o+U#dS'7$b v<L\&RuurRO[t uOGbVG VwqaBs/Q^=rĺ}=Xyc͘bq;*+Rx ڋ΃^\ s%$ Ǩ8cmµ[`y(K2;Fqz I螦$#EE. (B!zw!!]y>rlIfO[`v*.c&BIR!U ;rGCjCAW;3HZ: <,3| zȱOz}]fs@ZUzf4B.$8_f 0ktTڻE&,\1sZ-Kq1\g;uUi%7v|fNPW| {i4mkH] CZgٮ0{6juq][Z;ef>7Ns$ƔC݆!veC-2^ryH久K.u(p0t^.vo6sw?+ײ3<_OD#4F!N!ȴzMPfHՒsjBM3x =N}(WA$C҄D$@F`uEэ9=bDb]c iRŨZ4ssX"e,yZ LE#Fª$Ŗ6kQ*!78Uy1\$9פ;RV| O<SO z ˀ >7<==|:NpH|cf`{ɥ ͢!:Wrw>fLgd}[/{jnrkh_ p,]O4ۣV}D3lqS2if/ E#q؏[߅EIλ/fZVFnV/s݌1iA8NA2wWDHE =; ,bU,Y S2d17 3ѶVxCx̹Dn]_{€1BC ')q\ R誮!ŤpD29 bxE"BbMD"G2ҒBa`c2@4ecT'[R=z x }b'-H VIc0DCwĐe0 j}bxqPl]] 6?w;ml*3qc1Z) @~rB玃 G0<{2sxq|<{z݇pnOaRZ'jG |f)Z>f9 MlY 30i/#`i|oZ<8l b<.3sɢXube\3/Dݥy-cЬڪܳHu])̱ˬ2QvŠԅs4]Z;"t=j iA@cFခ.58 im Hxm"1f~oeqNݏw>sϩʏkÚE3u22{"ƜeX\&KG.]ck`%-#.ХŎ=,}{#cNjưn{pyPE8\9 ԖWQMrK@r)br$e,)f|Жϱ\Crr0)ftM=E#nZ$s23Dw ¾tq|׊$wpǝ8;q骣Q-4 (}!\TUz UXf'~ Lmc@B/(aJ;/~ňv06L|P7퉑P|$WMTՎaQt>w<ysj[wNǀxkK&V<Vlv)s.Q>zΗm7S^Zj^|LZn uV>Qt2d^ n 7[ǀbANg o_B{8Oΰ7}\N9 =#AZ#q\ߜ*Gp-*YqAZK؀ǎ^Ȼ:d0QPbW>Y ZklҐ\S9Za8H8$ qjRG N>9uzn}~IqweJCp=zI\M݉QYݏH{(XXȃ*hy\Af2,/V Aٌ,"].b7#_}9G _'[h?4'gg7ofx !ﶶ94S?x[l zAv=UdPVjG*L;1-;$3:갥ߍDպ莿Rowv12#A,v,X$?\܁#mɷkxq@~Ꝿa5?m>p6řiL,Cas)3`nYZO>,diCdY(!i0s.)f]sf>iUߘv~'*&J3RNљA" y!疬U/ HLAnA.cb $!U +(‚xu+cIcφUlb1\Nz2FCVD]Ю/)n+:4i>P26yf` 1:bP h4 ,$`x\ X~]BS@?D8' oqq K0c))fiʬb;c?1Ja52h h Pkda7mƕւݫƒ AD]A~BJg3 ei/U4z&5Y xQ >VMiQMwY|-];Hy2"%g"  ,͇7zٱ;h=Wi -L(QLsbBqDhȣp*sb.0|.K ?QCoҧO5د~a0K"J~s4Lcb-wsrTܯn`A DhVs_C4_`q-~{ J@o.z1BfL=@^cBFVl^yuE\ )i5$S5 ɜԌ?/B5%^(yr 禃e]CkpSCof?4AE$5w4y>Ͷd*#&{&n[a["}'oaߡ'i;uLXy9GlQAf), dO,a}{}d׍Yo orF=R\"87@Dx#6I)`,{"D _F'CBf? 8ETFN%#U|`@}sO}*^f,'[Fk:I_}.'A4$}lndWgrOԕy<݃GI BAWwjͤUMc?\>roh:­<|C6M,;^͊j 2NNNsҝfe+ d[ a ԫE.J jًhX~@Jcʷ}s?Qf ,(y'C  )q%rH2 5\s51i}VK @VK]YLexUia@R*lH XJɚG0gby 2-)hӡXG{<S@Cx3z}%ᖝ~,"Hp͉wŀwf6) NQ+?u<7WjvEt>FstNXM@!q*-y+O_fOЁZvLjPwvB6yD ШBt>![vBgЋ!k{fwwݣmQG]~|.l_y>^E ?ru$燏K[~9+ zp㔂͖4+Mgq=@ >hSZ.cktT+ٽaOx)YBYۑW#ѐ 8B&#O޶6e )rNI֨sٳF-'kҰFX1Kd,E-Fk93;Z-Ct)Q^Nw n`ݸR?vV1J0 صIlh3*Bw5-X#B8rݍAwt "h]Z|HZS{ J{c=姎OցDg)q\J h!`5ZbiLUyMcV>PiN9w9\I1 D1{EbkA AWD0DI<6my<z#A/ܒ\|k3gh֯TM6mY8A%#\`7(ctHϙĊl] }<Ӯ͋WR5 /(X jIU+``zUJS\?Ć譥K_>=|~e+c-%c2,%#tZ$DKթܘY /.ࢉA T|a8:6f =@}ɗ+oU>{ G,$/djߘ^2tR`؄0" eW/t2 @ D'(0_bėhxx` TG5zդou~>q!6XmZC }e>QfF

zUwdwejmjvR76do=۳оr%9@:=p ӭ97ُS>=jBA>zO:GpތayPz}q {W4\ˍ\Цn۞l|uࣿ]lBWVꎀ`q@8S [ot*٧LY k]c)$qMZJA; 7c$vWqF01%<E _h74K>~SbLSғ1 L2v`PNf]Xn)E@wT)^#u6\-W:/;2vGKg0 B`>GfQϿ/0p> #\! GiteS1+U, ,\)xRqSqB̢df}26[ uz%O ,__ o~ "JND# _vpNsI;?e#,_8mw5j Y~3BvcT o 2ҊE1h5pwRۺ"כ l ZPMs}Z,9=Эx=>:37G{/`FE-:RwyqhDhZhL&5* J<($YLnU/-%˾̮WKS)≵NH.]Qڦi;kCb 9pWf '%W#@bJnمKE$9P-%6 a^Y ѽn1Sy,%sa99Q3(w ayCd.e`47"dA?8e0RDDPQ uGqڂoƪQ´PJUc}y=%d朄NEcmELRhw5<1 _$~_Vw0͈&.!K$O KGBz86MC}ױkSuNtx{f)?:p~t}񰊚?5cSceG'<t~D e]2_߁?ZcOq?ub"{Fi\5Rnk!"Urd, -b}~`YdP-iJkGY 鉴6̠0{C~ d1(UC釓BdB2m̔j)ל7:_d4:Ie  5 _+hrqz;x;)fcs!?o0BLwSp+] -LN8`"B |NRSN2,#V КRP$:e)-qUEh90 qYU*9n;]n0pw@r駃xn'͍yh,Y0iɂj3YbW*u&*Klbq_(4 eL̲elI\FWy/yY!' 2(Hp,@PGq  VE3b!lp,[Ed pQǫV;Ŧj >_F䢒LbZ^i/R Q;.vUx9|aSúHH_N3i@'oT2hx?O'Lg7\Y2 qB46 & ñ[}Ung?&u;Z㼇%n``iGkenxDC =z0O[h TﴥSuP|pҾrBOg~1?wVϼa6Mfv 9cen<% I%[F U:XeHE>xP. Jaϛ,dmՁMڊgXZށϝwt?)@<^52rmU5 oI+t7I^ ўqqZbUAڂ3)d+[+>,쮬ܒϱLE p.0\@jbV0WI9Y~}1ܵ[WW\lT@qoT3=kX Ȣd E! F]]ǟ .*BKY~A !poe 2. r.',z[zuDjȣe k@zރ֤4(- ^V=ߤ&}<㰿s؏Hr:ĺ3;/<}m"!3 O-5Hט|N!\𤀅^XbZ'rR*.4!1n[s5̊)ii }v=;c崎@faYv(L2[ sP~nqZˈ_EPGHkyi5 J`` 2U傌;2C-Y_E zyAjgm\j8mp7?+C ޥݺ[F!@7&́~f).RbLy{NfStrhXr)iNs2RLtYiVVTvs+'04W๸ 엋5j2C*ַٖ)%D`)䍬,QRX8BG6{$:v T*'Ő5+,X@>(wz2Cj"S٫*TQo\\Iۨ#KQWyQwāK-6ǜNR2-fOfNLY\o!<ٗuki>: 5ca=^, Xl$,p q9Mx?Ox?GONN ;Oy0y/F݃l޹5iBJu{㊎]ZUގ}X y{ sϺeQh4`v{Ráݠjf@jcz8;E61tpe}t5m^ˈqc˾]O4WBm4ܪӰb]tn_S q5M%fn!GƦHa@J5&|98,yHɧ9Ӓ4&0AiwytPjp6}wGh*|MNQt h$3)J B4h HA0m tg|X9]-X€@R,W*,Ve:5PKTKu=oVĈM*p?KO@TL9 0i(w: \:!i<-$ҡ8ju {𱁖>Fcay1CzO#_ލNHD;=(ԗ3|}۳Z=q//K%Sǂ&.d !n1S&}!-̣X)TИ:h\4G~dA{?k?Sy>k [-bE Gϕ;',;KKH]H^ 00gh!11o7i=rXb8kv fE,e7֏Qͳy@~bt4rp> SD!] ;=x"bsMs }16E`c-ўG>kh ׂ1u"u=t{RK ׌tWxo.d\Lksr&UB@Ew`6uuJ#ey ,MaΠfWLRLRp)xo'["mTg{͔ 'c4+MBf$$̔?\}W&ŹtքpVzSߞV2JR{ϩY !, HHf)y ]2e gW/Vqh-@ZƌTwB6xUC hUu{VAQ{Z;MTF.-'{YKz%A bu^!3)c&1x+g9N8_/yD{htk4NˤiBqs2;P[]LH_!4uPu |A7,_t "Cvc" т>AIggv Hi|{Ѹz6E뼳&uvmvgN6|~c^L 2 Hl $ lJ!y4H)O ^ O]idlř#fX2S}?5ʕ'H%DACI@׈`{ yAHօH  %s4"؏>KDjvl+s,bs ,3[TA0O 6[ɂ@}-9Ȁ|h=6;1ђn/Ir < 9K\en"k뿒GXAQXURC2@7=v p>X.18g̀9t ~ad<;soxL8Ϙ.gL c7sEu W` G#^؊־7LUq]M&JTV}GH hhI4ፏ[Q5zQH{Mhe,60tf{zK(U J竻c~UM 8~<962^ װ0_kJS SpI]FZ@G!ADw|X4KCԌEY.T2[?1f8وO>ѵ)96{&_,sƸ,ct NKYnb:5 6KJ5K, !8Cts`6rdFLFj0[)(ʁ{[ѫ֭zuZvv/{N ۪k(ܖ ]% $H`dd()U|4e8UJ]M= ԉ8K8t>tιq  Vhaځ yhҴeS[UM V܈lΆ?‘7cMneVّ |t$ѿi1K'r;蠌|t"wKa/2|y51YqXjOOUe1*M&Pk\ba9&+k9wCo9j<zS64[:y6NJpȖ㪤k]HWا:*!%v6faUqI V2E%KftZpbrŜPX5rv9%#039c5,,l4H1Ys~ Q1[hc"!'M T:=|+:ˣbךڕXb?ʷ  /5+͂YqW3 N !9݌Mnucr%ya(L^ksuStQixp^9/x.O?H>F o `x?t{(v3Ηs=O3p)dQiUĽ5p z'/na >N=0{d0 G]%vzʸG%cWc霦v# u:tLr\my  p^($MҢ媶fzY $#M )Âs )$,B`)k?,ydZ\,k|P"'`R2XL\cl?%5plw4_jɉo& 9cd`Iɐ÷ͨĶe~첕2arb^eA7Ʈd"tr]vo?7 C#Y ߁- 3~o o!m 4!״2~u$<U 6j*;#&Z>Z0Zv9Tكv̬{K.*xȑ=j5 }0׬=kE#h,Jq| φl[߻q;i~Lj??c/; 7R4^S{z֠%-c9d6"7N N][{u9vJM!~4JCVu}O~lv >g߱S<} C AhOX7.x'>&x|v[FػIƒ  v˝V]5kTO0EI$)1B %@$pZ4 &%]~W>s;_y?@b?`/3˙SN< vfjBDF];^v/ KރqmZ؃r8bǮ~,|tn`܁>c4.P7 }{h} NE`ؾzMJ_3ׁTmGAhYK^BpJ_e{-^=6twix=7x9,ϫ^|yoHлe0 bKPmLjJD2Z2sgV8s"9-2/@q?lBbfp6c+Y0 |@h6nEq3&`װ~PcALI]0:U 0qS^%?"'<Y K(LH93*̖2PmsHh3@a <4lW'mr(pf\~Wxh2c4R4 o8ĻbNp˴!Qy_`7lA[5(ӄPTR# ]unTZ&wR[} :G_ ,;zہ'OgsX*fir !~my|nT,`3^Ïj({&'o5F/ҁv A_cn IR\tssυ{n3C&OђM3SSlHwC)7Nģ%%>x߸e-fݼ=jSd+\/`y&Q0*Qy DOn2-VYF̜Y9[ 2(b֙"6 *{YJ)=;ޭ.5 &J2dJ2g28 XW3]6F,T VzW.MVuDT5('EXs6OԶ.'{ɐq_sYQ,()9/s[J|f']!\I  9~F woĀϓtr.]$ uQY"nz3>gMVoD51-7Tt>< #cf:錰]FN (܋k $=ݮݗ8?^|D,| H|~v,ON. s1yD-$z,QǏOݛXOĹ|9 4')eFC>6-P}f^R=рjoi%X-(B6GB41f\m4 iXUΉ(:M WY<@XV}vsR]V Esaи?cRHљfr}_Ɩùn`-k_^V̇Vvu%ɣW*$Vg\Wnᖾh@sq9ՍbC;H{v$7֡ú!X=P D'O @]K~.0=y??#p s_N1*N >ݣ9pq/lf$"<σ+Pt*.g4kNN˒`GMٔRHKe'38saAPfJ׬/U9g(>_B9;C.>hܘ l,0Cq#LAK&N,qnƔr1^ `5#ѫeƧwn<79G #L#ʻq@=z]YVgnF;oe_c&CW]| =w|d0`<ʄxp!~, o=n6@so!@|>|9?0.^5ߪ_7;·~=1|C}8ޫ,^yqcvsHB ܯ8Yd50`;5 Qh2ǺgdSt5"}hʸI6"i8RhO=_wjQT_JcbkD{[D&'٘T`̮fً\5u}{/yAq^^N#2gg.2$&f9r&J~,?XJ/!zHJu Ѻm H8hm9#u[֫UR5y`ck^*YⰒ⨡LsBc[ۏ wC8R c{5߻d._x;OxBNBX؏prPF߁V'vBeݻop|4ϵO=oFsܺ`nɏyqa%nC.x:ɸSx G9bPtMjO#Iǯ{p6r/#*Y4JJgW}X('`PES 5&;XHNt6iz\7rhD!d)LsHqN6e-#)Wr w:L'}uJքFs a6$N -Bt!%!O`,n\xp1:.7N/i}s+3еK *),T([$0WMHdZ7j{=oQ@[VGݎh /]~Y ,@q7Lɥ*(i^翹3Cx.SC1 ęm:8lz]ο\6m @`H⸇W'othU>jAXTA|<24=[}K#\uqƞ!::~A9JAhb>onԟWݗ=%ROk>Ǣ p|WDD>[P/40&s0^5urEW MDbQʚ家cKBR27 )8vkDMPږ,=FG'o}v{" gwlQCn(Ra>&-|-6bh:p?f] ֛r ŠTQy2E !FVZYقC2;)_,'oc@5Gc\ܳ M҇& V9J{ L6$KZr ,>LAk`(Sa#ﺿ>Ĕ ZdmveDz[s̪G,#Y2 ,hK ti x <BZ @67|s]u]vPҮڹˈ1FDG $~{ȿ?۔|3ldPM&BE IDATK:5#P! bֽV:үWR`切(OJY,Iھ Qgs~+27c̪O jñA*.suqWgH=ު'%1ƺ#bjߛgw bPۤ0)*$d8?"莘zYL)6-tcp 9E|OB|[?,Maw_^ᗌ]Ur8'uN٩b4B0d⋂RFƅP)Xm-U[L`Gcׄ:$H+ e`)O+!I1Dp"SK}(>fxURwsҳwq |ԗ^ƴ>= !f$-^/4et!YۭHSS[^=r,+lN]q073~&~?6M|w $ixomƷ)HLFYfMo24h(Rm\u:\nyefR*v <h:ru}T.E`AhKQz|AH$4[E:SO \ 眶+U&P딖>MePr7`SBŃ,o6@ rNdu}f[jP;sJݜqJ\Rh!NaLsMYh1DdH,z d|*5kb GTp`sUA>wظ\.u= 0&>R$71\Ǯx |KRnƱZr]?A=A\Mn6{|N3BT 3g0Jqfb:8>W {\2o惆"!#}mZ6J15ꂾ>!Iil@"~J5ڶ:تg({Ӹ/ AM$,})ڻk ݿ yi 3}{y·nlD `eU-/Tr;2!iq=QՋB W֪;zRuQwQpѫwQVx A|LO]Jޫ`װt Q9Wf&8hUHp/=iyi9J>7[8„.%4_&i` [Ү*j%#N[q'7\;GGxsjh{~,Df#`$ KKB\Y9lsIT%>LQAB!NCpWPoz4{pk72px9:N7wl8,2)fU:Xrfg >*p"UMlFx*i&lެ  ? s4ho,Ӻ] m~((ܕMpCPA+|I^rȓ,Jp)Y{B4O`Sp->65=i@σ yeeL %֮8hnT- `2"LFhKUdv"w@H#7ã{WJjNx S)rIw"ѧ`7" A\ApJ@o&@{ ލ+ v@ l m۝^n䆆slTS/[ꀏ,FY uj^W~'w/|%| X-81j6we>D*(9  xeSAO k2t-g L"?џNxjhEf1\a,O|Mlcrkr" q7jGvbӓ2JC 1)SYVK,7{LcUJޜ*nEʕQ0/<~'We2[1hOg\>F6{X'*(XR$ HhdH7c[4-ΠBv"uBpA uK!T2B2Mh"C&3wm޹'ir|7f񀽨"1Ih2Mn4I9Uc@D55!8P&j0yev>qA3rBWBqt}tj8J=E*URJ;l. Fʽ~!zE3v,k#1y-*zyE TJTɆg_# _ttş,;($-j`0=1Ex}!jY.grUVYcV1!Ofps0bfSaz(KY]2Tf k$Z‹m^#>y"Auom,e[UKs Uë"jc $[ǔɺJT xr!&N%_euK,B$ *EV Kt!^RK5ZX"܃.'}vVk@n"a*+BQKl$ӿ`ra";f=D@WWx-V?J':+UߑEB C%)^Ĭ.U?ۛJ{ׂO>x,\~nu)T3ˤvo5r }$N>v@H<ig~KVxV4lw1G^XӵfF9hn|L#!X n` qss0+2 O|1~)7Y)N41RytF&E#]gZ>S ԿOXOlV*<͞w)Y?_a$:_T`1XT!#ɰH0D1_!ԁM kZgW{.I}&CA)ݔni⮉Z f+Z[fΨV5% 2M,GpD0Qk$l_ 3Mw9  k;G^ӫ~CMSrt2 2گ>F Z:Ȕi=i,U6H@W8EQ Z:`>o3 ~(`uagߏ!?-Fqx,ut+jBR4 Ő^@ft3:$)Q{3)vK \dfQo_=Hѳo/N?B r *ռ?qvYu4Ǚ0څ .>RYv3;W=H5K2& A&'IrEp1@ cVp*2l7~0Kx %&/ ~l{}9oތ[d~hi rQOe.{P5SpZQk:XWɼL[s-Jy"ӀkpAO㏂NbSgi 8 sM-+#e0Tlmn?WO7V8yn|Fmi)!O;+ܘ͍26%brAe 8[4<rs 7-|q҃;p9c/wS Vad:`\;jV<<9yn:<]-iI%MKc6gI.kKU܍e|+H gfsyjz9෣#Dk6% ̓i SfQY*/$Ia\!t4z񟕊R)&j/S9:?ph<࣮8HMOUJWE;]$Ngmr1}zx11q1?З_!?lU5h/$O(^ϩ`/Ս;"#A!Axw8w@@!4i6Lٶȣ]lucKWq?0o)$U.!4%{ƀ:mCt@Pmz/%\@5*`*BmQz7up/붩xdQ*UKԡ[nS(9W剔KIoKӄl[a9Vp!mWƾ)`"w{ 7sh_FeZ 5@|Rd\M[ܙ~6.)/ypaVSw⩵ntuSu "Kɦ«^@}Qa;܄i{9H]bʕwHE~FQ/V?WuoLUS<ᙫXխl܈kFU/ G'XR5Z٪ fJ#Wאs [p7VkZ ino.?K0v_n|j: HsՌ&[,l=҂E-:e^ )lR'+cbBVY_??wF7kd~OKy7&3``@Ȓb':M4]G֫܀UAsrއv} ue8Oeiݬez2*p{}9gESwNa pv4Dop}K㨖äSi띬_VtK:nKa_ԅ3~v/ʐp,2t=)G0d#`òCcn!(r,\ Lcr){p)Dhp 2w D~8/8`5~+,CBwq%6lXŤwu*5çTP5qY< R$ʈ+1Vrj@bJxoHi_/?g)f% aoa 0nBR塻$aEYf>'1F*dlLzi7*ִܺP.{ "Z\T$jS%2]4 B8e S\zBC$O `gxugM?-}47J:|At{0 IDAT5*CbA4^ / boE{r=J&~)!ahpI/ۯd+U)zNyZ`jV8 C{/`'uo{:dO*?flBX׳>TBZY j dx]V_+DϛV?<װaH? ~HrPAb2| @\۱YS+kE~{(fJzWE)JSM3EsS弃>x=n:3GvWHf(U d@ !8,E*n1ޓ'䲅+RFwV,c3J32KSRw6FŔ fՐ$h ƩT$79(—LMP]qȵ5-H\UJ"% v|l)zފU [r\ y7_ܓzD7LCr2Jy{%[woރm1sM`f| ;f|f!ldK4%G/bss J,lg;5Qe uf\?}W5mW<;׻hs7ips'⽇qJa&ARt3ɢgɖQ,4mI\a̖" \<4E9ùHKUToU%yzxjt]*W9(O#J}>UvҜ6A+r7y w[U؃+W7+WmWY#V`AE)]:|êSî&dɒ>/&>lt+Ǟ:͓̅* Ѕۑʒ^5 R+)+81N폜6,p_盛%..bjo8 +@40i2GxX 0Lob,1trU[=GcW(j[NuÔIɑ!@[MKbH6@M^2kք胃|^9Y50@ZWظO |f6/|@DJ(/vi2&@7I)p}@ܬcʦHyCozMBm-X܌ cfH1cr җH&=n1q蚿#>xv_ρ'Hex*%\ K#{E;|p6~U Gc[)gtvX]i-Qu.2K w,B'7uתG?,B@U/ˮxƍ J(GX+ S.:N>{5tqu@4j 붳wJi>(bLwPBQ~VP{ܘnן㱁Ԯ.¬ T*U}Z]ފ-|rz|6ScvdZzt>{ X:|͏Rn,ܢ1DFF$Yݦn(uD)_$(p&oh!Ză+2 FnZ9LSGehN?/WSn"ovgGA亹 J2֮$s|cc#Hz$\ec_{T2l^e=}_f(<:N)9~!+W4M\p!y%ϴ_i ;wێIoSS[ a76OzڰR܂ݴтف9U\WG ΪQ̏tk(Jۦʀp8+`Ӝwo«-Jp({Z֭|mpjGq ^]ќvY:rޠsa*r߿0] NޭzLؗ6ٽ\,H8rX@HS #GE)+;G"Hܥd* E1C5V0FK ehOMI}xx|-EktW.(23wV^>zcJvsw氳Έ&+k^sqcGb4ćr8~T%j*jȷshdu~:vveUJBf؂+?Zy &T4s-]4^{'ɐ[ щ77MV?N`sydz 9`r@J<sׯ-p!u(eV_eЕvT@~6J@1gPNm>cWVtrnɠ,\~K~㎜^{ ۙc=;=>{{$x xUH{ϒK44ѤiSp_dz)2,F0"Ov5P.mZn"$lpI,RDJIe(KAe|T=+%i/ςvrȗVB=dzw x8#z<&=G\گ"{Aw~;SD ^ՑVoQV>vJ_7UzYmq@x A1Jf&d XK H5Z™f{IciwX &NVE 7!0xM+ }6dkc%-oBjOtnҭrU LF>:*Ra09Z| [:mUUANTsй^ջt%5H#Pq&9ݐ/~@Q3~U(A[s bc )Td4D/WB.U(X3"]4JTwPh4#IǨC \lɸV_p6؇nU4s Bf<Q|r9F<)_M 1&Y5 Àc̰Lk 2p 2?>$ I;|8==<ܴ$۳^V%L:FԚmi@ @Lfod2"$eD( 2F|KmOdU܏YF՟uqu`T:k<}pOUҷs0sm\k6s|Pt ~OoS* /90vv ){<ʽAA3%b &ҥw%5|2-|f Ŧ̓}D\b|R'|;. ]?0~oؙUoHG9w5t$VRKn+=OgmZC6%p7 Z33z@dꇋ$?HD0`o\xILly< 3H >c'Mo!'Fxa&L.)JspsFMl\5f|H>#"ZwoGm؍u wkB溚{fW#j¡GqyxOGx7'|>`GlC/Jzypw9zZHE3d6!p2̂O[ ئSg8^*LЖV+De>zU14 A#|թy\b9|;tkWpdnVSo2v,g=ي^epV^*հ(swQ/Y7ÀHo` >F`2 hiW:۔Ќz,>"V̜˹jW8T[٫qWel6.Zj1YQkpi\. ܸ `T-s m5G@#s[OD.<9^ũꮬVB^P95_9کog!oG//_"hn*گB4N)GwV9++P=}_ >t.,LBn<=NoşWX.ߗ#{ZUr xR \K#D%$+ jҍՄڋ+~Lip"Oi\|%Eq^' l)k<}5:z#3))rۆ3cͩ((+{*A BX:iEVSPGLB ,U*i Tc9 ND;28ҫ&LsYEPCWZKȜJk֣s7h2NɿLJVJњa%4QapJ+Ai:s7;Sjn탼7IƗwY~|Jڍ}y6g׿,jLGÎM9@< $n~![8 B0/d灤H ע7,4a!&3e\蝣RFW )=鵉5"p׈p[iGvqZ#R!3(T*Xg8=q(<>JT;Uc ʱp 1_ 1{4yWr= <~+vqSFkHW怹@_T^Ww~Jh.,f`€T! -9z-)DLO64,@ A;>Os"%f.Ug "Gj]Wlz+rF-Ei+xW"LDPwj  .$tAL & ȇw 0قf3҂`e|*Gr%5ՁZ2K+x!WƁy{UM/OQr7z늏Ϋ5q:YtyW9!peMffp7tɾjIh7 ÑW,-;O*|!Qu_zt/n/i\)bNk̤PBpW]'d\Naܹ6[^-v~a#pҫ ݊+_1a,%S8_B|i<;gG8cP+`Eff)C!3!8ۃV }|׉ jq1gF@}NV)&k<.ז,foJ&U$/ uK҃A$UK$=ޏG"  K )`coU~# IDATs:?SKWwoRkUИ t%nX QNTW6%#"LoF {t]"Z"gFvYO,3O~ kf"OubL.]a?v@lx}ȡcgz 4ѷB4OUݧ}z>8}5>.''`Ǽ?vXM] v Dp{hOݧ\Uܕ m+X^ =F鈟\E< c}1j11ۺrEh5 t :|gI+UlGecrl蚿:tcސU?tAP֨Gz;W;7sN`R`|DiT 2w,6+!WW&Ȁ#`̓/tG+As;~rc7i: p/C~&ϝ#g~?G/2HYan@)ia&I$d:vΟWn9v 51۵ل\!\$R}&zrdnW'k,a SRšI0L҂U#p9b A:(gaQI(:mbgP{Y>!^9 \vGmLM%0I$r#YT'>Cg_ 3iTtQ`Iug^u6^Sj2!9Xx kcw-ָy&pFnR:$!8|1J’LRXkh/8(@lPU.%1HjC$Sz<ё@CpUi( ŢpRϛ_0$K3a50s_[a"im5:qVg]$ë dWZ"~* E!dS`'MSH`B LгUmv )2G#(0^YU}(7@WeIMSRiٲ*LGKb:5#%B븴k2CPrU)DW1e@m{'?c|ܟ `~M1G^&5_w~._*b0sȀ ]V &P#ks$\`c&%U>S]fY<e_V^Pjo[wDSe,QYB€ })L_|@e}{+bmcoIMj{qzKIaiJb!;pIx8̨m$ߓ:?UGWvP2ZPv KpOϤn[nfS^(k1`5kPUƛdįjm))_pm9iA_vұ+2Wyp ןJ'&we߯}>taxbo#wdF7r"RݜA| Q>lY)h?ޏ|G)v_֘`܍Zl!xDG)vŅUu͆۩M &m;%2`n qMp)+ 1A $"@`o o o  SjҔ^I!L`K ,dW'Q<E,k|Z̃[j ]`Dg__k>'%B)>RoRU F*@[Й0&h3IHsw\xɃOri`tz ϽmonӖ~ bL([S"Q Afuʶv@=}o" 3/^=/pxU|ZKdH-=ȓ6CL2ZYdO`E2}w^LuhJiuqPZ}9UmZY{gs*:&2Z__eTA,`70%G0 i:]U,WTuyt)T9i7Y-Ics+[}EڊDͨE|HjXqu)hb5;0 xJ+vz1LXՌ6{UحJy}@ )QxϹ[vR$ J. z-u: V[io.14V S޷ɬY+L YySkhfՏu䩯[UP BVFX/9l f a~;`:ͬVKuExPJAƽrGMz19$5Pݪ7y ֺc|h$Fd;,8jOd4!gcuPoHL|^_3}'8=,Ld׷Ruy<ån?^73ֽ<us_x!tG"Ԟd%Չ]۾).8=@L:P&L03ɍ=0&?m$,ǣM?Y1աH8VK-$:|RLC 2n 4]3knDrrnJaq5SxNAm~T& +f-ÍGCSm fgԭ Gn &V#|QUvު=N:3SDuխ͡&o*:9 2h J59'vW(/ۼ\`#*DWrRI $>~Sх/w⑵ul4mɪ2%/UB\Y)#@} t<+-`j{x 0@^5)_c\וqՃ( z+ ɼ7(.zU`&Ř#Nz<1 F@Y66E\B-(ޱ[ȴ8JU #etTi9LւE$TQ.o2+@J̝2ZK\J3PV:rkQf[Mbr/z {.zѪc2<^TG-WGXvXVwdB8S[.hk(OY0k֎ u+[|g> 8wBƽ@^@ƨy=:Xdۮ 3" I0G9`2#Na~Lfadzhf1nTR⢣F3v>϶o{KWrX4X, +\VnJO40%;8MxB4rېmwn_>FQ>f}<&G#cL_Vr`a]0yqp$rj\D~C\90E{Wˋv8-@ATV@yc![ժa 2eyz&j$lSϸ԰AI 2e 5DӏI>#T{TA˕ x*'|tyJ.xJ yTUкPy2u;fpVw^rx!k;'A3O9^m X#+: gbFڥlWP%"V?y&+4yfmQ "=#1'Jc0K؂xvgNEO3n)koʀ:%=GۄJ\ɚ' . xڮf}bG2 :X~v}֨3ۥ0N2dn:|ei6 EE7Y|uEGiVX"+҃\cv%[(֫Kւ* 4f X^edU 5$:e`٧[[U9+ՀwzRmةVMX 2E< KK-Wʆ*zS*Gm^X9ǽ 5gU -*s\N^[]dKpO~ΗO<|ѩ)zd+/d$Br#;좶m]Nn&RpWLxi|Y !|Rf 43嘜q+ͮvK"y%$}%c36+ 7T%ⷠ%PI\J#1at'}q޾P3FaH#}&vt-ҡga[޶Q-$s+hJq]KnFl K%$`$KU$_HVT0& uBC%_Ź@T>~c>FTllqFOZyGI8GA#q G]YC{%P)j$g[֛|\Y5_ nV3z''FGRZ$xd6F٪H$(M2E,׿yZ o]{G4QFN R M~v;EJ?_ W~_!K||4:6-h\9QqNd45+qVqbApGzu? Rѝbn v.̚~V[z XN1LLg{\H+X5>,fٿu qBTTSMsd``k#Uk?^屡6]![=~u䶭E=R:.3^ݛ,I+Iׯ=s0D?WE] >P"<"_y.XE[ފ[փ|[: g G`xtoF^G@O=H62@y UJމ=-CNwt<~,o8PKc ϥgt(J#uRW b):| oWϚŔQ0%! (og2UAM{~^g#) wDp<B9 wpYZ]/^ت%(ɐ.-q1j*"fI^m->[pxA#G4T2ehBTYV1 ^n<ϡ&K ) "E_.=yjU@(q2*Iuy߽=l 9#wmUs-[RT㽞dv"fI2ʥMNh$[va||M+w䝹yO^nG;ȉ>?L˾b!*FƠMya-{`HU$gF[Aj@Zj.hSlׇ>>A#/UZkXS:"s^;ߣZ(4.d|ju].,IIQGY =LV[!› ߔ$*$-7Ö3/%zo10 BHƠ10FcJ$DTUZf*Bگ4TTM! ,'U@Ȣʬ,Uw Fi0ՠF\JjVj0k={yڬ~4RgZe:DS`xzzagd|M{Nlof=$X_r,aU|v޷\ҩVJ.Fs-d j!%\EhP )Տfĸ2X#Ĩɀw;R <:e6t;KD!oܐ۔ *jiȊWJDJX!F$B& ?Y .6QJ"0Y~Y"E?dI~AF>sђ9vG"@VA<4UŪ>I)GٹpIq%' >*} &<-yVdwsRJnb̉[ 琰mJzs}sGOM>iH)siRZM-F uZ{0sBMkʀR5 F424ąj)N4b* E"+A訆>`#ݮ 3㤔&^J(<&T࣮|? )ED02e⹤RU1)YceRym7i? LzsuLPe)nV=U.B$Th1BUciW5:ɁxӘz-X@0 @,e1AU <@ƃB/ltX]^µfuo]VrvBߨօmݪxRx斐=}րpl=0?YE oz骊C=˚Cw0(RUM͒1FUSNQu7 }VMػw*M&kH1RkK^+IKQ!*TH UOݔ~hR@̓8V>Vr%[_! >jӟ{l:xT$9иdիfAX\΁hlJ B+ zWuid'[;M ?4_'ӖiuZ?-hA Ac3ԨҶCs{NVͩv+Nd̆`!;_A;-@[AŒ BkX ?8Äi%G=@Qjh^`c0/"Ь ^R(v;*@c $ƅ1]%EbT bQ@n,YL:20H%c">xrptijz͠2)WM%ŌTම1l!Yv<@dC@A惨d@ʳ>^)1Inu]0+pO/r<']t~/?0D )Gsk?@_:D{ϾyluIMt~:>/Q7TA?,[Q))` RU =hp +{\K~{4D5"XՖU+h\ b@⟺b`Qԯv?~?~-joBc`F k X(2Z:^s>GmݚT#ڦȃEe':W\kM:uo[a8@X/=|Z9G񽜖1뭘g'YWONsLv\UÛoxJ)~LT.I7?LZ BxA/!DBTѨFSTBĆZ]!4)N4z%MhXbZL-)U$n;@L֌wz{Gyi r8xBSf>z28kTZB{ͫpQHE4-\-{| {M51}@?4y.S޲zKu6OySW7 + LiF9~$lޕqШ n} TfW& EHPG"ļVjl |u# y@C ne +/Nm)ۊZRrQ&~uhKyőWC n62H%=ՑQ.Ewۨ.ӿo˩)7E*[RXMHⱾجLbA$i!XT*5j"m[&B͵t%kwLD]1^^HVXf/Yh ]5*e B% ʔ3q<ܤ앜ǥe=Tܴ|'-aG P`AU@#="Y"{]{UY"@KQǬ-)bA0yu{k ?b #&MAqgIfdFҖl:P>$%,""˯/Q_ D NHmS2YόNm@_}'ؑgFt5,z9i;p~OZ6:< 3F0!׀ '.2zZ;N%\qaŔop7L iTdVÂiPS DyY-M'ѩ+YS>JrZ\V]KLR$sA$~+1"mԫݴG§;qtC XdjFSwuKVӯ;"H,ĸ+{},2KpާDZ eb3xkDCw9ջsHj#*OW q? .KmXkY, @D YoS6cp複F9-OY@tR{J~>'Wtڰ3?x|s^+CRLV9KU{W>AxhA>vopAP9пY>?b·VH)A5djѕTf _yf!]]ܳG4j$"k4–1DUf^:+ɌY`x1sV)5Hn޴Va0~\hAV+jWhvِw.R})uR w@Y~\u%? 90W|%mNpb # &eYݘ!9wq1V^w+ LG] "t`qQ3$A5q!'8ړ@'v @|!-+ G'ˉ떫-uoeWEVxd "vBk[$xOg~_7 گxIOBW~L>3iE_//: "0ZTQ 8(MWyXj@2]aY̖hX$oGSn [Ggs@7m{]_6=UAD`n BY b$_DKJWFKUK1T.~|H30_8j僃n=hx\t5(Z59]TqUpH(|p©lG2Uc%IW附/ǂע12Pz_U?|j%гb_g#ԛy |㶌Y8 9|>G`7C}>h"f&6^qs? A}ȳ(uvK `c~{®ANX.gmJG%{cѪ7 0FR!(DL`Y&]2<ú};&>k2Y("Q"q j%.Q !!TES/VKH+jj«,ksѹnoӭq˼a5 9H4SOKy @,aJLG&"GYja{t$)-MPBJ!.18LGe\ ŋ 0S7oQhB1*T)!4gi&Uf}̡*Rw!H&c/%_E 3SfحL7A=ِ`,rK6}i~k`Q;{%lFTWscw-/9WXumXOtrK8χ? P>e`Ζ;#Mx`O1AxR {.0{/ѥ]| ,ZSz- UhQQUUcAEbPԶ lę 0A7hLMؽqU]겪~EYUrU֏9yeV%Mk[U>5U^Py!: Zg:%cTA,b\lAFf2D㲽=J%CJ"Rؖ,.T= 5Q4+^X31^NGL!<<+ż|4KS D2'T!Slnx$8ult ?;EDuQj3n9il ]ځ!Se&c^CH;`t8s`Ԓݏw,kZҬnn8vGȪW<\؁}n4Wĩ؁\i\|Ȏr([O)xo,>yk]:}bΙ|$!f.ם b\ Rmn͟5D%`+ qQtY~QERT`UPAtܑ;FϨc)σ1&hMQƙ9H 8-1fBD Q0|@H"P>vG!$|v =a" R%!2Ve)^O+!V;})C~&, zcE.vڀ%=X|d^Q2 @8c%Uϲ\UD_Qc4eO\x]NGgN@+V.xџ=tjbo7I }=!. ô5}sz*UY,Q%.%"~b>VTXl>`}T6dޖ>8s5p:~0v&5K1:b6b| f*f{j^ {bc2 MMK8>lj֬ 2;!L[ÜyB&Q=߱Jo=m$+@@Dt1B4-j GktE[oj/Qz]/ogQOz4h^m`̅t >s <5c٫ea} F r2kծpaEC;J?j6,S7vl\^'dv儿.Qd"u=6X@e5$/\^(Tlf%bKqYeBQ%u3|I}o2UJNl5 ݎasw+}k6+]-X[d֡vdo NQq{N@S {A.-܆(|[l*)#`!d|@&,FXAISf6vnkjI-dt䓒IJl:8슛"9+2d]\W!'xS1JS;. QEB|/"KDĨc\ -P̬`zpB)6t8W Y+W]bXWIWRAM0dA|/>x\N\^wxjYm _tr[64@6՞T@kQUHhIj7ZHd <,!PB\ko;:YؽäCqtэ3.V97g;?:lbD8nUگrc G0(Ny{5Q%GCC0GoNv92PNO]+ޛZ8A9'2w\ 'y5{iŚd@Av^G2|ޞy_^x?R+/8%^w7I[ dٸޘ [RgKPQETE֠XW5 W4,f4S YzRӊ칪:mDBO-2ojAc0կ9ԫ& jm9 'q$7;vITwKGI@3ht:l/uD7GhZ~϶J9߇@YW4Ed~@ӑ!aWjlAl:\Sc*B8~x@d֎u|ՐmNg3 _'rWՕe Є[#ɴR\p-X* RYJyjet* 9le)jIJ.̴74u^Sf<+n |߯9~x-z\9Y G@j@ ;p6?V-!˷/bkMSH3B5{JЊ BF)F/]/+E[ 3wDJ@sttKb^cTB^~ b*xI8'tfR1[*?^c%&FeZ /PD8QIf!rpE|[ __RYײ԰ (U(4 eѰ$ Y LzAVOͬD>* z-:T+x^#\ 6S@m\lpe )bKeeSp۽Q98 h3|G[|޶ m}{-oxzeo1hcRnz~n(5GnZ0IJLUȪ.C @ j*0UբE(F(b QX#rCeYR IDAT"Ϣ JQDuY @#jD AKy+MvrkH?O:OMd'eN-;X!>w~2;![#C"dsޫ(v HX R%(oEDG&3ЁXym :p!-ZAAbìЌM611!I&cJ,VǬ5HY ]T(ۈF~G.H_(ac.=,M[`&UY8ޑܷ]jdVyH&9 <>7>$^!҃z~KVMn~jbA>1B׵ˆBGx>#7MSzW `(QiHSTH .#`PaeXXs^COpPLM$)k IbƠebPJQi/xy%B|@o坢Ζ`JM./czX ѴfQA7СL$[܆MYT7@#yr Zɔ)Em $+]MCWB|l6JvH4_gRW@-~Q[ZuV3.FēPuCz⥔H̛zj neG^uY0> q7dq/x9ƇS^0xps@ryr7(88Ҹ].UBށurGuJaSHE(Ju.3I{IH/g) !Nv6y.9,~ L$Tf)CT5.q,Ġ|.m{7e&BvGRR+IUdhT=Bvꇡ IFAz4!-(u'}SY!t Z2ľ&Ou+|4&߷Ա lS+ Vn!a?SY"`ݘ;mF{74120yo7ޛ/@ǡCmi|{X>Z:jvx¬ .(̠-mD"m~]FUTzfຈjF5̗*Sj({g!ucVTi}4ꇕG~mczzUMX FpHTlDBŚT)_U]،Bg6t8ݽRxe_`B*r>HBfuIXɒRDM['n!7_̬՗Z~-PEHҬfg)?xMW\ pzUR2& G<;xԤԈXP+teG:`J. ;n;:&ќ*7M"c%{oanxz=n?xU#P+ C!&y+y#| XD$o1PID,]GݔJJ5DAX)K,Kj A#X7IHH9:Nigv^Z ;vPk)k\g):|6'bi!)Z^#۞1Mo p7xѮ츙j5 [ mgSkgF;l.=Il\PhUPy&I(%̵B4oi@2-f``YKFSK_)}%ugaIsgw06p7#xP⾎95j n~>JJE(xhMQF6W~JBchhA֨XMemWgl #ݨPii~Hn*CrwyΣnǪG}*<)blN|> PZJP'{X*d1Q@C-ЊUwS q̪ YB? Q(#×udS` 謑}+AG4m5ҷaq5nCdo u7gS!T]U8qԠs|&'kc'$CS.y\Wo g#-^=-巡n:+?X.ͭΰ`HѝD+W57[i劇H"6mmRW?Py Z2E62 (F cTQm !F _fqEH$"TLMLtҺ̻Ոc_UW~+ͷ"&ހ"3hJ`*%Dʤ T8rLqs uQ!fb`5#L&BY!A J9 6^%_&0ӐGa!u|_UJ$5p1вkjXcVbw i[xWW;`U31Geݽ7ttՏYuwV8Y@Gl]vw40~I2%LrNX TX B?فEd+s{|X%O8MDj@_YdP u~Urd7GjB[?׆S9+hGst'[[Z#{OuńwZ_&gdy{voikj֐Lj bFAt5Sn2܉ Զfq+9=w$l`JY! 1` q *@H29oQ}2<=F ! WGj&n>JDVTxU5 M}mk0p&I:| :V ܐHxCU' D*0_4Pph4Lr-unCh"D5g K9+ dfFq48doټ '.&>_FW mWa#;ަ蚥_PQEoE$^_%rpV^qķ}B\3zg'D% һn٭@ũ~`^J[P/%Gi]Fl<2Ml#za"_$3`, $L8 [I#$܂,n BŷTqgDkϫ]/;ށ3Ŷ%})1Jӵ,5ad+^e]=ѿgcS\V{c\{H}W|q|w +8>>=~SRu=;iIFn⡓RlZrMA)P#D?$y S dX {Kתcyl|TqCZQ]ӏ$E M7J.ȂFj l\Q}#ˣnyu mp nOve2Dԅ2W> $|dݸ}A֨X7EVB-\wLh<Gse+7BV!ZipxՏO5o%em5w*B ۭt.{%]qX[E$"g*enǥ IG$wi:{/6_^:vVi>qNo}ϊcn"$e!i+ D!!vA-GAI DMCѳVle3Yw)WpK1?sK`PS_Hhw%7uMs J5t'#"p|;V]19,T听$$l41BTIj._m:d)_8lɥ5p迻\9Uj8 rA !E҆6#ࣜ>W*(",q'h59E0B#s O>դd!B"Kf"Ʌ$`j~-Sm=?;^wu_[f_ !8T9NT2PmycV9l';Q8' OœO[k-Hxk+,GO#ܺ>vN{W]{.u$Pۢ YzjjD(JR*&&I =A9._ KB(jp MƭU֠F Wě3%3>y9c";(*%rq? sҸr:-W1`?f[_9nZ X<̱R~|A^NU0-U`y}r~>\Nگ9=L?m#`DCjM5 MJt6]mYcB[$nmDO:}}ᤋط!_y^Hfrx5Scmc/bjB)"Fc[7t`HD%&%Y-HzStB/r,sܛ+>?(˸؂SնMSJ%{Uv5 c97#)YJm '?DsK⿐ǃgܭOkpaf-S߁e0B5?4b%hAQÉtV[焵 $x-b)@ j5,kXcXd~7g= D"z%LL~I;4j!p.$X{G~/u7Zd^`w?궫&c^g#~:_ 2֣a(4 ֳ]a$zeɘ0 掩H2 ,ݺkKl > c%u1ٍ#}"N;透nrdSM9kjXSh84Nlձop\pP_X W23(k|w@m<k_wWG-%7Vc%:?.SvS?+Ud` "  R“Vjz%w lZDQ׺,k˪0- USA~~g=&~/ː.L4?IbXH!&rlP: Ju&k<,iZ9p?Jr'V/{P8 6#zPe T8dOkAٯMJ[9{Xdq8t-3@FShEMR{do/9VAp-aYcB̸KUDD~_ZIDׁEީki64TIlÿ94] jՐ;ջGRI"*WlM 3X^Rg8#>PHVw7=ޞ{!J[ \l{F@qB{Ux(۰ƥi^HJ=^o:v@A*w !a:khpm4NxjX{1U&d]u{Yk]udB""*J1i;A5AX2}q0!..k3,̆b FD:xک[ځLnM V^(T =dV RUeX5$n;uCtW^^jy-g@C ڹSZޅ9r&s@JU,긲!5 6s* F ݡuoӻ+/w%ZInlW (긥[i5lx|LT`@3̥ i炱Z [%6 A=0-%s>ǟ1.t|$kD@k   ɞ{פaC>ږ-^: N/h?\!!q뺬kEo$UYd7PQ J]&KR-i J)m ux|aPd<`eһ)\GvzO->QאZ`wU?un,Ӭ|syhBqbKs)aqS0.BJ5\c/63BwR%FT"a*1Mc/FBE2MMA $ˋFhہJ@'p<#3@ցm#9۝a 2'fhgxRJA9dHmqh~ D7[ё}%:I /tч3l YMʏWv>^" Kr4r~ӊd t"a U!!$m%B"t)0{K:aI&;OdB&1.q]U- S琮l|F=f(<~HnJU@" R)UrELIzf'·, IDATj9!EtnVr+ގUc^ȅ5ϓcdG9Ǥ<籏ՏYqD P@e4Eɦ*fQzBۀGjڭ.MV7l;j)5ŭM Hthy*RãS-eOˤ5WPmݪUpo\* XbD,F8 kTQҍX{3/6OJja]jND1&j͐[sk?xWjgll%N&EpN. v+Kzn%w+5X";]ەtkd*F:Ԭ j1 0p}YL9hT]fsx9ⱈ&*( (aBҌ(޻ gMbkdYbF1N&d>LA8.m%­xxgF}0e.'Px  tS|%Uz(*^]# 8Qlq:5wUryO˽t8(jLZ:TPi7U;~ 4 G P D-A{!J4]-CDÉc-j25%~`@یKϵ1cFF'ڱE$#$dJՃAbv= raZWܮJ4+*Ϗ8;xLGddӕLMo\ ?>*ui¢ߣ9 +3dsуc;⹸n4Qm>6)U % KP|@uD&@:LtS=%).0-OT'ZTdH':KQjk׿2' Mp4+dŔjeEXjIZ%U<vA)]iJ:z rV-ߖ;y`5PW&6Uյ|eY:h쭤8HdM*`C ~n9 Ƀ#cpHvL䤝 o:j߃*}1p&"tv)\*µHN پ'&v:<>ئz.zO!O5h hqJ9ŵve"{+ЏÔxif"@KXE$BPAJTETդO (K9/0} }єQ K$sAMc\C!IvMhoY_8Y^^BB dN0f[H,%EDKR%2& &GphԣJBO=wd!5H/kq]Bj=i0)'WݢNsH(뙁m[*&yiʪWI _-IތKX/k~lߒNH>* uC~v! Jl]A=?Bo1C2Z+P՜ qL<UdǁwI{8CzS^xa qT4mdhji#| vAN_ ?{X[ջQ5ûKևwT~D-/}_T{ h)~J ,u[tUE@X2'5_ZGrڊi (K%ABnŊj!:Lbb1Fj H %>eޞSFp m5 Xxb/R-mقhAD*2M]Ƿ7De%tZYdon-KU6rfЛ0q. {ms5a)'81b9`ܮ/ȦۃK%_)C5n"`q7#Ҋe;5 t<^^+l3>m,PΫtˬ2|YGS<ۮQGh2'~[ͅUÝ9U'헪!|B~ yڮ3WzxSrohb}1͙ǚ U $Mp:8jyGV~'Sʚ1Eea]5.!Zf `C͔#yؙrv"mC3-PW~mS >d-+` e = ?V+@R QSR_+Ϛ$(tsY2 h%coI]8?ov]>+wue0,sӁ)l+qHaz펰~fFZ癰4O}h1]M@ȳOC?/}ʂmy| }HEM&BQH` 5W\h*$ACrΥffNӻM >$D\CbS^&2ɥ z[0p8 Vz\#Cf#P3|`bfE[Kb\d5HHU>hy6{mNL7]8KaNuzm ËށK +!%$RQ(rik sio幑+lG8.;@1SGl[?[$sibUӏLۣV;v#]gU 2[?to^PNO, {so0q; Qs֢ 5Sa hlmbWY/qGv}tS:{0Wow1BTfFKJb4Rn !<+\&;\&_P{{G~ba%NSmH~yB/v ];%035AfµQe՚j] ƿO!,Yyx3X:0mKnnV4Yh}KbJ MЩV򹮏G'; ?p֊VN[&lpNoveaj&Z`^ZTaT =Z~,\4f;.7#6dclw,zm'һC%hzMcTl0lxC{U5'܂ RsBNźi< ;ޏody^g3Tl_K fdg'ci޺o7*\  SP%onI↥ a鉛"OZԤI;JGɾ2rJ 8d1bȅl |,QLJAML1nB:%v+t<}_~.c׵,썥7 qs>x<,9 ELs%(S8"p@TsDRHe3Y!&?l]!d ;xqCO>vCOx!hh48<Tء ӭ´`BWՃg:/ _ߝJN>{⫥JrpusqcxɕOCW^NRL0D߂Y!DQ8$nP^'blm RZqs%[]xLAoV[ݫ{q9 ոMh#TQK\JJW\טԮX 0QԇRP.YMw>^~/0uH*.;qwwW8_/;O^4?_;m{|)$[ A hJj~kPAaQ5q)~tثщ{02o ɨVܛTr @sξR >HU>Nu*KVE$P,W<"$U;+&_4D81`5E P*CEt^?ˎ,K$q̊op%p?!`\piLT W.ݞ<&o!+#***" Km10Hʖ`}[m.pF(X*)B&FTwND78~EwG*ϋ]SPwiXY!}XW2eY!Gڎ-`|Ʉ4rmP̄I!ORu:>U;3t^чӓmW\U9֞ 9c.l5=~ŋM- %M2+@>u \3B\%YJ.M{I+C@3UT^@t.g,ɒ!G4ÆG'-Y;b \KP$:\ ,NY,\$6C1bbF.ȕɔmg|`Tz:[N,{`.FUq{/ެ nlf  aBqB $TJ 5z+mw']wdk[6x`)X}ԛ tYMV=rley! s&iqnn d~6a(+p`ʥ+DqS>MV[UI@ "FLX=˲1܏PSw."{MY[w3^q:~LlM U bD}4EԣASQ${ap]Ȩ,|K!Mc@#Wɇyt.n&+qW5BPb].h%O/q'WmpXyYY.^)}]Ȝ n$&ϻ"厞-M.4 ıDqfSFHAl0` 2 #|=Op0J޻IB\6ƺc-8)VKB > fklY XAby9 t]ُp Llu.V~ 6 rqˣ &ˤIy+M}k /O-~ > )z'=k/5ww >_SGw?19o?B\w͆5LnIUtl  hxZjp2*;B^e3Cm|;M-->M, hb )ۂ q1bcW:5iy] Ogdv(1@0u"RGH\{E<=4PjW\:Gt 0 {s8n\xh]5~X}9b ?q+g[|w)i~9Ы?g^gRd(ߐ4wR_բOm@8-o烪dD.xp+1 7W7fc=y)3۶ִ;9""DUU$@QtF8H^Ͷ.Q/P2FC%wLGS$Qu5 |$PBb bJeh}oK>xr@lFRc߿y%d)@^ w\fA 2zPQ+cLP[;S-f8 tͽdYRzs fBwDSzbCě^yF9Ǧwقp$}9XY)a-]sg RAF٧gY|4ƱiaG88/m?l'Lzw=N^!%߽|6>{ў tɥ"#0 :*C [!3Sݨ `t!'G zf,Ѱr/ ,CXC^ӕt'Mc`sK.g}LB8ˑ*ۙօt,YE*Pu\h=Uf?)ܭ>o ?l{ÛwOy)f,u/kyp ЊT'b)ľKrrۋ@0PQHԀȿ6 O: {"2Fxu] -h6+ UD^\NloAr|g\xil;| ):M>_|%>[ .0?|QڤsV] =;)"kPĽ Qxͣaדbۨ .Jp(L# Hp#M@-)/[YڪI^BTEo @$n7čTĜpήߡWQƭ,. \D=/Gv32{ A.$ I%M"ɔj1ḷ˘m;rY~Blxfjܿ;?rwrC~T IDATy{zdr Wd,aĀęՐ@Uwjp7zsƝn#ܵFAG&lܮZMh;8]=A xac)CR8zF VǫޜIgg+ !Vh-h]Fp$8l~o}74?!y/Aܸj|ħ(.A!p-(Յ8>0;G \G>qIQ,Q*–@![|5,챚-fY0ff`;wUqWM*9BN4IN!\ьߜTԣ.^Xο[#f06rGH_%8=l`-E]G~H#i $fۻos]'X<6%E Y7b}ml_m!qѿƴB3|TxJi* J(8at(Rc`7qIu~&CCL;|!Hȷm,f3WCIDB*]" \Lm1?- bk| MUB ^y%wӯϋ$Dwz NswA$xs=g;G||ϖY.X{pr$5*Szh(,ʪg(Im@DzKSa >ƅ0/`c;HCjeϜ>yʬo}yObS<_&`'AUK-^!FNh{Ʊh=($`I18%YɮM܌~[v7"s ? Y l\ڠxb!WW_4|o6\| D^օ)Sn{,^K>/\&0SG9 l>{^е"xOf-s+yuoT-+E]u:}%b"%Yş7Bq6`+8[cN݈k2 \iZnQkb5{e81.Y4;fMW /V2L^]9ܾ]OBM;ƣr<-o>YoZ(8qV||Ў/[d{msm8v͐`970GΑa7+Cp S CZ j=i環5B,FF1 )DDb$Ynx}P"myN-> #82 揨GzDyd=htS1xck'gkMp WƓ=hZ 6h 8 +֍J=LVp^d# N6fBc2m3+"ޭ>Xul2_ozݑq liЌdtR"3 )ƯAs Bb>7ȲL?8im?Y&-h.RE|G୺QTWv{i{[~" M:|&O:p坲|p㻴IP<*zLL0U}˪fjfĊT0fcC2"‰OnqUQW2&=j'bWckbdSIG* !\"AdtG=uzxoC^ kSVydb@:@RؑxSDcz͛l{C a=2ܔ$V0+iݮ/=jw@0,&J"js?2:P]qf/>bo.xFj"""=w% Y@ѧ_Hq0eպLɘI/Q$ ̩6X4Ƕۦ A~L<]lId=~tIRd p׊WC'LE\ <3}w{ʍm}nAZfWx =.! "Ae ]!RCp,<l5Uu]|LI D_:?e_G_R=x@2jVEEU8G^.,/s|l,tIX^,*wBgW8']z$ײRW(fҤ‚[,& 6e(e78bȸB8ou/3eLz-e.2lRg+3 v^WOvO1AQuO&n4wQ0 ? 5`qK. |D 浫+X8_J?c/.Q^q+nxKǪv@tSY~,k-|I 3, s~.^n L7Iù|S3K\=wz/՜+~}9&oԝ{#>S3Geta͛s*;bI1[$;fOQ _8P%ŁqV@o(H jNAjc8D}gA`dEO[,(&Jŝ?af?/n5"H"75}Fҭƃ$y;j3= 9{@ӯ Ҋ@~!W=`Ð@FyƋq-'`f(1nNHaФW-2*iZwF'q*GIgès&bH$ e AtqN%*ⶇ<>qR/w]#̓2a3{X5Q(Y$snEGsU2m9 xVWx%<*pqdT؞KΗ%_WjUYȫ_T >/|S[^kؐOm PQ* 5fCA$mG;a#{ D"R&PD3PU|1)E@(BA& tE!bpC LVI0A^dįUFhh) jxpf g;4Ƃh~d,h } >2 |,H!h\p\XEnt u">)"MYOh{Wv772vƆE;# eg@E4Tw%32 &D99^3/dru>)Bvc"צRv㑫hVoo͛o/jΠ?`dF#L!Ƕ߽"d :ׁuRsU ABFU*VJOsnifMU<3&3 э  $ jܒxd?~un3*k!Q}.o=]L) ls).3XfBS(ɽaݾZm{{Bk]z)R?we`>8 9Ó@ dc@<YEH݌\WE!D¤9y$'Vy6w0]PTΣ[ !rŪW67)ЛמlVяk3ڳ_d*g? jjB7_ W^"? 2YTew4i/bb\rO$֦(-yhZ wX[S.̾Ȣ) 0)#9l4Qu#sE=|]\mu b|Tǁ=5ʆ+:ӥ9:R՗p) E|* iAH8jVTV-!%Ӛ_} -[G-i*dPd*޼X,13/ gM3Ê4,98({2 %;_YS"tsu%m&R`SuhKbyl $i>jHae\]M*a?o?oqf9˒p p !S1}}@F@kt]<~e§\D%d+OҁQbJj՜f*B{1ѓ&H'Yv{^aF^1j)lWpQyG|rfsZ"R2`;svDF ûq@8d' k"oBx>QgFybcPM56.VtW.AG0q*Y+(\ TјnJ]Wu3w[R qGԆ^%: ,`TGC`xa۞©1Ic{r`ΰƏfk?hF7MU2S'&,Gyjp|A5}ߟb6ޛRT 7Ս 1hѽm{]m{@F_wWH=^n0t Wzڜ!]ʻȠX@6dsNkVp?ZҸ@4Ai4gADJO#*\3ץNI֝=GAPiRπjh4Jz94@`fǴ.FQ0v*]`;v%r?eF?fGE7G L ,y ;J6rwrf]٥OJcbH汾V!4qq@L߽\ DB* ^*kmWW5W33 jn"XHD`od-OV8(~ )HԖ@y|qn?Ճ $tE.G@XyvB~W ndD^w1ԘUir"{m9Wք䱫0`@g]YcPTY BtaoێeQ !C2fs^Rٱ0D8Ȟ5ٸ8VۥL祧nP5 #Aîj.f+V3T0CKl…ZHMJ=zDmikRZ,$`ETљd>j 2/۷w& ‹8feg* nV~CGǷm* _-O;{ F`婵OpN9puBʧhɡr!@HAuq% F(<uu`JqDIVQ14Ni:D'CHQU0ܗ}YW{bf=AIPB ԹueP@<{UoeS{6+Lդ̈mЋ6`ĩ "L3 ik+|"2d$h@C5n5y8xx79b?8Whܸ wǣW<9tYQ-vP)ʓ/ .,̂H9DdM{eAe]U(֮7uQt]CIN;j\BށjtWc6pglfc>^oc|eݥO,>aFuHoc饲Tuq{C/`.^ɗ:1Vbw+_|b/zQno\/ BA@ \s%Nbyg[ݽ4w< IDATG xaP{ &4Z)ܕqeF|Y?-#~⸘/|c9H(L6r%Q2qY| 9*IJTN6~kW5hBh:whj.}( t=;6 @g_Qw Ã?ēK/Eoa}R]ĀiAs,u>ULSlY lGo#0R:6t 2Tuֺ Zgz}zUEuM +v6a8*V1o1:eucL.Q#sO߇b+Q*‡;磙˶ eňB8_ ;@$S:,t=(݃/,HPB!?jc5{XW{fjVEAd3Z 7!%6x7w'j_f4y){ )8o况cZ(؏M"##!  ̽g1F+"HXqY|w7A|0203{YPhuqYqȄ`|7 rT>hZ 0 q+*J.Op]f&+fg~HɡG\vj"} !/,F43DѸrU!oF9b@FwKmu#X_Yٔv\zKJWʹO}?&oz;/TA*3 A`.JB(^w92hd20fĵd:nA `38WUuG=s{غHG2[e]m$w@2K0(#Q(!Q1_8ռ[E2)'/HT+Q(#5Ue;^ab< #?J{9Ӛk6Z)ximS{>: q7+7gmƮ!gQ{ٴ<7G|%̣W|0΀,*\iў+=5zBdF4PZ:܏{1(cWҡ#1`380L[X+Czk9KJ6Yyl|p4I "%\B>Vm><:]ŇHYXz07\e%yC훯G]k%Y*|F` h(ZgӊLsE=e# P) T Xqh;p?vYmISkׁ>$83=c+%\Ǜ][pO/G}rT1 N@LceJ@Vp3Zi⯩;7sWt݄+P8"\tԠ!b49 ns]l]X5Yp_ʘO9$ ']p^2g.WDz w^+qqgcܔ"T(E4AHHbJ !~xV(;Ѳ۬Z)H5К#9VZ6bBe-rf xԫc$Dյ!Z8nԎ;Xԇ $+vc UKt *X5 =oX͂BB "9=|lدF$fҍVMXlڊ%+@0a$J}H(gGʼnoo8+n8g0^p|}E]-\NuUv֩nx[CGCn]q~!Ϟy|l\e͌CɂM4$( T*pdPI_ULHꦪc8"[nl]s]|dBs0hE77;PA?z=ݼ*Tc{̀L؏#*[WqlX%8t+ِ~dB>Cg&H`y&A%p@c$P/#p>]Pƭm>q]6nmlʱ3{8f?*Ocփvmѣ!#lȎ݂7 $2 s)Z]U챈+e]XYq,ig/3%v߼ /bHlPSph\# -'!7Cq4% -C"U=yEA~zLJ^9|-Fkp3r !)[+GBS!38 oMܶ-"%82ȱ&Cc& $|{y :48(eDj_XgZpNRLfTӜz </.WvY5ewT=: ٬6}N>GÒED@[WrCcfbOZ (Ro#+%`{`#D5K~Z]"8|U"fL R)^Eaܩ@ʌA/}xasy>:vh)%x\J,ʁS*B*$48Cp.!Ĭ@$TǓC\!HzT Dc V`O[WRIdPBhly$3ҹ {LRks!tgvRd7qy*T%OjRB`K"KBRT(YDsS 2 S<iZlJI>4\@>%'@c21eBS!Q"U5лoy  o%%(~D#X]`_&&OY4 ,b_OuaLBy8sp#dq]6R9Uv6:u cఽKPY:ke>^ >2dԭ"l!fxソWvο;(S%*+u|O/j^ʂ`rTSqiSNyI7|NU* (<B'4Va!I,,)l]`U Kɔ'a=c&&e`9rva׸Z-h.V |Hr!K)Deq%&Q!IKTu!Ck=.%H34sNF+/Xr3%#xT]{KZ 2)oܛmjp|tٕ<2˹ n$R44H "*+fF6 [a5;N x >؀cTWr< GLӘ/@'A <:7szQ)njxQn\|3$7LN7u) 0+}BZQ6>Usϒ/P){.Dl DV d"9]SbŁ@R,Ncyt5摧8d{_,kjm՘S/- (V5۲rR^tRߑ^9g`;]E51?P@d<:AREc|Jdq].0g#XjA%\+*OF{{sSZe(67<;PF{˿kΝ:0r ưOs`kd@,`%]Z`u41lC>z.j73>t8YeyȰ2d FjsZF|dyvW̨#sǍBJk@%'Ŧ˅w"O6Igs|o4,T:ؾ;ԝ} N, =~ ;۟="ke+5) JHX D ,UU{<ȪMuq H@C]GH# CDH#GP*qY!'s#xG}8_>>B&#]v^MV@Lc)H+t' p(:s y*7%lA?$j85r%BI@BdIـ1n(TࣶF`2st ^eSJ$vCD!I!T?CP4~%!U+g:U`4&㈓Pt .cBFGͨ>@c'$8΀ǸS mCoFL~rOiC  wh/&a}Ӟ_1 ]'1XâѩrV=<:dlL@i[[r'ꙃNl2 RkOmٚ.s>82;̈Ճb1hK?DgWuqu|?hI3E=@>!^w֑v}CfJ1+9a1N?q걗dutW8\_< [^^E2Ws"_D5'pAp 4fejXkj0 "SUנ]A##!%2:;).N.[9}WSogi MC 1 L *:cƌZ̵TC(ƣf;S|H(|I-+qwN^%pn(ܮ"4'!4|Ҿh\~?h5&+jKfztp#8{QP4X%uxIZyi+<;@i{țl0LUe9Ջ^y|c:Ad3^n_wZUJD0u*PME8&8ͲW&z2j8)9ZaPOO-jF71hJ@GTMUM! $hCH ũB:˲`{30$ h +{M5,4zޛ ίG;9 2 *6l<"1(F؋3^Q_e9._mdG!B_l-Ky_f/_\c)k?WRUd]K D=4 ZxYS_-GX瀴u%2'Dפxd ilRiEHFK RNz Ł̣íBݎv]ۧ d+ӏ.ӃTc : #GɛX.y"ߎ8H[Hd]#ԫBhFw`¬tRcyKf7(IW}|݌ .[AinZ@2 Ӭ Z kZU"U#C8ꦒYT"tPьw?"L'yJBX$oq 6b \x J1"% NFxڋm]s飱t0L} xY0!&_ 鿿7m]q^Yk4gd@Rj'Ed talqlqNƌem=T5M^ 4Vvg?^cM)rbs&7a7ˉC}p_ 0r|q䷂¹ &!ݑ,GWkc̆NxGNӋQ؀BlFf(Ŋ.s=h(g^" 3ix)ަ ne(c{oJL|؋X.)4IVtTUa@p(L飯#*Ԓ) Ѧ9rxi#7?@’3t]D;ŅRarRJMG>X^t:"R/}5#4փ5=NY8W`oǂbh5 0!?KLTY֟Oϕ뺊=WJ*! qﺿdP>.179c^oUP v4l@H ! ՞_(Jbj7Eu'> B8^6`"Ua2nyҶgsgKN9`?^g|ixtEWj>7t4u>ľtE4m!X2Ë%mv2!2"2^p}.dzj6_Q܉53r<1e!xcR{ɖTqAwNlY&?X u݂Ptꉢk~6w!;+#JOJ9:v$\E"(#UpI#V6Z$*"4V5 0E0(\@G,p(R&) "rl g9(ɘ`+)#jױLu(q:`dhK9UdNz;UYyc8[Bu"-kYl9$\4??LD3G']JQYiqk]WY՗ zRd15hmwlF~B2֍p$ C fTx81{H"- ]y Oq DQ̭ho㛪!'mnϙW7s~ylfg>FG^گo^]IBNKy( )CtD&VsCYm43&7`VN Rm4t]$; ")C)ɩ*1H Aeg*@͠#}V1$&&1?Pđ$:DdEKmz-X{mǷ"=@捖VQ3M1 VބTer IDAT_{P6#iy m)b ] .ꐻ39=7Կ(UˀDktJg Xei|+bדח!H"!X %;!=i#fC\b{eH[u{U,rHA2fuƇR{ST}d{zwT'h ~ТvߧW>Gv_w[1tŨ7)^.s:\}MrAQ#M0ϖICPAHrzq+Sl$M̏Jw6u gƂU)i$sӴT-m_ge k<ҋ[mi4 +T)-`˷NsM&ʴm>el`%`qAKb!3 ҋbxis~|rYJXDEdqċ&P@=& :[۞"Tu!j.]r4ucٵ"V iPaVrbπʺ{{E=ޏ%M3o~"1O[wc9~CȜi-U~GJe"RclN?lL,ˊ)iJ":_ّJA*D40Ue@*b$nZH Ј`C9O/iT LSIK̆E)Oco_'6GN/,1f/ Q).̥&Z_mwDFك˭4 <ź >Nmw+Q-6 :b0_r_kZl"ԮGS){.CK K #tj(|rzǤJ.=}@RDy|#Y PPYE%2s]c ߿Seque&jVQH5y1x"$jf_">ֿ9{~fFWS%Ԟ8 דXIKe'5 a|C**>;\$b=0vX=u0]? T x;YTh[15lژ/Il ҇@h,! <%q7zoXk6 Q1 DPb&O{3 oX_h HwHL"0 (@OY9ۈ)se~u>ם~̪*wL]xvNY[!WғgsXlU}+b5#Uh@,W)y,*?BǕSy p<`}]m6%#Tp 2hL?\ȸoU7ӐǯP3 C^vƅ|N>~0^gDGW2~>p-v( 87[ t*Rw92*1x#Y * T0(E'a,!ďJ4*:tB|GUG&fC}BLoO)@V"e|Dd^ijI'*7*ù([IY\~' [|sٜIny6P/E=р`=D,,F/Eb?BzR+Lm_@#@)'QMb)r!`89;z+vJ|+j/CDwυ}k5;=:Ƿ_x|B%]\ PuuG * "99!RlfHAb"*!~,`Y?u L4,LF𡦪kָ&CTvCr"DA,lr캚<+ Fb1 )9wM3롌mQ"'͛QG^a/G#"; ,qr=8[u ||5.Xe- <)rt<>i/ X\~|sF1gDo)d@\\S\Fa9baxxJ-9#׎(_;[vaEp+BN"<ڗ֧m~WϱY=S%BHWf !t10W$DSNTA3>e]%!XX[BPSH (JR :6hE}Nk0Hg| p $*`]dM'+QNxQ|]nH] ,z#M؎pÃk:7/tʈ4ܱ.s'H 薁G?SD6qLӼ I]Jnh)F"UGfgmOKTGZ`qfV%@|2xp ߏhn5hPi, >yT^$AS-E;X5IZn<b9v;QFpuV$vlOmF ÏhRor(F|&}<#+rZNF 3+t< t/ٞ%$6*1AAW]B>G`RKA&#pB!um~jBd!($ !'G!X d b pU{|Rn*a&9dn[+!c~>x1ʈ+[I;fUKxtRVˡjLYsQ+zn]ձ[\*cL= ںvrއxP&'21 X+5#nJ>lS -Xv%E/@5p3u77 PgėzwD11pU%ꔏO2PvauJ uCCGMDȹ>Y`\լW,3 GL@I+pgrzu;&xOz4)>~ $pp{0e>zvt#ǵ\1ġ)Ǎ)eGCt n]uB]疪 ]|YfnreDJ*UK65=UPgMd)UR5=J2o,ژB UWVEq^_tԤA51ț&!mTsA\V0ԣ9'_I`_ 7 Ĉ]`]~ z +F Fk0.2v͖*m?TKBY-ν%zZohUMfcJa6}>V|hiȬDOopoذޝ=tv9֛>Cgڎoozs{5(M_j‡@@e/hPJ` ].1t>\..T(t. c43*#{CVJg1}j/2 gAW*Z7{=dν½4~]2+U?1 }4y*U4*|%iEI>*-C*ƭ<[-WXT?w=!*|hV{SMy!#R\$9haIfZï3x$JoqvdTB 7t>* 1`up^} ptpϞ`iqĜé1Xp`pT? هT- ZkQ ՜^F6zDZgo]%s]=lvlu9鷆j+;dXy\lVA=>?q<BÓOY;Im*V.J@B)Hgb\pݥC.p> 62ܐArv">0R;$SD@'b<1INԶ"@U#nmO TuNV>#mp!tnF܂n}]}S]ބM\ YK2*c8fwp&T>z˶8.XKeђ?O.Sq:"{C TFd ?+?Yo~FED?(>~;Z?SNtx&*)9ܑT)1R!N!e!ќ}>~]|.]tt3 HS|qRK,Ykx!cLY%UqdmG,_Ic BR4ڛYWh7p 35yN-n}zHDW+D*|3A-1y\Ii!u%t VU_~VZ89jq#@B@Gт5*a=So \v}pM zs|RE˲{fCQi0@wVjzR6|yY?s)˕+='𙫍ið)ޠhV"vO9cɧiO|O><ЛZ9MfD]YdhއsS`4u!v]7H麥֬\C3Бx(3Zu;b=j5I@#ȟ]h >f8: l53hhLξuoWa< Oha3Ms,Ÿc .x^]UI@ǼkqAc ^ k8oZ0V?JHDâ|^!pf .Q1z7.7긾@ 0 2TSg85'mXJH |K9f`C05j^T.- ?z}hN9PV590,Oy `E+t(?皫=)*'w-ڽ8Y<6F:o| @.Tq}MUf\(zphΝn٭\y*a)Ia)b{f|6WC=a3֓):?| s~\5QZxBZb: cynή?.G3(wH$zT>9jwYg@~ 2WW>!jV'%}%WVغ:Nx n nP}@[3d W[߫]~>݅Ux:_WF̸ԣ}30Xrj>*9ЌE5FUX4-g | kN-I wOl:x|}ϔ:J'hDϽ1v\yt{vpPR7MLIb2H<HD..a& U-?"}=oxU0KHɔJUn+Ab 㵸ʊGBK\iOѼcFmbjJZdu17Pc'[*aU5@ȾCR.k}ZT?oc7pI IDAT]/1#D>ϣp@RL2} hq'4;<:xx}.6T>86r"!Fp0;i]no<6&l)Mb)Tg2_Zhf t#l3)ڡ'cȉ'Kp:,dV >x!F`C߮L^M~g@imvjRoga f|ͺFX!h}鳍 .;\ej>rkfl 6҄\Hj8Bgg`лfHr5ӭ3pΒK~5|w0]'Y^Wv3_Xt$n i|XғX_ Dvbh9żqn APGwQԠ2ѕe-ԊH Tmh,Zwa=/Y`+ZmF~.c+Fϼ?l>'*V~K-{xx}޵$ً-cvxm;:fo&ޣ؎TX rGgزiY x0nت #}gz ͹{ xtq|s-9!~N.ۮ3̎EW6kX$`?r$՜Tܻ~>ة;}>mc/V N<<+*{Qt1|Ֆ5`7m נeo:MxWF~ ,~%|{s,,hls dGc|_b_/c  Iw?zjN$V~U[`CGi0J沽3Ң#ؒmZpPnj^j*qBVA=W\*$nBڬz#.;&'La`KyK1]@$ lÂ"9 g;,q@z >т<;N x ^///4$J\_~CrUJ@ aXPӦi,Qsx &WXJ]UfՏ*RVKJDYAhC)Ԡ7AGdA9?myJ B>+?*=ׯ7ސsE'oXSSgϼjȥBNj0wRaYxOUwDm\#w&mPr[op>cľ;Aq~$AoiurbBS[} F N|q*ZSYZLBv%T۲ĨrU2:/`3afARn\mh}.U@`)1%RCPP~0uR{L|Hv`0pܳՈ[g9pXx %!z#/_^/u.uD . s9 |0>p._|Dofä$f`boUTf%\H+?#yF ǣ Y=N&rov4naV5䴔a:R|a;粟Ub;|p{OjQ;ej|%SimmՀ\b5攖aLR}/؂>h2Kwhux)9=hR s8HYCzw b pgq%pq7=<Tε!szE#KBp1I G]\ KG@ta X͒h :)N+^*^K(I[Xd4,&<'Mƚ47|9!KVa՜wLɀ~TsRƃdz91L?ld1w `~6ӡFOWDV<h:7z\CQ~i.1>.13C}4!l뭸UYf_zg^uYS-zuvS-yI}e&[r'E6#n*ퟭU fGP&>`qxtW||y/.coo LXTI$-!{lor0_ф< W1>}P QjR/inʐbs{\WX~{$HczCekml"5X\0mnXh˘͒6+/1 ܕWn?qXG_S/߯/v=/ )bwB]BB&q FxS^x@]Gv azZjߍ(19 [m lh2*!{eUo=$.<|*!ʏZgVsڱTͥ;vPGg)wo-9Ӵ!36б^Q m"ꁫ^Ňk||pdb^LHihZMK[hu[7߿ 7nL Ā Γn>&\P 3$"`]H~!{j{;wW"^.`.@L-YkYC %ICd 5o ʌ"aGfh+, T"Agj=@ PZ BZz9#b?yeNH`Ҁy\WL$ob&xc|lA+OɛB?"h1̭i՝y&8;&\O=\&CS}$I%+I=vIr7v1ޓp=nE<͕ Uܑv`ĕ[{J G5)e&ij>Om7I=Kβv֚s.kjQ&W+5.Cl_ 6&)#xTʟײ@_펻S7w*`XM5D8ń/7}$N~! }B~/Hl>تLE%16?%0=6r9ڋ͸Z'P~y@&um>;G}Zތv@G6Q9=<D=1.+oGȣ65 o{p\+Y䙡f]&9|1|x]* !xT;* dLHilF͋ 1U}7.cm'*JN%dV6iX>`R~y7T@c3u 6bp*c@؇@'%')AGr1Ŝ<(3:Wl)e q|PqCJ .%GP$w=ѓH']df-WcQfȱ̓ uǦk25~Yk{8 4Os  8O1qhxL,(,qL~*)ۯv_" N рށ!^A鐸Bx<] |Dbtp5Iύ%S=yc$H~Ů0c&9Lhp&4KUІ*X8NB`&`=iA \=R bۥW'hj;},G2OO\&c8=l5Y/<#VoOk{Ouv,"9#PIfϒ %2?J$lPSp}й]`>$jLMo"6UjSÎ{6/{/}$X,Q%BA% @]5~hʺ ' p"9N\p/:Wղk1T O/#k"G{/^KGX(ʕnH#U4VKbFO<,5@א A椑d]3sƷRsRlGZAA'w }vzi=Wq;T3A?@:^Y ;!x<(Ns y c u+YGZ]hZ<9]<[M<4sޢy]:Y"uꆸ窶EŹ>2B{"V,*s~pt ?nw&w;5TAn0\+W_ڰ 8\C;@Ҡ#;(Q8U&ۯ2 vs . v.]:"F4%VwKM$z?JV ʝ2T˕et_iͣvq1?:*Da}JȎx.ʳ;ݏO`5<[勏NmJ[d3ɓcmo֚D% >t/1z7?b4NmDSE&hCz,"gÁfefKWu |0G7љ2JXu#]xk |fu $ZߧWS+QCW4ex_A|G!_#.!$z xvpfPҺ\6qgwzWrX87_Wb$Χ*(7QzH+!svr K Yf!V5gۍ!P#=&c)׷MkukZ͓V 9TgTCZO͸SڲLorjfٯL>7wRúҋRMLtI\ 8*6: ;fM`.5T?.1.~']hF7kzu>#.nm`@%d<9 D6>4+Ĩ)"7ġZSP "6: 䊠,b!f;+~@$s[@5p>_QW_a97pB'w62,sHK1w!ffXNpfKšo2@Zw09mӂH%LvAtƗrq f,- +fn;x?tA|Z>j.Ğyr=.sŻvr 'j[|oQwOyo`h=?Wj88̄}Y|<Y(Gc4Ρ?i0>ɭ :Yy?r 9]I[p@Tq;Mm$57$]7i@rJjQ48YѯlXsB; !]΋ @F$+ p g !c`W2C:`]>ac0IwS*C9XS%C $w\"~Ⱦ~MjCу`8UJ0TX10l)JU轵Fr5I UsusΉF<2' v*~t3;su> 92)|샫xH'Lsy"O=S$)_l(Mhu2㭽WȊr4*56rK&rh:*SAMrSdWֆ!%Y! ZRdJzT`\r} nD%@BQDds.n!i -ᠻD'w1SttM[t0(.+<(^ w%L]SlkQS^3a81~m=<ѱY0J*j܍S}tiAz T28#I^Q o} >U<y |r W6O $UI@TdcR0 bl#%`K % 1B[^QtQKTB ]xĮuA,1Ch FIOFC?o:i||s<7Kq;iю|ɳ~w[~y5r5u}ڷv?Wj)|fj;^G@A~7Iv1trrI&Ǔ:\ IDAT␫51w [R;`u*[xpQX&SvZWЪ~`oˮ;eknjbAagSWuLrB;_[U4Y seB?#Lj 14H@Vڦ`M+$5%a!] !]G^.cLWwKLfH=w*y0kшUXe,hT<@/gB > ɭae=zv7k;;Eܗaߪ(wcal鄝w=OG3돖s;L7-}.]&h@h=`6"4s>V;UF+8oJVBJ(S)'o\z4@W e[qc67W,s v=U1{`YŽ`Qx=UC,Ǚ-Xx!@nAJ\O5T 4tCj*q.]GC@DBn݊s";d2~+Ef $,@ O4+N.'& @a_`e*<K.`QGNk̛YڟTKVόn/=XyJjKʣx  x X:y1h}{^Lx~$/x h]oOO!Odvxiܩqsm?0@U S QS $olbl:S8fqp5>trP|9u SLj qt`FцJ5^y.V2uHVQQ+5ᕉ w[ _ }:W+/dDpsW0vLRjR|hb Բ5畎@Bl EE d%>xbֻ3`7*z,ZHu/R04'gk1G $hLlй zlzG6 K}"Y^aen?jE@dmL<+Q2 +mGmOGK1`Bv>࢓|lњI V6Tپ9^qŰ>&1qO&RŞ9 MzZ=HQ ;=E Hw2w8_CzoVT@[ABzf7{ΦYR2D 68J*pBQ*p&6{j)嫺i.MވK0&#]}V"4yH,U Vjc5Bk[l6FMt g\}3.!VdON2G'@}y#m" ;Ccu, 90JH~540?j*eg/эj5fe^-TIr oيxm#jj'u V"lGcG&lQ6Z|SLJjubC Tkq\* $ +߃jyξ'|K7O,ؖFy 9hR(GWncdLeh#0pl,-`7`y8 [,5IBܚņXy%;M{fڬvO̜@j|FQV/@/ƙ 2\92b&қ@|-XWK˫3K~V ,oR~>z|`QAQJǟfI ;<0"0*DXeRcr@ 'i]>$sq:;48!G^};`ft~Äe2\/xX#(U`ʹN>qQ/L&c# X1 SE0db7\ 2;-"'mQ"=mGͫh!Vk3.TzIjc<ֳ1+MF4 BW7לۂe.ǐI+ ,S`_@%bx~bF'Iy5΢5n BrusLI+$B#t};D0Ќ"AQ6Ma&)'^]緺"ѣ=aO,>6Ej߻uZ!P8˖,ƒcT9F\')Ұ4n 'q{!S1L RBmzn/>z4nhsf* q6 7NnUVX@h_6j+n"lT>&R9Ak"lUxϗ+`d@D@t'tS&;TpO%\WH/ ]`&d?sMR*HX{ƴG0$1_ic reh p޾KQzІ[H RTd4CtstOU4e_\ͷ‡bݍC?xCgsBF=l|lsw$ȟ=uDe{Z=fΡUuy-?v_906|8GB[e-_f9(ڏ|h>=B=@Py>fꥭ2{[Y15^C!REꇦ0(l⎮9>d|l%loGHT Env" 19 o-NTm^q Lȡ+.N]  % 7yDrB4ሉꝭ-T F ە|umu(G}8$0L0M5@٫DC5s@tƮ<!`}y5SyCSA ߠI5}#Y<>8ܺv&5أU8^  O?O?x<[>`1OsqOJgC&<-6rZf{wRVGV8?Y՛7k.-KTbѓ)TF!6_(?9SJpR_}@+f.?W~KW{} 8 ;N`&e3m PUIhH /fJ_ 0ҹ @`.xB0 rѿ¯Wjzn[;=F1B9:+)7?@ y`F<x{mqY}nYB~8Jmt)P 9%j<2hvN*Zַ)` ' rkxq#\n"DZ2.Y MwRʌ6_'>jSfDaG]FZu""ǢRCͿ}eR3]F9tfu ?ޅ^qWZ$@;Eo^{ } ׫<\{xՓB> @tϱsA-,#WR$`gKs0(5׎:z  MB1&]^Z.͎y=uD_m <YKs)ܝ5yrכe?MOQ;j:371}@!y~K2+z2#fF~YtV0v/Zs,c5.V=c|-UEL-fɟ o4l uqM_pC"_3 JWv|"!}˻'n|_> !὿F5Ԃ Wϕ͔IsZ H쓤Ixl:ρPN4g䀰CRI #Fi$)#lwλ5v>' c.} >I|25?=or 㭽͙Gۤxtn6>3W{8<\1Jzr<< GCE*W?Pf,[ު5ީJs#LCE7N!m׹(Yt$j6옚SzT P9q䁔*X9U•Uv? @/;o$#HBK@:|js喨E'LV!+f(j02|؍9fRvK x=բQ$ L4shNLTÍFiiBjܫp>f̮l*]>o;keNťSԣi.M^n^C#kOD?{yx;nOX|U|0nN{B/1ݻ$+;\lּ۪Ub8OZn[wrh{O"[DJMg[C*Ѩ,|Xɵl0N%@+_qo¤~25UBt%qπ"~_.GM\D|9~p6`t)Jgyb<|X2D]pD}On~x1R+m *qִqBjv͒}Jle7\G:߹|%jlsxKܽ޹nAVRQnDuU2`8ӱL͌5Vc,w=![X&` 6ΦzGMڮ*^5_N { n.]]2Br|8̚I:.v,WsAR5H8/7`d=(!}(Yw< &\29=_ݡ{ze|1nowG>29S0j/jӼɭIg#W#Qv;SiI"[Ϸn9s !׉|fWP}Zɻ[S>v wS<(ER=xGO1Уg)_‹dz/;bGIV^A_̲ldv^eЈ#*!eh2 j(ڰfMxTb;M!Ìӗ~!-} 7$4z=ѽir%=Kr҂Qgd٧2r̋R%_MSva> Me.G`$#t BgzЛ`n&Kld!-%[4Up=nFi{tmiXa=<~"qxYlJG'!}iqo:FhD؟X{>dMcr3qՖ ?p'ʇְIC';ɀPAcCҊ5X[#<{hjUn37lZ)YI,ksY$1Kx0_TrU T+oS1,oygo(q5㫑ߍ^}.BcI^OK1-J0ʲı"bJDXeɦ|?F,s[5bL{Ow7>=5_b>C1[t7wKѧ"R mj;لҵsFQo! z?>;[dsdi{Ay>y 2,0z s)(_eh?$!=@ycĭiusu>Ԟa8:붫E"nV́uk ,[%)$<[ȡ|z BFB||z rop|;..yuVڃÖuj8LcJזVe m G~cktt##3,]Q!DIؤL6GߑbrRZ_5eM)pZeᎼXx礤͇;KYR&?SֻȝUY'^DVJ!@3Y1T@ zāGanL@D/Ixj<bO zs bH^! \@C$ëah[0te2A5 B+i Vh8;~X|C\^oۧ_y_{[NEkt\ U}ւgҥ[ K}r=;yZG͟O#:S?9vkd{c~*!͒IzxxPa =%uf}{svEOWQy1(FC~>y~Re\m,yNjr[Y˟dg M+\ 5x)AazGՏD "\(% b`rgbQ +*Oha#gE&, }lA}>2cps7Nyk2$TZN0q08P`q7* IDAT_FBk,A^ 4Թ[iH8 E1\v9P(ܺ ](!͛S&QCdZ!x |k YũHX-P;|&Yc.`O܊v88:om|}4?>NlP#EWGLw=(=<\G3sm3U`3vᡸ$Uǝ6s-@Ȭ 2~+پKV>jshn^" ʂE[SK-7.?ْ]V@0U@xƴ;.r ^F؀WO'P* #}ȥV\~qv?T9W:wuR{oxq_2gμ3fjb*Fyz\bKL] P~&OIdˆuKb}1tY=%Ny+p8 P5ނ|^mv@Klg2l\ b.!U3([U@0T@~EF$tGf5 @`zphc[ 3ȏ؁ӇZ?N\2w]_b/1KLsYG~o{9}7_8rO^AiqUҊ"Z3s"p$d}Y d򸴱9L%ͪ }:TmZ:='yՆ, 'hK\cV13yH_3/!H c  ""CYSسTzAHFg6)187lxhTB- 3nH̒\rSIƋ [ɤ#qC [D8 "DI %X͡h&I,(V.:JJN!|FYgZ5-G `ԇ=Ϝ=Pj]K@hCAHKCu7I&V'oxpMluzG8=֢nMUZz"}xǗ>]; WqЦx* Y^ByeQ;4E^`U}:<$yRg/?6kFZDR B9sE_gһ1 5JGjjet@15S\HI`BH |&)+HbүB\&wgl.;r[emp >Ѷ +(wU~QxȒ3-J0Ӡv@J3pK+~Jd|:bX1G6 |IYن5. 3?{jnǜks?\1B H +&JCp!TR)b D h),$Bp"Db$/"! )$@Wy_ǣ>z9\{s묽s<{k^ y㳫]Xg5: E]x>ץ7X@K  (cSXցum9[K;]Ex=TGPjn䭡C]\v׆`U cUeS c3nDwc'2؎f̬7\gsb #MUK6C"{SRL{(uF^ܐ^HUJ}|a^`ޏݾG.(^uUћnC>|zߛS9e, *c@һi ²@I-X4Ɵ/<)~m?CBh`^g,Vjb"aO XӴWگ ,x£Gў}ݭw :?雯>/r5g/Â}8n=ha K鯹 t ,TB(r$XDĔtAL>.u ɵZ6!̬5^|4aޛQǰ2)\ ̶v)Uۡpf/3Öu5뀐=k+O}Y쭼4 HU&Q֕I U_`+m'J/=ZP(-ǰAJT"i?ϑ.o19_Ħl|ʽ֜ )Y6,Pz;09=?O?\|\@~O__h??Mz4axN܆C/Z})9 BUwhnX;!m*C$> V$NF a #8<ډpˤ>7(5)GP,ձ[ARjNJBFD7g qg}>rVĢ4r1-`nkEX .F =^Be`g.8\"l@HT#J)nzz?Z5QՐmc-1V X*ŮGv̙mU~Q)IqjC.pξFtCcJus:q^.X21ȬT]n\'x]3/\QkKurյ]Op\rܮۢIh>_%ؐBUZo,(^ݘwh97_*oxcԎ@3^d3:[ZB؝_Q 90.)wb}:ʨ2TB2c,l2ΥP4 XOg4:]c@𢡊/QwPx4GLLr@nMŗHMWǚ$J7DfCXs<Ϥ8Gg86 ~'nQÄ=Iz,FIhQ:xsƹ%fSK rjfw=6R k~M[8j 6(m~>t6jFZ%@IYQ׻²$yFZsؼDUg8h:XQ?m>}x 1痗ZMG)p-1XޞFJHJ4_{s|9 {^$`$lݲc("UI=' ]C#N%=v{@F??W ;lO"}?;1@oKiLČ8SW% U S'‘HW40Պ映BHZQ8@FYm;߶1άsNTΔTIY$S7K PnΠTOQUSL}3htf'c ; 5_ 2@m@;){'Eby~gLޞq^y'qiTocg=8=Q#NsxN~puE6Vb.{u-àYUYbP:\,GҼtTe lR ~"CgΨU?" &S7Hi('Ui0V"O=Iwx2U͒{~<="2@аNN@p|E'\p{4*QUKay6*"i{V= $LV۱f(0~ntaQXxQ4i!29ץA_?\!bF8^WdhQ/"u9/i]eC p8.&-!q#aw"#Ma޹'Nv%ׁV?[ !}xo:+mP21x~n8%݆KZDaeBG֯q5]Rab Ҿ9H_M@Hy#=@MI4}9}DhDT\*,Mѷ@M͂FŴT%$2* _z y֠Ot}5ltwh3K{?+Jf{ǃ v}Kn{4 eSkVI7kE`c 5\alơbX/J:Kz:7keSڮGZ fdM>X;]i[JmdjQ@1$x\=Ұ*FT az-XTÊC5Ie8 ۣex0#l%H2A't >t5k` ;J<= JXj P~&o;ұ4/JP*O@b鼿J\rkb}u 8ua1ž̗F"|^ZxY*_ݎiDɫ̓pdly}S*>(8;xO-ajÚTg>u^͕`=zF7"& 2ŀRGR@WF2H\UK 3cGzE&=ZIOwnWԂ5>aE3,dgrR%JX9+8 $h&+%ß`xҁ9)Υ;YULj6/vf4FvZҲ,XooՈ`uFqnJ-m돝/6;DPZ}.YC zrށkFׇYr DQ^S=|MqF.iJ\ҝ27J*&*Tkx =<~_-+}B+I1㟑0zuT Krg@ @=zʫW1 7g@|9?A:̗R_ 'jQd*W,]9 X^vbẹL0ycPpw()6ƺ)n1-bjZU ®e(F{:_V]B VKRmxC(O+^sNcW*Ba 6QAԘGQQ6lGHR_yΛ!ZI6 C IDAT{BWWZZsu٤mmGO sVJ2Xlb|Y9y$%`z0TiK4X惊ԣ@@&AK/kD7~5KLy!ĥ.ą9$jK<\@$NZW4#^5yJ8B=Jxk%xfPlD쒄4iX s2(\߯&X{TUl8cGW2A-l~| D ?_ T8G{8 ؝`^  pT4'ȴWہC({LóQGPr71,:#1:}"Qm.85pɍmY#{$hM+V="1gqwAV"n/shNiKʆm xZ0` zfμ~nl~)X l_o7q ݃ڞxΞϑC i˵#ȡʼVNc˘'"ZgI܊ަQ-iA{U@"̋{\-ϓcL IakTU>. RnLsM*8ge&ʌgwVP < z;Bꖴ;$[?\m`+8#D!8 kT!M1&[ҭ]> ]FBҠD~}ξ1Ф.gmY|3ScLG3ݘamׇۨ"Ǵi~17d4G:ȩǾ.pZ9A_9?T F.b-mɬ^Id`JZU\E 5qbQ2h$qL{F*U.B̞j22ߌNKnB܍{gU_ߗ\gg@O݅x@hz0{@KU0WAz]F{eO.nK kr.I4B`dX26nFKM;vһc$Փ;~$b" \K#]v+'l!r6͂`MS1ِ;ͷ){Bnhi+nȃby(<g$ArB/z~K{2(g*j ƷmXp9W`6;)q+\%!QQR 6ПrV51%`I0Bv,_q*u)U撘].LO.^wůVY3 G}- 91f`2!9P3I9aZֺ꫗jV:Y`|GY^{Dߟ~ +sx$.KOVc򦡰` Knw)sPXM۾8׎5V897g^Zc\! ;#x}cqD]!-^z(2%Ty=;jEyd$-M˴Bˋ(Vy%J@HHYHrJ`Dv)H<&*9"1"ʏ*ڑ^-n:x& -ϐ?>^3ns?.3rgAրNhJUWx]]ŹHݟσK)rUp"gBR=[>4_<׎]p0g)SL V0xmS5JGҪ^ |HB|$ÓL|psJ`.Լ`|ڹCNH#Ct4PvjoGM*JS[qeLR; gh$zq_Vnx=5d;H*fưݱHJcu}x槖[4_?%HZB FZ]2%8S"Θu0Dfԃ~<eA.a pZ_ޡZ^^Ve &U  g< 5@h b u׀nw}"׹q=.1 rnTr,(GPDzO/cF."B *>3Aw?Pao*ahΙ]5n P|vծ W8?0#ζO5v}Mxk Π}P -WM>yzŒ0#MCP?۠ᆧ_FИ`SA xۏCE X)@d7>h>;Q+J*]Fn( )(x/qe X9(Nk㈱ 2*,eN-nݩ N_ 97-_" t@_ B2Gy*X.Cf2EW"Vֈrb{e1ߘ1ڥ* 6۳QdFyOƙ̙D1ULxߘ"!3{m~6hIDl8s6gmj;ZSaYpk_~7՚4lkfgeU/ z4ʎt4&7#:$һ#S a]' E) W#_ %i *OH򇍒]sȥgWCP$!fQS܆_r+pH8sp}u<f#r'>D1dZIyI˕T *J碡`Ndv'>I$P ` M+Pot#oj v=Tn\+ zB:Fr$-DED5$IVSHHo :'z3s{sΓ"GaQƅdfib2)+3o#7!Z!i_3.\__8SU=~kr."b"7۹}- Lu۱ߒNVThPJ\& Z#揁aNj#G|>T> Y| r/ ƦVqsI3ygͤ[D8KIW 5,1DFÐyr* eSf1TfK'%(Sl6 b,$ǠA5+m =1K%n8A}w$t /'%3Of;}'٭hą:I@GOx iNVDb* ZpżT ,}S:S:.坘.vq^710;%@sλƹF&뤉!ϛJ_p֢>>LfT}VvշϕlXb.IaZ\-`Lc\ujg1V&z+t,*js,|"KhfցS=k-*WTvJAAuUY0+G0lF

+$xdU’mvNZ!^7En~y_!ۑGǵ|fnA[wSn(T%kI{Spit/b}5$02# OI+!+rLL 0^FoDƃ($?}|lf^s_݆+|<8 SI=zzaԨ1JGOB`B1Pqj'Msiqiڪ5PR#E7d=;cAnжM9s=sd82F9Q/X ~|{8N+ I,rvU/s5 grp\CJ J[sX]=-ȦfX;sp>w24&m7hn6jy› \x$д0>Xꭀ"j]aMICyy+HF4,\ ޤd,9-N*QA`"SGx>Q1!V.5"y|W@KH$hޟ>/ߝNϟt}酤}UQ/@` A 0t5_z՝wb#-|:{פ+9QQV*;Dfn{~A4O pYsp֘; st"~c?s*RksR!ۼڒ޳|EՆة|*Dp/f+GKmj{YPMr#Ns%>>Z#/هFQp@4oo?S{c ,\uDs8jTgIv*y{c(%UURh JȍG48V5T , T1odK>?2=K |{ѤSs8 x'O0\~Q{@S3 #"sкSum@iymvj`rYdj84Љ)n. $B_(qT. SJ-̤zU|= AL ꛨCy|Hvcnzkۛd~Tv߾M3[&si)#62΂nMj_MJܲ6Xt)(jzg o@La{Uk{04J`4- 43#͖j"@lRX#]5Cd =jnU&!1Bϒ$= tofwݧ_xg~_? љ5{Гauw0zQՔTѵae }l^neQW{NN7RVn5YpBݘq.$/fkoAy$yom???Ϳ[p={8\ IDATnǞ0;[삝V X[Ikb[aM/LpҢFta5ȫH MbA4ml4T/r2}s$*AˎaNA57ݫ+dIhDOnO`k6P /x40@7V k>R´2)#ȑrƼ# W'(j}F-WMGD9Ђd7oi7yP`lvH*m2JWPa' *k1k*aNpO.aGVI.g_~Æ%U{F\$~!H  eV|=b!ԒIje"Ӄݙt'ٝ;HyǗ:|3?<̿gnۃax辫tՏ@ b_;v) $z0{Ħ%jdYDJ$RQjOձLY;K3Fn%f%QAoFuq{c}]{oa|;\\ySӸ YR4-Npu𱥅m|*.<{ =;u\DHcyV y)e ٤TQjEZ#!f$C\ IL)̆reȑKQB4IR$01nh3:zDbߐt k$@$&x%IKYDa ZR: : "Y nMNҽ4 x{~z#G ކ,DlǏIYצCSP`>(%3T"uժlybL݈ ؔP zuFr!Йk9]걨{yD|q$_z5FTrYX"5cY}+D6DF޺+G [c3oԮBXzbM^6& SWiNHW:w^cM2Wߌ,bWG d*\"y;kd4cHot]|@v4){#?ZKl,/W#%!=dwfLnC痷vo@5x!{w n|딱K H~19t:+;W|a7v00 ϧ I8NɁ|bKK]ivy%k,|mvom~YsW_xUV1WVϵԜyɬ1Fֻw`W;S{2h CPܗa ީ:TvٽGw3JH} UOFׇ1+c;{>S8. c$ٿ{y!i۹ʻg#6yR_ <lp qmf -X҃df;o}{m_>ڗ}ymR~ʹ`u; >S >h ;S#`A6\J[RP¤)?"oT b2zJ2 b%"zn_At](KvJ2d gؑ ғߠіv5jXd!`&6oh3lFqKGܩag H9nyW`Քؿ{sՙtCe EwNWs9$93;XMo4>Qf Z wR6Jv_FnO^~nR]U6*KmXzՑGZQ7J k'EP@\ikbITbĬگ?9T%b:3nQ9ÞDE[ߞ~[3Y{A$T]?U !tCxcG_Ӏ}tt t&[5T,P)Zk0baլXr"vS::D6 qro 'ɀAHuT'JknD)3 5ez[v2jo ޕ dcwqM?ܫg Aք<#}g:/ sm 3c$A>tt0o+?ɎQ oAľ٠`k> )}?$=X,=spsjanbQH1׃稉Z+|q=h(Gz-?y:˄#UFcV>Z:.J19XXUM@^3dfӏOڗoo?'6wwǿ!S_w/@?/L쀏f+?車fƛ(ΣTցA6_TqOǵ깱#`bi۾IЌ#8ؘkh\T@Ȩ1ړJHYӊ,o2MZˁ4c}E+DJ욬U@gٹmwTϸ^+2Qnr~.4m󥍼6a2b{{  Cwף;yʊm+˵K(qCcK2zٌ'~&<*P)4Z| XsPC,d 또@cy7mS / HX!$xLv ⍜&F<}˷Ï_{9=z> :_<|жO{ŧOWLK<xt-`7;7~$cGDU2n*vVJj.vs5-nԥ T SI홅(?Ӗaƒ,q@j‰drkQ.[tnl-%@\-y~H0/UI.^{gv_ _F5W䴓 ;g^~bVU;i8]ÂU z{{{tTʡXb%4H94-J~Y/GKj@N*H8:ᇀP9LҞ84LD5a07z"ቔ& J ISo t mJ‚J M6\̹ƜsDG>sk֙1g$. WθȞ7o6WwSs}"tM8IX 5/ zxk.W|҆&ӿRv*&>H$Hl|LI6T>lzࡾ_x\!*?D]=v,΃nW. 9]pJ_ySDŒNՓLD0h+b>G|![@"f`rb `jᙚ]NNHW2ӳdOAKwn/7~Z7^kNoFmqzr^ahЯS0% JA,&a:5ne`c=떏`$khH:`j BvaZo(oH8ϐM#6=8;9iikƜk)'.Wb > z~]D1Vn=<0KY/= ȝ5[!DW;Qx'1pLJ`.hfVQ FW~:3ȱ?'*H@JT^K}MUϮ`dHefTMT$48S1S>ȏ0'FcL*Q tctvChdɞ%=1 -?տ{x;3ǟ8 }/ax}g@*fIKRQ 2>Ru~l2GkB@85_Xd_ I)I95rqPs<;ph|shiHgIi[3NȌŪL"(Eodpۨ㊿k")̙ -WʷƟkmoG䨁,.HqJ+͉kⶈ&R>RV=$h1Q2,AFaS(WLu%"] gc(ftM =I80ea 8:05QRXX nIj b-T>Nfw| w|"|_p˽d+ydYƒ;"|;CQ&JQR!DXZ FN@F3+M\X b䤇syߋ88%}0 o y_Gwm!;slkD_xq PxƹsS{gS&WJ"!Փ(93>\G;G hFfH)qMLM]bhS}$ 0!օd%v.x.KC9t zL沪`h,iM%%"+NوaWn'+=Ԅ,K:[3٭ =^.Xo}y'_>w9ZvwE71x_;D/p 2a4$by @@TOkf@ӄ?VAaԋY[ ᾞg4e+ֶʛgT02 2=>;ճ={:'$~܅';y Uh'mjN&>v 0Sŧ u-ԦʁiAe%Q_I ]5 Ϩ|!mW~v~'jTO T˳2y5dr #AzX8VzXϝdz=['wfn|*J#??3Xs;f! Zy'8| k@Mw05p:BWؑB"iR"*Ywc=܁`Wo͒ ӝ 2 0y#d,&+{ၔTnjBHuFHdkK JlvE5ôS&IJlX!KKoi8xq~W&Ͼ{ ]9xKӖ`GK`¹3 |HfQZh6x'̡HENQ.i-cj*I)2Dӈ'~ZǑv`=2KC̓#v,R@B*bOtl(1p9dVH;Lǹ,ٽs۾|o@Ho΀qM_ 3 ?tAW.x.fЪ忳'ɚL4"X-誗) gpv77ի `yQdK$据e'U YVN["->, 9nJngj5I0xe2;pk7N1NT)ĊVq lDFQa{Q\qz6rs ֫!'ͼyk̷hm`@I0 A ]˕_B4TTv9"CccC:'8;MZ(<YyV i&ٚS 5:d6r e:|Z&g[6J=;OߵN/ڶoo?^^g?s[<<4V$;8|30>i|梤ؠZD[(17:5K24yzPX |=d8kM'vW n/fK0e'5$5% dT-Aj8~u J4 l}/6uM3߽m\*L *|f"ӝdss0"9ɜy̷yߘovS{vSzM`O nGY`H@PE CwݒE@ѿ5ςznM 2anb.:לڕ ☚;{MH_喫0"#╦wtӯ>??s IDATm۞>{!;Y{~|~Z>7?. D 1'IπnB 1/I,Gn\g.D~t,˱eDI')5w[P={y'kLf 9N(߃/yAvIE+vK+U_^:G񕶱{:| _={zk:'T\Dh 6֞oۃ?ntcm{k`8v]௠Cۈ%ITRY'0f>X(DHL<0Wh2j@ ZqSEi$ъÜ3Y=s[Lo!Q9*H-\ؙ=}{'Ifη/߽<|LJO/Ow'䀶i:@|a}ŴϽb0'w]8#BG xvxs- 9O}fN/l̫ S,VI?B={v.ZVv88yQcFyT/m'BN$N1v3]Vo0?r7n^ llWpS&VvEtS{`m{t:S{SXh :]:ĭ9Bp5ZTsJQ%Efǵ-u5yc5"I荏R'ycnJl0a K߅]-~xC%[l(Hȵ)y޺~Mu @ɕ. 2[|s]*w:_!D)K/'Wr=kCE+ɮx~&.'!2%UIart"vP$dgA0#l\c.flNfeO_n`ONT{S#X Am 0@@M|\xSτz 8Lξ0"eք&1%⒏o⧁UkE`Sf2 )"`)t'H.i%*&6F77*=;!6 .klnܩ.Y8%nk+Sd\7JQ}~-Mo/@@֏\KJ)1y;1?v9(NZ!L, O"냉ӕsj6x1>ȕʼU}jᒂU s/\Q)k'e좵N0 G\teY.B&f~J Je[\Z,0 )q2D2cX`O $'>dTêf\|Ź\?2 '7__x_kTV d@ߋ/+{^yB$zIx=$)!>斺1 FqpH"Y,KRd׫]&; E/0s0( j$T&L\~|/ { Q$~SWw}JDM o>wɊ+`oׁ09ToCrW¦M}~3Dؐm}+7 ƐfC bR()&7͔ΉA*ǭ`Cwp]w@f1l5riI %pXrW7KM^\QNtl.\PyKz2]_2dJTJ0%Cej l>$n(#QR13!90_\CRzXJcYfb['[j`΅ui[ż ^y 'nW\z) q$AE%YpFkYp 0/Н֧}@$O%6^ d r@V>jZ4 A>?j1<|< i&I_x٘b#PЉ33CiaFwv<Ǿo?C6mNQ×ӗ8~p?0c< t&8xR㯌4Ġ `K~$Ɵ&!9S,K X/9DCbԦ{:/DAOP &G QŽeTӬfq!ӻ@ٓ7mok?|E5R[l)W}My^ټWa]\+_[$sץwKJ-Ug2#!H~3U8jG`Nd}tˣQ!rd:M^#2<pabaP*׵kۃkum[Y f;.?J2[&]P:߾lh,ƢDyJ:ש,uRƁoD\R:y>m1;-G浺x䗚XND$lɭ+oqavq:f`t`dcn8{C}ܶr843#x V&dc xG5W 6sHwB<$L$0X( ekFcE#X0Agb׆(fc%@!p4aLLjD50&pu3Ь36Cs|>j.ѽ!'؎[DѪ27uӺV~r?@P2/$ &,aݼzƚLī62+OHy;\\mp Bs !D( R"U<;Ծ{QH%Wϔ\,[{&nmo=2u)ts~Ju꺾\UֶU{17sIBĄl \\>4zuԥMhfF/7K):%̮R+)T(9)hA\U21)ٳxVT0c`(Q){\{%^-G_HWR𑀕Q<ѻ=j熗o?CS>6myiOϿ;yS'izSO,\.` ؗ 8bq< vZiϕ}t`vP*K-@7>F"tvhv+A4$% LcZ`0'ݭ43ՔF#:W2Tjhd[xe|q_XS[/1*qaSr-Hʀpl+M NBĄY^HhjS7OM<_ԷGv=b@rC d"w+ h/黾 wν3×dmd.,5FًDю2~6>hZ:),$Wo.L|!&pV*cRIdIje i'[ ה/YRpl}nst_!r罇DMcTqt >Szs m6A*pI6-IbE^X\@iug=ȍ@ 6Sk[\`ҏ_ (a5(THSʤbVf{d5)ӆ |YlErb0_q>muJ"likjcfyu@V2$3o*sT (dz|r,O1lݮVȔH_&Tf b=1iZg\w楹RQÑ0FBPX+}w\$],'y X{Y%<>UשmeM]Y]&VkUvX <3_#DP(.K-]LګF9(d6y(sB`-,"/ga&Q'+վ\AS}dW;bŐ l,"VmP~,DJ{هư2C#`to1_W/)()?Ğtj\k5zs5~o ?'zl)0!{EklǫC Ͼ by4'k{x3' >s_0? 0(>h:r8PÃTΌǃUΠG#p Tt&քJd5}5)_|v2^v꨻16] ,8] i*WjuV՚Vm/`p^gDLRta&&(cƷ[˻4\H u42ɭ~Ӭ$WX`O6>m~V`E\_F;+k,hqvoW}ܥ7ɢԳ MBRϽ *a3XbNx%A!\1d#AZoڹvv]}Ç?xz* Y%`u- L + [ğg>+ß#o.={?<b )6A4tPc)dNKZҒm2 -RaԊȆ+A@@/ ^ FHZ!S DAh 豘:\/>n+9'm㎥T ~nW̯~-#57͑mm]ݕ C jà;Ћ_}:6 4'&pa0T£SUBJL,%T{ ʭSvcӒƅ7Hs>[r\SW>bm#:a@sJƒØN1Tb,}GHO$׺⋎.Yl"i8d_#x4kkxSPVT pjG2|%a9R"KrYnW @1#ŒFbȆ#'R3a0Ǣ䚵ˊkc6'!2"2/'63fΧ8_;}w2oO/j.}$xY- cziߡK IDATRG{Vg!ơ7}x ,jپ\^Hds\g>B&opXRcW?fJt4787t s s }Ӵ}v1yJc!Ww>~M'?|%ߣgO&xR#Q^S Kbƍ) x*Ł.L@#a'(GDIKM08:Q-&U*Ug=^6=/IGXO'~-WŤrdav%}*^e3|SWnzu̇~qԕnt&tCeΩu}庮rMmC۪k;;A o<3{GzFW*dvu!t͊^~`?\6P8{0_Wfjl{.?G׫wk\}Qwϕ]jes6PߔA+V&Z||Q)e A<QEc'L-F,ک&H68-0gd9!x6śQ8 ;7__NO2qHȎxvtS_Q^dƄ ex,fS 2FKξ$Fl> YYЙso;}wrCwum/]~D퀩+e|c/+ݳ5ృ&L@ g?"f/^F@I[i b5x ŏ߷q*p̌2.4IݟؘyITѸ_%0NH@+aUJB*E (G(ߛS#o(e_ zg-]w$].y߾x+/LLc q.U6jM:~ϙRRȻG#;aߓn %!!a\gNRjDƺ84o߰Ʊ;so^޺M.uܤ"򜢐ܥ\}nIR5?*@P*tTr{wqwhw`cC#"J#9#c "|c6sJ~Jv卼|X%BKVp!sC$#t6 e! >Ҥ5J6xpwͩ箽\ҴmuiG,6ݭ,>טi hO?>?/~ZC  ԊRÏGDi ,Wtb/$U{TI $ׁ,tuK10-МOKXWtUeZ-y:iD7Xƴ2i]G.inRYyؖ׸۱![1_ç "oΉ )׮mյY\ԝ.kjuM+vi.K)vhnwO| 9D#܂ծcW>.jM#4¦zP+1tPy|fݽCT5-+&ȁHi\f%׫0#W W\%nUIqfzTD /;j%,Yb ]޺P=X1VD(#zY[DKRXgYc-02Od,Hr\ ג̑-2Kl|ct53`εзoN]qԶp9"HpmˀHubu@8~T?F/=PQDyoWi'!Rn0m6/V;E#k ^ 2a$jqj5m]+vzed`j<3?a1R+/,H&E^˷`wRze"x%V,ʛ}n]FKq|^6^Wn,oP,y6/]w\}-,d;Tk5d=b?TuSʵk.u.ozՍnu#l x8P0:Q%VL"9oy'&QD8t1U_rz+5#7f&s# ք8ʾkfr|Q/juVʦ1W4 RUzhRENkڞV\TƱpe%EyI2r)7?/!{GV[v+^eiq&[蜩kfrl+6SE,HȢ*UR}F<8:sC33]ܶ<{>1}VZs @, $v.l4fdAЏIb33J@kVC2A@R*LUGJKƋ:ߚ.DVַ%Ƙ$֞.W#YAA쯽?vۅ^fkiHZ>lnpj],WZ]xK-Vºs֏ 2+Cs@{CT?B̄np{чhzc[H|07H 2؀߉e`CM-u؄$aYִcdE(畍AQ~Y34F1d,җe\~X 2yj$#17bLc$iff8ם}sSԗԧoKħe#X|i!|R[}rFz|gBGfû@ 5!9sߗ>ėiBQq*1yqh2r˚rQQ䠕/6,fQ-z0iIHGIQɈ|]ϧ!s&̥vnIƾ1|7T.| $/ڍ`3Q^]X4I/Ssh[:0~Sbu#l=@7<88T/ȡׂTnhenSsGywCx><L xW=ոbb[{pq4l"i5 k|AvcȠ73^"vt۱kr ɪ9\w(3\n+?] nL[+ $:I.RGUv;V?<.fr{a/$Yn Lu7%,S0z<1<J:Npa4J,qOLF3ֻn?]痏ϗr؞O 7S;,h x[ ߗ fȕ[(֏>.g1T'Jr-5]-ʯ ?W4  tñ4zcH& i!`E!ua%uIQ!hzn fFPvH˖!8d3 w: 9fEUUb;jЗ [0kCƯM^ f+b@M)o.;WIVxw܄W ^Aw^4b|.(ҏJ~t@f~nt- NYcz]VWEN_{`Qqf:KB0#/~ZRŏS  Fu ǫ̏0=RX,WU8 qN8ECYGDI!R 87z,FkzoH{kA[,x%rJYKn`v2?+%ɤ,kU&dE Gh)ae"SfӟzDXN4`KSϧ_?._4_zk×_JT\P(*r%%Txu'iB>3<tyK@Al@q$m| Ė oɪxLG_$g.]8a Tc8BB0Ќ0o(E+U*Ň9j,N8߫~HH!]O޽|gܖ=lJ#Δ]'#ytd <4~Q]gׁ8 {Ux Ituvܻ?4>B֋s!:iB`kng :R `w5o8"h.ө܇x()o)R9wV7㫶<ߖf!#s%sQ_.<rL~Sv˭ʌls,6LH {~_IasK^ģ /Iu)$_4Xg>xuCWw]S7s}z{_(xFH>+kAKе5d/ /~/[_;ﹿF/ּ g=#B6H,C'N+1a6bR2A2TjP1FdVqD+"fiHLB*j*UB'VUU%J/LבR&mH*b BJ͡~ی} ([Q)5\́ 2Y8yCH&&SUم[sن DuTGHuTJUV:P2/Eߊ0={%,)F>dL9, Z]aS^U:6!2Yq,̏#|YyQx[.fcYֲԓ>|XO@_2Kg<3%3[`?|, A(JtmY=Z RH,ped <"c.#bT 1 <52 fЙahok>/} |̻ ׀[R>~;#}/Ok ^xG,^~DJ[vٝN(D5|_];΃dOcJ3ue*ъ8UUfZ('j2F{H6l6_Y-W*SHy-koccmխ0;Q$&uݯ@oBL88p~ bp'xdy4_WF0ceS):R(ߛ~Uzx4}|0=M걢:fL$uk2Jf(k_>sX+;5'0v<|ԕ}RY>lj`YeBQH%"*^"2gLqU*a |o+u ( fUj㺐xGܒ &JO !d󸒢(, QsteJx,S"pr@Hک"zxE3XJX(+]/A㘚$V9IC ey0qSW#" ` s}͹kK]=ms. #O; @(=\c 3*?^zoo|87~2S[t^ϽUŸrӞB81-T =x9;bW§ z (U毘pYBV~j]Ph v<MV B4g!`.)(dvS8d^:]P-A|K̕0{9t Yp3BWIy!x<<q^Hϲ`RAħJ',lUf\ܼR*{g|޿ _[誇N DǐqV}e; Wh1g!H~QqLCwjRE|\M#GUqAcj J*2URa!]Υ |U%;#m=)SM'85Hr /@RDŜ e`g(fٹ1j9D.k\/" )3DEݐLh55FCW}{Sۜ4kP7gWXd#8G( Rb?t vVzmw`O~V:T<O\ `,_#GG3$IQmp,diZ<0R! crbIԣ!S/Q9̌H!rU/#Y |%.5Kʴ_ٝ}|k[Owߑ ٌa)5;Msg ~ȔaY I65&٪ F nqK ^LkG<ۡ7'Vo!ջwNz8)!=4"¹ q }YDt^ƨ4xVw+/N'?z4¦jf-w N!JEDB`UO6)!eE*ӲxsuJbI$Ɛ|~Pȡ{YwzѪGӇz8PUH |~U )j˫^&vZS?rթ5m8ݮRAvF;F FdF'*. D*b+QlxRh5g,1+fx``R㎅oYe;] ,lKg,Ӣ& *=1ԜFW6$dAd\㋞lri| !fb+˕@AYg ; eMΒ:7 Mߵ9tjө_.Ǧ< }{v>䟆MƵ_˄倔>>Hz@<§? $>VKC/ 8.XtIϬH2kV(.B"sKRWi]$lг!A!Α.*J)-SP ؅]k̸|kAɶuBy︹[f [J6Wa,wI:N?b&6g ɢ=+譵}qT+\T S z(QhUV=B=Nô|og -wKVǾ^EyvQfce۪ȕ~j<Gpq~xT@z)έH#YLbf:q--̵; XI[I*,IU :EZ'9l Uʜ/_2:q[2RDr,-|1`_Ld1XeCR}M&JOݰ1*gy]лk|s]?_s]>vi蚳cG>AHY{XaAJHWST5sހOP|W`f?8"S $h\%8:5HҐD$[9(6,-98; j]/wj}*^~P7 jΉ93ܫ8&ކn knlߡɷoMc۶N[x0ڏ0| ּYIcYp!(njV|$[QR)pżKn }Sw>?hOw<!,{K HΆr1C H@\V[hy ޟM~-0ppvw$sK9}t 삥AEc682%sbI>^ZJHMCƖj]>P4t NLza#|ʏ+#_+?xd·MbM ASVo|->Iፍ%kw="AcWN!um*AV^1!CVAt Qφ/ܨ eDE"AY*m{ìxO',31YtoR=c7ٌD șBX|xcfw,|/cY2/C'uH!14ZxReLGbi͌60M߷u]yϧ/ǯxjY=c,ǫt n*ݡWO_;Ҽ{cW9\?3s(ZuB @\kRj|`1۶ !~jd@ueYRy'w?J)l̈́]\*>G"FEʀ[jfL!|0ۥwJ]=,-Qzq9)cldAlȋ닚%&* ;2Xnեsx_dSH2gYW5YK>&[!@NZsMSE:%E>d%]~e?sCmmn4r:}|/{<}m8DP%[@v! [,Y_;_?Oo ?O!x-<5ʨ--tc,Dn#bbnt<`X 0ĉE~<CQnkEDR̰J}BR3t7 ?ZXTSWP v^sFj[@D>)(k.8+j&*w y{~ka„|J 8' Ixrѻvnhy-zd%p<yF>ʜ-(?5r|73tTV7kuM]A vi]'pʯx9榾]XĊ^k(f 22J/Ua:T ҧ HW6$JxdofdW8o,1p:tLDV bGsgn뻡ھkھo;59?gGjQ@xY0n`{jp(}=?_ @p|$#O |1#/q zB %NWK_Z$bk8-$EX]%#Cpãg' wUTRiU:SGcF]{H<>ԷTݹ4ɪ[zWCOvҙfJ?x=ZEH<!G#u*jUzWI;ӫUq\gny56' %BA֧O/8#WZm/Z2[*%4g ּx Ưi?0_ XPdy.]+w({;n\)v"4 +텡|hDP s5YIg&U哴Gⱂ(ԇhr8ѫsU a"~nO{,yy͇Ć>]uUWV|\Bǹjm-{e x]Bnǯ>2gEƳ7X|[?{E{3r >J.Xb}|],.Ǚ㉛x Rf]bG+nWީF-Kݴ͗uKɼ*\ Ԍ* } }t]Sw]]is{OC0r5Gd1Xg{k]ef@G٪' # &CڻvHE+?a?tz fDWH "gA$AZSw\?6jCE.'JSܵ&]Y<*) ˝+l nM?27Isjx oّ1()A1B_8ZܪR*Pc@tFq[Uc~lM͕z!s@y|10 G , @<|xIt̛"S;ROAt+;e׫5>إVjuُ fg2]bW%ŚL!S:~M|d<nX?]ੜ/tuLJ&Nt QS|܆J7FP 19̏d8<JCgdօԀDAq,yA䘂.X@A,G8cUqKk ^ǯ7^t6sSyipsfa=ĂpwXsZpdltqi3 !p#w>K.R*-!@G¸TFF]aRro$# Jc"#Q5\ HІDx1)54ܐ^ZՋSo5-DyW9D(.mRQc>l蚗7/J`f~.F]|50`J@ Z^&{햺$b&%X}W(RDLs <Rt^쬇ZAÑ#0:aMWU#WWdL^a7 ̙ըŔďc644 z/6Eu+\*w9w{Ym;hYU| ~-;HddA#c d:0 =cBF4$0 ZX艏tm瞽w}23sXkEX+"2+k`Q]ߵV1cbmtch(fLOV"5!O%O>gx89Ą7#KRߒkܳN'lmY`y? t21 4VMj:[l|F4+0刌#۝HҦr.ډI#/6slhuNn0h^sMAkyuY4;-Q}0G < $;@Gokv-:Rx Mќ$wlN@Y'fб"3fD8ヨKKSt70~M0?h~շwt[`c3E3e08"F3cQ_<]aP1Wi]t>rDzuoW{0>3><|D&gc@o{Ha ܽrLFjdh~i7Ӻ^i u悅o?}U\膠qkB O#yW;{1T>q?I:&dn^p-إ f= KjTY)LQAKV˅MEa$O`r?'`3IUn I){?ޡp'3ĩHV<93`KKӘ-WTծ\;t:Ncy~yߜ1J]L.쩀 Ls$= i`8zޅBZy_@C(~@i, 8! V gjB P |e" GBAOoԨ<ء7&S̲!NeNU̜\&imC%v@â@JPkOo{9A72{@w"~% {iȾ:DW$g> vVHB3`j}^oXY! -d@Byw'S=]/#_0#-,zV>w.qKdc 5}x|a}>v D6mW7;D˸xÝAw\H@PГ";CF,BbbqP/)=,8k#dz&WC&h5Qk~!ֶbM烞` ;ԪquK~GqG:FGC*5F2>2kjȎǶsGRKR; E[> Y5|t\!%zP@p-1Ħ8Ey06 qZL s+.'ɛj]-˺z9NΏ?;?~S~єSWnsT~I!{Z& @+iqe ;3?WK5!Ń2(60rXqwC;xWq53IQ*r>[Y"=N[i'*CWZJ-zӾ7-T 3 dnLhEZdU~;]K]Xߺ\ \1b⾾aDXgQ%48B ݝÃI׻PpSjXh}Z:}$ deG"݃;/0y{y891fciW;r̫|"dls:9t]t.|;uC>qv}A.Y֢txW2?%v|{}g{nl`_n)iߑes/sU&VDD4^c"":g69wftD鋩in|i*5}mm]Vuucgo~')H \ o4Q} ` B\$zyw߿__>UmO< 5tixCT81Ȥ;9 IDATH N@̟ od'y't8P_XM9# D3"rDu jlo.}0쥣)Lz'mN [׀"O^Jec5b5"[ͧ{]W9{#{jrZ ýJEB`lZZ:1 8x8TB WÒýs?wWKߏ˃ Aos&DsΈV.)l XQЦkjѺjĹ7 oAla}/d($e$]<.OH+7" mn#z1ykG<AZ_˒x%Ոj Yk3 DSQ8Q Sȗf >4=ߪ걷 Ŀ~u|g/AxԂf_xp7NSxU^ZgyQ 1G+(55\!b*1 "Gbw5 bQ)ˁ ڵ\V?Żqmvwu򾅉84ju,Z\1d_n] S\y. @ȫ߯Qqiyb^;},>>ݝʢ0߫uH!U{H;'p0w&w&ΘPyw0q4[X._ B^#}xFܮLcSk;vUׅV{]9 "V5D@᏷sqW~9 aBwܕ:{$]j4St2f7Xm Ɍ՚혦U0V KcfKrm,uf AJ7 C{0fiQ2[ ʏ{{+"vv0t/1? ǰ{hwM5M6eV:Xc]M̂4FZб} ׯ?խ{@>Wb8P-193B0c(MG*]D!/6 LP [͋;49%q ~HTm&]" iqy^@=3) DD|&0_br&9W^? ? DϚɱr\eR[+\_|CsݖN7ȷTY"|*#_R j kxVCpp> NKU7]@L8D:Zc֫V'GVZ!RJHX.KJ9yOg m NЃ!^Bi‚,iak^([ިdǽ'`+0l8'J[Vu.8@ikm G֒,܀YT0#*ڡ֫.vr0^\S"^+щ) / t092WSbmk77S+O~.AtnG ;*Ъ*\i)sלQwB,O_Տ-`#H+⌉$:4:D?9eR ^[YyN5+[&L8[n[ wT &#b|\B3*eM)"ax=<8!@.ӑ94l3F,i6][M}>\WSYcYuY۾)㶫v k99V*>{>ןؿ,!x!ќA!Ũv/r(4VtL3q[\˗ JB uX8ܓoi4ǂн_35Ss*/.*BtkE;A$T8{M|\x.DݡוL,XsF=%W+!;TRsba),8ЛV'HG)|0FXP5!@}{ޔܢYc+8Ts18QVV(ZŪV7-«}K!iƫD^;'76-u8ɚ$h8Zb1r$떚{3a)K%k K@c) %zf,gq80U~ :9S+ 9cuzRKt.ϧr*OT_|骺΁TgmgM+UdK[P*@=Pp0< _1KeJHX'4B M"f蓰dyTRL|{$o'z j$C0d^vջ9S4m,|Bύ^D~&gr`2l#/AEաOe1oq)Ơe^̆e$JSePpýel=J`K\k71!0vjiڭkvUVNr鼬Eִ%:O:q 1#UW#!1_樜3[Ws܌m;ӡKŗ~,9]Zul=fL [qN1 r$xѵܱY>D!̚ \>h$RӔHڐ n =CKA")αePN ~USr>><-O}[5HhT`kUԊy s W݉|<+xU>JK(5_vŻ) 5$nVom.JE"5}fF:&շw)'يEnQ7y겇=`^uW!1Yw6#y}mcm-6]HVĐ;~|g  2 +W&E0xAUCc䅄D8k@ȫ>w̙\Vi\UY,E˲Jk`N|ϔc_+ _>!a FR [uEI׆M+n°iap6eNR>sаNnP9i1"r12}>pbNLA7^cZ4om,\VճuyzjǮ:?i]k;|=+ud?_-R`p0\Ѓ_k(&B=aFؠE'PdJXU"";o4¥CDVEjA.J33?<IP̍ b=L8f+ 1YdcP2v(6wY'.Xu^yӨI' sqI-di2b} H,6i׊ vH~i}5ZPRuXG~AR<+s%ZUW&T(BF{-xp`p*gJ]KF( iίll7F"\x}a "Nɉؠ4*կɿRwKEp6V{bꩡS,ҶY"f7p*9vxGA$MXe N a]ILMM{;>mmʲeuz9Oc]>hcw<)#e2BRSa \b:rm[9 ׼wk^v?%wľ}6#Nd@H,9b]vSi&j( b@ @D *0*a.T؊0ڏS5e6*Q7fdalӅ@>ſnJ.&e%ZF1(m7tg?oDk|{xʼL xӁg.hMShӈՍ ~%4bUMkkZm炫F"w P5p-ĉ U'Z#̨.+ ,U *h6|MN+fm!](JDg6Mʜc0'Uf:Œhd9" <1)0 q-B&<$Ct6;ڮk)9T6z`\+>_Sj럷/^z @_A@TL=Ztx0q#d)b^a[1L񩪖CT*1V!A &ǙWuq'0(?h-k5]psCd -JЦ7Tՠ8s8իKF"zg7iۃ7ζvS\6Hw|9n} . @aI=yn8$-\TV05 yHLp4$Q`,7p`a J9%sq8oZhÙ v]߷蚪lNe]˲|ǧ}zz)A ~Տ==mX[nwGzס|A. 4L>ƞQ$1B0NC|B` Jxݔ" UwDG"F FE0,WC89Vq #\2] |MZךnx5r/j\^)؛ &UqR[-e U|h|ay[#ev键ZѪ-˃ώl^V2U=F6+sA67:3UA"BS^I&aS.# ݫb1"L[%ζI,kL.36 §2v_9LJ],RKcY4M?]1ĚR QSWAFiÛ%t2}v͢+Uki˲mʲe]U\OO~4OOO=mR1sXcObcmy&'|?(g)(GQ=D>335`Ё5;}? pRe><k#%!:9Cҗ.0u;,FL[ @"FqL9ڧ\ޓ\@LH~ϑW`_@xEyQ|XSk:rXKO/bGv<@ש$y48"upoX \? ?81fͶܞz~yev;}v=mr݀ۤ1 fUcKgthgɋY$H2j,"-^>U{5u7p>0HM@XX? dLTd55望w^:893 `; ^'٫jfY{+]qvMIƈ}o֥9w# b:79X,l>Ҧ^fi]ֵhUgs)v:QOgکK J`MkɟѢ̀G.ex;A ,#P cy抾ǒEfw32L,V@eE?̶NI" 6 3F!oQe6F=c\"آa014: 83 zwMմUYsS˺|)KUO~܀k* {^5\?`%^#?ka) ~vwc4( P{*X#&nP# ?STs"WeФp~ 0c[/R&bq}̃aSGxiD"tBLZTU,ƅ HD@&'$7> c8Vb_iVE~b=Ď,.۰9#͛Gۺ֫V|TL+ݿn{-R}zE Row g"h@HY3,wsm)ő{/;_,<."D, 1>m ݏjdH* >RK\KG:ksCrsZ5XN39zgp,.ڤїk0AL﻾oڮkM}>6\|*S]ں|'Ȁ>@~{y 㸅/_/$ #zSڣw/A| F]{ k:4aƞ'% bṉQ=Uކ t(KxP3&2D6>V—`)SnJZUQU6x^W"(vE(SxhRrHq )t0ȵHZRʥr)Xеl}2]$3tezZ~3cL9[JaMi5 =x*щ؜ f\ݚ"6eKpa!oM*#!-Mn\:$ťNƢAs5p䒏_ 𦉧k Sݾﺾmڮm궩ʺ>t>}<^W؜^>v^.HK gr_PAjmoڶPփw~/#8;u/^1JC;BMy`qTtz\qG@b0 ?0P r*QtTуе'Ll'UQo6K8nAaw!p8(z}/*bB'a|e8/e &6@HV|`1{9 CX?5TCn)Z5>w_>r#8,J|.@/ z?OVa[|֫XUAэ@Χybn$rqD|,SxWA98AۯF IDATҀx" ̿)튐Nε_9\׏i}9=A(ē=[gx|p_9 d-j=AܕǐbN'{N jm,ZZ3@C=uVUӜ˺:N7ǟ/?oc_)$ HmCgr+h~ì_olS5` %_(|]\Sb:NHiQ2 RF~hA.!jF! }`?(bBLA DdLx*q zPiVheAu$i&.H$4t%dWr ׾!Lk Հqޤ(yEXdw. r{:m]\cկ4;ZݸG]SJ`UMiaU=({Gݸ,m|>G@_vv pLD5El%k ؾ95NM K4$YiEʢmĘᠣuiK gVDlB2.9L:V>IǛMtPAU{ںZ@\Ky>~,Z}'?w7]p}/`ȍ^ӥк/Wz#BQ%z`!D 6tˆA%#B{BD~V my (!c|#xuH MTiR Q5#}٫2dc82DpE3*n@IjՎ5~$8}'?֌)72pJnvl1ys7٭^aϭ9yˡ R7|S},[>pU3^WrWB8V?XxX3"CakG[>QYZv +G-̠3R,ϝp}[0Zf0:v40 bvkmhʶ)˶9u}XOǏ?iɏ["C9޾>O_WH=oZn67{ ۅ}N+ 7~}K_{WwP%V% z*h@)! !:"0x|Ow<mP>`bTUs]aۙG bBH  g t4~5X[!6,Z6z{ y6!zOI+aA c^"eqZζ`f+QqWǜ0cV}ץE}kq Q\fu\jx i}?`u-9Q&kh;%h= @VSr/$"n8ONwM YreK v2|+V?3)}y~}`$c(grr2Np%>vϝyl|8Dzxn}6]הmStjӹx>?icg~n"cg׶c;Uv~ 7~uE)?WCv{U|K%*:6¹ZIW{qn}t(w>jZ~=(mWE1[ٜ(;9OĠ bwCwmXbZ!AP+ Ǡ'^g5ِ6ʙ/ 鎹 )=WpQ:wA%7=7[ lh/x͎Hq̌]'!g2ěf0Uuu:y{W9ϐ 7[21ĽÓʥP  N:8o*eu&UƯRg@Ōjj5{Bkzoĩd}MŃr4t cpE d^~wP(E`'`pQJJg 3œ_PRT%MZ8٤.r)G46^!p)8#Xǫ"4㲕aS[w]n[1mS~:F'XEf7,\^} sase+ vEm2C2JHh:Z+Z7^G!6 n`2l~H y}n8!qd蘌ZYh9LuHѦ %ݶ375[Cy5\A$KkJHVRU[qu{IzL~(2霓a3 _2*M5给LM{ڮkk_8㷧cu>=VS]۲*uvbf֦kE @n?p`_ܳDMzkN;|?2Z{*A s\딘R=\yrUiBUtN׆P(ޚ &k2P,สHFm`!kML3Qz*:8],_D7Mf,We^\_Y:6\&|[mGP/8yŧ8V BBQh +_=a}B V~m· (A&™;7׊LE8jOw "EχӦnf+F~S'+LJe(f-vULsZ6V55&AV5hduJH1|}57SQ0(HUoce8!v cܣ[Bcq9wmӷu6eYϧOTǗMu~ڮRGns|ۥO92Nn; 7~߳?jʪʒ __oS_2+ | ;gNAHn+[b{1S%+SUoێH5[ +~d<0să@8 %z2p#և>Tkr%HZ2AM|6dZDuM_YLnG}Wp#эtT \7v?DSRs\Wvε`|գ#^;,[+l;{Mu`pm;D|D╯"pBP Ǔ T~Po]9 ymm.-7Yr|Fƾ d҄{^j4e!c rLv'$tgz!`vkT<ǟO~4q^C4}.){W 3y׿}?o>j#$ #|wzߎ 8@pHީ&u({@0O(wW)nc%$2膨w{>-bΑ$zJLLOJ[3i-Af65 /0׸Tn׺ݯ7:Տ;8WMy1sBFQAI[^k]kź[X}^X6*̂s .q3 }UP ssH}\lt^Jiܖh&.;8{3/f.69oOLLSr95}6dk.18V‚`5K&98UUmqruyjNeUTcu|yF۵Wc[rC}@w{@% @7t'h}:=U)|{UZOUPA#?+:< 's)WɬN+fOEbƵnbtoH4";! ܖx0"2<,|L N9@JMym%"^he[54f@$n=Ju}\+iyi~-?_+Oqlci JǖuSZZy!]&^ 2Rd9 O"98z"hzFU%bJU1iU|iϨ4Uw!Z2rW.K.A>d矷83#cW͛f.G lv/#4*!y ]~EңBIԊU9 6uUTUT>T/Rs!~gbfGP}e;\.ݐ, 3$ ΘP*UEk pհ;P'`ŤKD/ ^夷& 1sx؎Š!:1mt:oR@[zHdNV(Njڪ0=, b-5S^.s.ngۻF q%tY1 $kxXkK2v4òm̟e޳5n!il8 B*Ӥ@ۮNGGk]Ě3@W"XĭG.=JEZPBC|Q(M|Y/L9R2OM`r,JvcrmWq*^-K{ʣs'G$-jne "\?qÿ+1ifMeSu}:5uySy|:\>cr֥KemRrA _O7:BCQ|s(_-AA=<҆l,YOBhOQ!=t$PD 6q 33P97p9 ~D@!;A Edќ0xZ9")ɤ ] |gۿY쵧ȕV8*e:^ BRVv!oUTh@qU4e|[Wql}Uut{ZmGt=U@SևXPH4qg*HqNbIy _c_s^4σa}k >v]c¼9&fNڱV^~#ٴ•]>Fd YΌ[|.}9Hx}xόs+^m6UYWr|y||<>5MsEIܣJp]%#7)/ IkϾ?`}ׯ`s!eАHwTǪG KgYFmXxi0U3Sz1 Zq`̴n'SoX^nO fj4[ d'GϾaԵz%^FcenOҮXcZxdPuUֻ'<Ե:d ް)#1 򂐃W@>o~؝Q3]S)_x%JT!R=`"l:<VU2nUI*&egD=>Fo@OHwJ*d;DE3Ԯl1=)vtLɼުu\E'͞A1_lcͫ!2,}H'L]5\gNe'^Qf ubQJ' $T$`o7/@8}jwm7eeӔe۔e]T?TO4st5ĸKڭ ]M ^k)>ÂO{>fjf]j8(pGU>piDv&5lfyTt f>/'8ښkysvRE\@-^\HmvXwIxc!&"`!FJxOO9sK6;,9~4g~޳iN{或 hR\.?*ݿx1!ܩp:x^'SJ%֮-L߬P5c{CW8BL'N눦5G<& ҸLE-88{}H!=8_y?åmb2.C eeD޶:&njk^a5jb?O{۠>l{}Kh̃b18C_񈢃 \3ic)@AQ8Q)EA)D(k뮭ˮM}>VX^N/NOOT?O~ZfӮ4mgj{Y3I}Y=U= 3._gӿպzMppy7x!ջ1`qL#h @yg s2_6%zp գ!ZF&"B&N78" .) uA1")-+JavPc^L`fqE$re&ݳDLd͌:}q3C3R>r1bjC`f~yɱ95DQ6|jDVжtG+_BdL#: ?uN(*Qླ !!Ѵrl\`\>[=c6z|J $ޏx_(fZ23HWS m֯OԞ51msI5f\/aNde mLsGd$ X 7D!`wQREAR0),Pڦ*|󩪎Dz|~9?Ǐӷ@kƾ`^_=ʆ%uD-!{ ܂~?@ok LQr/P} IDATj*F&@u(+CxoB J#,6(L-0Cko &&T (jJ8:z@3ozr=WԪFɋ ۽i?gzH}pFhߛvO]D8%WS}QwÂ~aIhQu=-̳Rw;ߵ~Lm:}_T˳`fg=4VF[NaOa0ca;wQUȻ;Nyw'3ȻЩ\f4+m&q Do* !, 6qf,+d6D")s~ļu+?lx9aDD Fۜ3*/1Lg_Sˏ+AYzz#"M'`A.1&}U K$&8vMTUSOu}:7\cuz:TUs]V-'VvdOm;?@[ / w?]vzUW1!iiwyU3%I-Z1)Sx9ܨjSC>Zu,clf"%2t&A8T }Yi8:*FSq-X "ơh본 ,-\j&KWY8ZC!( ׂ5|Ђ5+`Z'Z6۞iq{*(ok*3}%-Yt;?*KnW L'c^&pADמC(B)GC pqU,D(B"c@iۺl._NTǟuu}G劻v$_ïӂW|ZTls;~sc h= {A [GD3. Q=Ô e Ũ&jEd~ [,Tԝ]Yre"GmH9L:ye3h5e Y)vKBȥJZAf5(Yr˸ @f4Źr n/pqAU9-o$c)%ǨӱJ Y (w]ZYm5ͥP|lLژ dpO"ò2 #JlYI[&mfo tD9Qr!A-v+|U!>Ng3$۾o.uu:Utx:?Cy~X^7_`qt5-X9@9,`[d܂=x_4+I`v0ӢA@3K|{uV_L@TEEԠTAjTȚ(O])j&fh5ZaA2Nŷ) CW7S*,xs\|o uBD:6$8P(F7zw p77]q7.ԚŐsv{<*3$mX0X6,vz!m`WYST؊##||Ft> ;Ȕ}__D?ǗUyX'"_=- ;-լK5X[m^>W@o{XljnuQU1t0jS}ᛓ8J:qR!8hADoh5nCVx H۾Dq_ eD)ŵFsT.Lr jZ>MW2>ipOJUYΠW@.Y  H8xE~9^Xu]*!LvݴV kFYq^̵a9S±HK(d }:`4,!"(E@B`z0x$Z ߦbҿ |,eX$m9ڦk*pY>^~s%6ɫOڂ=$,ޤ`hϋ}wcӵXic!x$W8PA+Lk:Mcyx<|8^ޗ}>o/5kmW׀|^`UQ7*!{ w|O^_v˱:5UQsmAp'_;wBT`oJ0(6V8bkmA6F Ml;(cԫǂ.,/da+%QpL5#5hgUKG$@8)ܡxo1U暗o]}Ri/9ɸ&n6e.Nn4kht[7-P Ayw d){%@mCD{,/h7tc ?,#T@s{_?k?v]j}ijP IiΆ da Ȉ3F0\uexNS׸xK!mK|.mt!U$qjPơTҩ;+ I &\zi=SgsnZ[jT}Aĝ嗬h)},3Dp徦<+ \1`|*!ty0TB+w@gDoNC])2 {N ~ R!;a!¢J[Px=lK˒Sd"j1U~ײ$&yYUkr:1p&Sa1jA[ğ#$9ڤ4גa\7k ىM 곀"LyRCN₣?8q +p(<(D(XH `/{jڞ]9׻CvMR!"?%!!A*IX-h?b@Z,mh(hJI6q߻]9sܷ?gs;Y{ws뾯˹,S8?>px=_<޾._hLFt~-:V|l 5_{_TjqV-}!ugfv(Z=Ff  Qu]u͞J{mUƛaB bqjxQ5 1$n]I!Z?P hɽsB`&ƞR&7HZ++v2Nᴸsg\ @涖n07mVM^ @&Okwp3h}k׻| `9M 8[ZV4T> G +W|Pz0Bt$uO^8Aj,l&VŠZ4GF3@b/;|bfLXh6"4ٮI>KԾ[,|抛(ݪࣶiDa]9$C("ιF#b}UTUq.ɆmHgfx%QɛOOOb}pb@e&.Q] ܂r(Xg,gk'a"8H2\*yy9tww18^W0{kvc j c@Ɨ/߭:<2 ]}=3VwL'C“b۠Tݕ!'dτ{Ӈv3NO([Q3oLQ>Bpx؛%7jR )Y [5~Vk@d}}Q,dh"a+@<0ʼnk|S@iMiZOA<@H CPDQ%QVEf Hg. v!bsfa*҂" lmQL0ݽkjU npmq8gONIHOѰ42Aȸ K%qA?66yi56>0|D;8j^y \MGNUGKKU]|sp̏t:OǷx*)c |<$cN?+' mދ[.CN0LՉfШs D%U*p,ЕD|Ъ7xWT ߆D]K6F04==CU# 9! S a6uoVa[Ui@RMaDpmO*4UW`'$bn$V}Mh]}̩W5ԧB-3\Q:y94zޏ~%<V3NY}gpML}Q"˱(cQ}~z|w>nLJO/xl^}l)ɖ@j${ _e |?- $*Qo>(U$=>WP [vBB;X Sēf8AHĈ"5R51 Sh4!Ԑ1Ѵ}jԜl57XaM&Jg$RHhH@f] BV>r8)f7vZ Ctg) VmF窭 $n^Oܰ.{3*Bljw&ϰ}c=r6 Z2- 9 ~ TR. fSxs #`҄$d~Сsf9c5Bd8#Ɲ/:ElݵQaMՠU500 : "-j<"7_ߤtP>v6}kЀNml|h=AKw!:c'烨iX FTMիJKU]Nt\s~8\C~??]u5)kVLNnI;jMfUX_ VH@%hk/<13cV?j}\k qk5$ʷ6S4â=ea%Wh)Z貱ωۆ_|L)jzd04\.ZIDL\qq}hi{OFuL#pZF#:#NXCĊu~=Ez34<)x2 mCLi-DX8bJA~hh4ШJTVU;_h r!Qi۠vRsz"}}8 q 9k!-u- uL\H87bw =?)mK~NTf櫡 #4]*S_I'b' 5=}/^f?vNRzAQZine?18^Q0˜d\ }f0_VsUUq>].CwLJmq|x_Q!j?xs&3VGHLQWS4-i|=,?J4 vPvQ=6d;v4Yc*pK2k0}c=aSͷ~צ!8[^KqLF$}&Fq- Z旼 ;""glJ=/z)jK>(kC8-d ,rŴhftɿ'l"0!\89EpuMu-Kk=Df}XzZieI+ ˅v9%Q"Ь(!De])W{AtZ|?dlf)xbS"\8鐞:;qcj0.!o*tQ7|i>끰iqtYOS@'d+[E$`Y$Z^3ޙ]4$6M tr7G s`j x-n~\|8t;ǻ/>)}T{e!K1>&o~ |Zj_(X_'˟/KP(~"'991PpOqث f@>PT R@PA%CrX{~@hd1Bb)"Ǡw#E AY&KwrbU*+LFSYs}e_ol”2kD*`?#oۖW27$VkՅh޵*X\K2"ظyDmGR:DPb]F.LAvuyM9 rGN i`D&ZMGco r IΏmKWe#MĐ4ZX U!͒]>59LࣳM<}kQ mx[y;P3ԛ7v|81&?>|߾G|bwNc cMwcmǖbYWL¹R-?B19:'o'T[!npWE0WU^**ՔTfߋteo~nn^jkUmefu\wxu!:4>f x#4v迿j9l۰vlO몵l*50`.NjenN>ŅGp$\&G7:pELLMz'{ӔaY -Kt0pq 48Zb&`?f>lXkYJZ_ώ{ýj+k[ԠO~\J|V!d$БJL+U_x_}U2?N)?LJw헋?@+}c^gt(杯0\2. L@F#՟ ?Q5dx"t2Dw, YP+1OU@$t 5T$$ D$ӴRBMDPX+ᮥ byCӺ}/@T sbDS KRaAzgVkLf@5rRaOz?m\aG~l8󽽧M>֜K_1d{U$e^VK<i IDATW`C,̅^1PMj+: o2ta{4ngi[*ɣ5CK8]8搵t;th;q;dkj by*liÄEZHMh ^&-&&;HLItlSDbG0VHj//yUEϗ?Χp.Ƿݛܮ0~y4X;X:K~c J-c xPޗ~>?x5G̣HFX > +UCBjK2Hp 3E! isgvݰ0$jB%/Ѹh@aiTue@ZsyWEkq̢5+g5@FPzܦVb;uq,G1Kg4gg#=JmƳaAI[jgM@RC`aw> Ed^|<|O .O>01@'KۍF a"@`jVyoVR<ȋP]tû8>_ov\^\*s5SkDsc}=E/klY|g?+GQy3 1!L JSP@Q" U\HdЂT j=A!9F%hX[rt@VǼ ,7RA /[,y|E+0;q!5U:j({Eh=+׍nn/z O%+-.Rhdlnם!fPkxl0I[>skH,JrOݺqȥƙZ(#VЕ|x̗ , #D𱏡t$n~ ݎj=!IH}&6mb%]Yq Dj91G!!{&{rC k7y^dIYJ&@{2!G:"6prVuY|1IBhlD ֙ҹ{jzUZst:?ӺS'"};|^w=]k1tכ6}1 -gͱF')kㆶ~&/cw`?}V3J8HcNwM2羱EUX,v< ݎxJf/텻n;f;a:"uv=1rڸ`5aJaw#!3ՋYq]j@J~έZ,X>D Yf.wYKЄԇӬtpcn.=ؽk7{hZ %F bx6:hXaZT՟dӞx>zFõGM݂ 6as19 "]/ϵUy9.''^.Փ5@VsOA@ e??y?Rp5 A/ N+pӺc* ) jj[+P^m8O&Z1썢ӳmYu;ϴ3v9h>cJq&->|C6^}o~\+4qld̂Vˀ؅= w{F}Ȳ@DMo?WVzC&IZ7MXm"+8D6^=QFH}>ũF=PMk=WͺC:݅G̜!sJD!GGږ +N>&N$#p5ik3u. '{C-Jf7܏>v܏YD ^D#ʬ*|U\"/X|L>4~c~56 {ݭ S 70?fv)x@;i AaxLIZUB@ ӻbrJpu"DPEM(T p m\eg G@ߪiѾ|%e XIlśpx9̳hnk:Yiֹqi$lgg7fO޼">!wQﱏ绝ŷɵaq@on}Y]U63bfjC¦8'IHS'ۛ۟Ŝ&8 TZ';g2sʺU@Lb4$Py7muHp7GB(B/-֎W:3N 4E e% S JUB5!05"`dHw2K~ %U`B¬BUt4Es8t@r_|\1XpfW #o`}s긒ٕa?Aᳯ3v ߄eb3nWȪ"HwC% btawwD#]Fd;% ջkקS#ZԢmAi' r l@ho-“ 1]9c&)wΘe@ + TU)Qhak 'u89Qq tPDhۉx F0 0Q{Ye/8Xtz8vi'ݬEܤDdOr'SW[@SppD*ʢH\! ? B!OQ80NBLE(B ddZgX_;4tJ1@)H.И#Q+ G;qAEL'`t,orlЬ$mKzp0)6x/ Sg6<-LF@(q}$>>@<*RH9@5R4S!,?(athN9Xۭhtn୲hw*;fh0Ԑz{5ZupLL?_Mt 6\BCzꩆs`vΘ9 Lw6VLԫj.,uI:+$q\ >G8uJ\lO;gȌ?-[ZGnY8!eU2h|,R?OxOx[Vg];x k 5k+X+@WP& \n>+hQ `e&T(*80\(>"">ԅLJ9Z0ƒ b x5d݉c5n#`[H A ֿPBBTCw,[B Cƒ asBxyu/1*|i?6t}3knj%]=; l4a?.I{6(DLYlŪma[[T8rsZ0]\Gr8s?poћ=X$AD7 Z&: QLj $"7\cF[HYDYml4\Z4#ý*VU 6чQL&]!r)D@4RL@^7wl;%ZW@e2º%0E%#D.X6Z𶴄~djV{z4+en @1`Ь*ԗge^r<\t8Owy~xW<oݛpxηLFLH~wjs8^uZ|,Kԫt]uu99J<*p@ %G. Uq X.2GQ6"i@PirBjZ!PC i; %C\Vӯ 32c $$h=-yr!:Н7 !! c͖m"f˴^ǹٵk+yHd@nw5 4- KxuEnca"EB lZV455 E"I6RwINwhxN 4z50 @8kHw>+i$w6M;F/q@8 q56o>Sf;e&Fư*J*DzTsBR`S]n D!>3D}Ɖ'Ȍd*<-ua[N4YφSμd8=HS`5]`Mv/yU]r_OE/!?=<o7j?{K@GHZr,VN@ֆ] 8 1Dֆνi_7?eJG\P18@U*)hHNz|:|E*Vt14MY87WHV0F|BZO?9ADJc&HX)q.F "lo{l'+\OWiM4+t5EyAeIvlWY+'旹#\l"AnD!""N-`XyZj^MAj h4k@$f# EmtHmһڦF3Ǯu>WkڠzJj)xI\+ B _0k (# Ι9pFySӏo(XQ`]Ftm>c I\`wI0 >d8$Zocړb1:v\QHc&\ꁛU˹(NDz8K~\Kpȏjݛ} Ls>8@v\kBz5f5>zׁi/aK??VS3s✚^#}T2QW =AA4z蠴byLBHmKAیuX@$j&$0$q<z+BڮxbL'J%=$rzʋo<~Z /2nC<g7g!qd} N\Cˡ!01'ED,sjeE˼W5ʛz5Vx gU" haMqaiWM7 A*Kղ NdzFp'$jE2r> )S/;x \ Ә!A`^O:;͍ry@ʜRRX?Y K/ĭAs8?ؔ7h4Xz`h=U *3_Uy9E~r8姇p:n7~D[]cE_`Y+4 S- d jty "&YkR0vo0Á1)^4 "IcwUp +[C?dFYV7:^FK#Sq!i=?WфF ;Ick[O?}nWm{ݩ}ʼn}5x>N]Cs}6ayΑ^ 'K5gW;^Ս+nTnvn˼2c)J iEDeESK_{BDܨ8q-yȩD6=Iy֊m9?L}i C8#8E+>-U˩*Ǣ<t>Op?<9߽[X@.燻pxw9<+5MئYXk;Gr4ܚln+֚ f^wVOr-+JTB^PIŤTY6Ƴ?ǖ 0Y F^7 _UYPŋ@)*Af`؛aOYD Vw"*V=ޤ0ӗGAN7ٸQaf 1b!dKlRM5:fUd+v-B95}M Wd̹UXrl4mɰ{O-Q2SsT"(!$%g_>`bHٹ_4A&+4F ng>Iu6w|jҚ bs7{/775M%Z"J IDATn*= H )5 <{ʋUhU%=joȺMVNI[q4Fkġ0{t,6b+.<'A@MeGYOeq:\?!?=oǷwwwT~hdzyݮրas ĒU|ǵ+ ׂB&\ԟƦ+D/{|U1`F;Apjm@N*:XȌRb]uJhd4kT"F`dB=LkwL3{yZ*Z߻)} 9\snS|[36OS϶ׯrȺrWٵ.[4Kg9qeL|Ŗ24`VaN'iEn~Ys >c cmǦK#dJJ_]Wo)ELQ\q@rn{嫽feS vWe- (إbZ:P| ֠bOIϦֵksgƼa|` |DM & M8E+ #iY̏Xp:Otwo\|ԙ|N౥@^ >& K:>?ǘ.MnBzY-__Ns"FJ" 3L G3čq^[(uWy67<0Z;욡t$8ctMy?U+n'yS_!a2uF;q>*F&Z5T,NlG*g0lؑбf\?5I6"mPZAyuՙq0J4ͩƎ됶2Ŕ՜LtD=h>bNersj ^y4h?2NxFxezZY ^+CԮ 4>Ӭ49QqgWd 'u/@>{=Q*@#YGjʮq$:ܮjyMA_SYǢ8p:nOK Sz pX3{gx,Nj%s]-שѦ2]~~pgS.o>?Ufޫx(̠0%"LQWܨ",:JjETN!f"b smB*!D` ɆӦ4f^ai^`uW2xt4㫱ptK3ğ>hXFƞ3_7L6mj&aǻ[uq`թE޶װDZdxP`.(6^%_/Ep|8#xvR3V&cci4XC X5S/OwĿ_T`3R UQU[Ty> ҡ!1)bΠ"fgJSvoBHJjj}/UR _(GZA瀝yg&JqX+iGW+Oz\|_ \܀I#ehhLv]pb`=V(so\b8Maz.Rn'P' t}}Z`b$ A>-x$;va[IFXj[8#Sd0b4hQ'|cuAئ=jcdlMk^N@05lrrRSvY Awh4e ڐZƩH(  _w-;s-ݦIQ± c*ܠ)}Gp%[ϑ&xֵVBb05Ƚ|\NKxO.?<ޝO2+ojW0_%{Y&M Sa:cǒyY?7}n-.B+QEr"+ b={~V(O* mXmcO67528z?G1K 7pi mVOIMӖ?`]ikNl9)ǰwIb.mngZyWOE=HàL (Yonc`՝MGZ Ss4<|`,3EDŽJ7^}3P `@VNA^ _XYUXi I "[^븉[֝s$,boGz&OOo<0Akw~2%I 颢 4Ip"5=|yTC8]wp<]r||[ׯ?NSo \3X;Mb:lqm08QXJ}cP`)*L>BSZ1Bf#ScY;je(qdvj(0!P܌v# |(wʼnnW1||D^8CӏL5L# by֖q 0/Cy4blq k‰;uЄ!0냏a[ܬ 0#ivhe/e^AOS~|:_]xzj;KNWeRy}1/<_^(X߄;//_>`BZ7S40Mi*JU(y" Zh8Ih6j,2FI]+>i% R+1~l( k&0n̆w"z){'UKof= k. q_޹$򟠫q#*"ڊb6r"[ɜUgI@ T+g^|lY#ѯ GÆem큛=c=S=#i1\Eͮͮ(<]vLʼnA\3sHԫpv)ĊYY'L`΢75ppN'j>tzkVEunfH(iԊ/Us~x請7_{oCJ[c nWkЉ훚l[Հ|./'wKHsqaYU M7\HS {6@]< yEJ(O\P@2QUG c1)&8{. C \T EsxsJS5S gIWiX!Mm̦vMB6&}N;O+s4;l8́MO-̂= O`OMnɁz|1Y Dv l`uM@ cQ *Y {ӿ?B=k=BFV_󱋓J2WhWW$5OӠ'e~\8(ŊR(ie&t{ģAI0&ӂ1η1kPBrMA -,MAV"6w1\"C@jvǫQMTK^*OpOpvu:lwqGZ=U^d>胎5ӎS4[>VL/eY\wCT)IUB8g'U{R eUf`jiT1 YI9*P2P J{*FD5 )>rü!+Zeߺzڌ&TVFזt A>ŎM"L "=̙izl g5`j;A in3O98V ]ι0e :^)Z=uC VZø6|7mlnH >b]Jć(!y ]xUUQ] K"f[ `_m vixڹޒy8X|X4; c'G<& |Vi᭺?X^N8c^p>ȏe~~ח:60>yZoҌd{L\qZx|-Z_W @O{Ǭڽ G 8V,}PHyD4p'ݨ `*-b U<A (AQ"F-Тa2 NM91\ ( >qZd>X4L?dO 䚂~q:Qn%“B$myz;=` \~+UߚSvn{ŒTP 0 L/Rql5Nձ-t3jmKJ%\Cpbpbsev,1Cw'5L?ҼO4KG=(J>n7Yׅ[0U/M=0 +}8$ 3 A}d-ě@"7`ڋMT.:#y3aN`^jr Y[}ܶ |V)Gu S]PX g5SO(gIY9l>\Yvlvavc|Hl +gEQS*|X nb#Spgl f?^iOL?Ԓ8ퟧCWVi癇ihiV^\}*?1?7y~ǻr:Tyu..h>ЯXk STj%X#4ߒq \?oVKO߾1Kxܘ`aq YU"{ =1%  !LEbTYԩ|nB!¿"|xE.!k a!#XsWLk'Sk0܋o%0jѻx~t>r{2)0㽂 k?6(=o^Et lX{YOLnb}{4Ɇh>h02#R2v|P$ϺA]|QΊYQ:-+t+'#&A BZS7a2z==3Jh>׾dx36nbN]D`2ʠ*Ԫũcqp:NwowW;6h 蘳ֽ&)cw&5,/wNof?p><ND)p0!.AP&:Pw H@D@z=Pn5AZy ѿBC-xg|fT^}*V+g**C'vu&ubZY*~}aStej {Ջl[_+6 @WsS߾m;8G:/fPE|hB(p6'D֝~6 wCKI6N.R\pS8mfq}+}"Z^HǪU(MRoMۭW+AAD,=P n{K]4Pbk$|l*<#a50o7-`_L˳jq"/?ΧtO=Xk5k=S~R(x /~VsrA3G(nE|K BWxhSub!^W,`ԦW*B!!TЬy8Sd&J4]cA/2N,ao,/#uFJXw,kt΂43z.LKpR͂3i@Z [̞h֞G^BM:ཅϑn&}0pȍGp@3{0Z#EtH{Љ!҉o\@Ux)w6g1UW1[UY]JѢ|0iTt+ѕiaPlpkP?{oj[f {so{DdWE!$XMv% iHЈ؈K`? 4h'` IDAT/%4t ZփHC۴P EIfcDiʌ眽Zs9χ9Zs97"#kψgAc&dK !3d"r:E \ǽUQlO@HNUCC#JB[{T5muS=VctjC[._XKX=|12zz/Pm%] Gfgs0 @7DOD8Muts*tQpB^訞;;O22Y-Ȩ`BdZ\HC}0FtFvNZUߪ: [W}[=VVMkMS=UVu`ARr@DOA8͟CϚq ,'o\@mt{"ssFWQ&S%@nEP^&c 3 s8J5%㊰J:Ru!c @ZHe9-@g;ik"Xg=:%/eqkHuκ9 Kޣgwn_K((E3kIB@M3,@]dnuJ&5mܵ.٤y&L\&Nk KѨ`:!Nx6,(劘uXUGH#8o4J:ZGPgꀡ lil,%wydflz^{ B-ؙm/ V+0c@7ՠ*Z֏ݪVmn`CJ`Ř\Sl%kz%ju)x@ @㽎ƿ7;io U*T'Nb`oT*APTB8f‰ )g ;F&3JzHxOP0Xj/U7g>;Z'n( k=òls˭6tmj,2sK3_>0ӗuї<ߒ{[PO,.@se/ASPRHCcz>9`]0co9* Qh$x俉Y`XȜv pj$2 1r%kXE)N .e}.x0 : 2e0@dL6Кw:$K4Bݬ7i Rѥ$cfyuhHeq^Uɱ 8`82J`z^# pjԞ$.Cm]5΍GF|x롪N2у%Evo1S5 XbB3>Ǐs=T˔C1@'!p[ފ8+CMۦr:㱮G?~}(4w>xk: N˷B <.aA,x ~ǿ-?7[sx FbcgBA0P !3_aPHok&cy`M 2 QV"!h4+Eo%w/L_($+*Y_ +eX?AȺyk*uUmhkwME{uonK2ׯo=zlm:e5X?1 @㽏_N·G"Z)= x#(x +>Y $6Rb&Ti*` !8hSF!BHEiqI׊ Ow.-BmQ烗m dk5Hsh:BuiẺF5M ۚC*·=g6ḿ'#ػ x-BKM$rW%&r{V獴Hve"څU'ƾ_ŝUutAWxfPT)AHnV`#Ĥdׁhz^@,dHsV"JOtf#J#2vIa:V E;AF)鴼ݍ]14D昐EA|ꤕ舢s&wjtU@ATZЪz+ mojꪵǦUm;8|&3gN鶫$v\5-W(q ^xgr'" ;Axd# *V| K}5b$ԃ$ah]xӁُ TyxVJ4 wP{JD=0aC Lv3ɵ7BJL, f&H St%!I VQ"UB،7ư&b|Te,^x]=P]46 OgtσM3 mLlŵ~g^\' D]6,*m_8p/g_#/ ֫ UsF3C| *}*zobG(ۧ`,w7]a\jit$9 ".;^^lB|"rRX|:NEce_:qh;5:K"Ï( wNWt)^UZնi*zTm[W=Ǻluxm(uSuO,s5;9V-:5Edǥ R @k_Oh5 =Z6v !jBmּD#BlI @*PH`e&Qy"f$UEBۓۙtkS.NiGiC0`CeC\_xy8>OX?/YZoy h("!4d&Yla&eR90,{z\J< !)tE;V2vw! xtG-S̹s ۯ(1naöfmW#=YC[B vƪ:b@DiŔԨ}42%| Ĥ{XIHTX.􃒡@vrĉJ|[X9[U=CUU?nw?~Zw.:[$_(x)㽻W=0:~̟lqӄ[D쾢XqN?D4g4fQ@IφRs 'UԨ(~zwuk1]\$Z^9[ dmeX(Z=G0OZUN+}L g~hVUqlNH.W =l.n'5j]@ MuMhVH%٫vSFhg4~f/;I;sJ)i 2t4i0N4EIjyf"*:e4Ҫk{LغCeLJx~|Zk}Q<mVK=M%Slǚ<͏cB28@.:?÷/QoeJM(TDB[Uu _ę&%I 2\Yĕ\鬡t̯O.Η|RPD@X /!ZZ*T!~n3 E']4z_4 `$۠~@7hZ>M;ࡩ=(za`{FۅEkبiƿ}vʳ0 ZhBʡwVn' onov}`Ą>}^.dp΍ d?YAn M Q]dQ&?3QcSx NJfYi< d@2LBS-fXj?t(:_sBD11Gv!R!j%:}smh5im?RĂ5'p+뱅p3s dx!4O2 _ \['`_;jI}A+ B;> Efyk薕Q2PwVi=45U).r`6?_N--:c;1dn;עiN]JWv'mtiPu:ށD̀9'>n'оif:Rfb $`EmHtdQ/2_,m&pQZ}^(_ (֨2ehtI)άkA;ɑѢKswwn)Yy>( 澽36z@8iElvؼ=VmX5}cǶ>=>x ]3m=xlZkV@Ɩ4s`[@q x_Y? @+/8{cQ5T*zG| 7`p ..IfMJafeӧ"6!IaLm 0=%aBeBh, b\bWNJ↊rlRAYųFȂB#}lҜƝ'4țCQ:FN1;ᛝ~/ۅ쌐1ڥ ȗ zKb*h,/QEqIoQw7XgpNɝ؋އ9*Q'kzU2y (8J"ףwҲ=lVh-w(*/٦Ğql@48`F7ChڷucniǦZ[?zm9ذE<́ ,!t"@qҪ𚑎ibA$,V'PP7JAYcXT;cM %+62nZ(O ̥l>\PofBHfYtͺ,<Rr #ȭ3Z'w~tA)39uAAA*@_LcB 5Qqͱm<hCh=MU=CG6r,jṬ؎5j x<0ڎ?@ }[?^}lZ1-;v`"A,N-`U '5KEO^%e@E#`UHМlmMCIVⰹjѱl-_X׮7]bӻ 1gi]4MsmQ86_s@]},AKX45e8.Q )Px@;V/[aCk_?۷bO̿*J0:~jsǚ7-s9z+85ۿut>g>An_,, fe[G+(0VzQb:$e+V0)],(j=-Jr~8{#b+V+H c[ C ipVVʠߎ mJ}tEψ:DctsfhBISևIxS*7LV IDAT^?MG `LNba>02[ؐ@fH&CztY xd":t "ZON!ĝn4.thS2h)J2AEK]ACjl 61xpǪh㏚[>m.<\%|xcC-cx q*Rs >ނ~iwg_2A!+_An8fċZYKԄdwt5cIe|Mf(*&K}S0эIp ` BfazRdIQONEbF$T_9~ t!rQ:tb-=-79KBTV)9cEx qhrg  NX-+IHָ:ם^͖7buJ6Ef/|t|I؍x <ԟp]>Ч-WT6*Gx% JN0N}kdxtSϵ$Gڥ: <0K4")A4!1\~g@"=Ehf9 -3BNLc#g|]xGI[nCWDs4k.d(ˬ)%$p 1i;U$>V!% >3u"C2A|Aݛݎ(Fi(0 oĔ(@@6 sPȚ#;]E(H13Adi$BE(H,5B"%Zḭ&uz/szEh0ץZ7AP hC 5A\#.WiBX6kG[ۦzhmu0:jy]5֊qR}st?7\'j\u|% \n  lp; 76gT0eDI옱ԇd Xc  b A *4pQZ{d9T6Pva2'KwU??Wk{̓nd΃(s͂8M)ij ~k{&̇NBߛ UF,4NԜl` NL[D;!tԊ5A,< >hrju bŎSMd>nB7@77!}ཉJ>Pi@&d| h }K$km$3RrF$Ƞ"BۑY2(4`' K\6V\:ZRODԷZQ:. T, ^4x8g+ڵu]4jZk+gՃ*Ծ bṭ `{S)as/[َ렯 q 4[`qupy{0 [!r Niaf;"|z3)> ۑ^am eh`` Qp ,,H,LR\LB]g<.^֐z<ܥ"<~xL:+y lt^UUIR4hu)7)1͜9 >.5e7D"]łGܸC'8@r׌Kti(2ttڏ ݿshI_4 +srAQ{t6? ']zڇVuuCT[gm`FoVubw 63{lɤ8G߱Nq xlxl϶ոZ?|")z5]e@YABRDx} JYJ"J@A!j l̤rtn+鉲qۨۜCӰ]*m=l'3D3gɂyY 21ʚc2I gD H] d+>YT8d/g10Quȧk^GL1J&1> ΍tnWl"P2Q{msݝ5sJ;Nc4BC#,J!}GphL(ە%)ً:RڱJ$1!Jɏk h [Q_GakѻmUY[UM}l>[p?pӱqR5C7 >Ne7y[ُ/x|@\}~wwn "OE d)3E`DBJï  !,t&6kѤ6΄k93BfjY)z3ѴP̀ #g>7ҙEYkiUtWz.gY{FsFZ;\VjP$!;]!UE"{i}-sJ!s-Yv+ i@h6YB'y*tR瞜"ʤAI}L5VkjʻjmUP9V7?~۷(KZ}j l1P\c9/n~ t;1EP?2M|hWh5ew2 Vڙx3,9tؽkRG3 sJv}0]8%6 H.}@ J MGd84 <1ܭ4uH>CЄt=sBr<^֑nZ Hՠ^bS xH׮k玵Ucͣ9s{9~ >t|șC6Svϥ3J$3Wr?5?S?{!"`:*QYw}C0> 1V^:wʒ؜$>I@ QRS-TEERG&FQ)1uF@]a7ɦn IR]>׊X]v.l{iV1o;皥'a 92 mI Ƶ2k>V JA+ @sFGPSjA۠:7\%\-ws$#ף^!4V́A=c3]=(V%Kb5N1&H.WLJRy{JD\x֫^h:+ӃUPVHj}KY`bN@= y b?Q=1 Rw.pSBS ZAU66xx4Z7 @K.Q d:"spDu  *"/1cLgРFӑ;yfNaCJcYr^Ԕ|$zX}FBg וis%-C(=΋q"s#m[I$NLHt`#C:\gB93DDq^> ]PDr{(nS}u2dĚ%faڲ]>\CRhBtQIUn/n]y>Tz'!ˠCk"{-%) -t|^ #{ܔ^C|p/.,@|tOWxk mm۶ugʷ4M%f0l‰k'%Tk#<^[Dlu `<|<< @:ġ{1jG (p/C _xdXc>LߩL$7{)-zAC詪WE)P&)ôKtÓdpEz]e,pQZyS3OA"nowϽ Qi.eqU&[d~hQC=#8Ga[kYm u.uĀĀ$T܂U[_MNcgJ3:&ɳeV ?i׆qpD B&1J81V0zq:wC 1qZvRGEnW#@E V'*O?PUTC1:A .E=(wi@X"PH- HL\7@+ͱu\Uy[7gڪv}T޶ZEGs׊1!3,88N\m <:Oͽ:Z]u}_K:yٿkJC-=Bp݁wn[h$`/+ @X@x)[4IZZ>(D"r ְNdGg2tv (UdMjJFH|^RwEJY0sZ踡=&!gрdX)BUo%Mm|sl6c]?V~}lm}hl]9{xst\?A͒,0 Wq <}28䓟Ǜ7??b+G7u/_1+ X_N cV&1 @(&6!xC垪f*j7%/j_@,Qq0_g4$ \/}rPO:|~9 {ٮY9A?z9e0<ƈWb;$@!Zsֱ-iXmKhS#z\a][C$W ]&-bm͊N1Qyi(GC3C=`x8QaTCU5n7}go-mcGh TtVptnXQ[f^MqeLkRMC +r9є ^5P߆Jk;[9WU+GoS?e;QC(>5W> ݃iHnbhFz$RI1ʽw`aJ@u `A+QrR>W@o3ZKư}iB=NS3tbQYB .@yQMoJ؋PAuI+*Δ;RHKҶˠ{ 9ӃQD>\LSs=D.yJz=J|$3b&Cg\h&,줄EϱjDG:fۙC;UM2ȭWF]E(%[һ_vUWi>׀@@>W iG5a>8h ̪AUCjr"UΊom=M}mս=֟޾ l,0?n: djJW%OW @}www; !ֹs?J?Wo* `Aqa0"+f|З"Q%YiM>AAmZ* ݛ M !@+*2rQE!nڙzfN$'NJ`si ZN:4 aZ5-~B3K~(&x5:^,- X8 |8󤭃-m)$I!K1+R= B=PTsr;N Jv A O t1J>M=qT 9z>R lzwZs$Ght Z<#Ə#i:9PxPx@hTT]Jh58!6xkֵmemh6C_7Yk]`j|,s;i*"/_;j) 9\+ m %;w|@^GСw*GP|t';pbB D"qz 2B~'> 8Wh eLVI>>Xn9E m|0Xe-@{rjP?.]{kn|lly-_3!x3؈fA!>容h|s%I |ԀĶhi CeZC!OЂީ#hI-Lڳi c'a2x.4 h0˾(uL n>24! 1P@@ ʳUȏ׆D90GGBtvL̼G>R|D f#Y f  ⭊]gmv>>>bVHVsV_[ǥ[ٌS514-Ut!xk2'Wr_g'|~?& X8ca/~o rՏ| 7|#k ̚8#^dkB9:e7{AEqٚDܵLA:NiRN/ND*ؘ{jO'?I>9gŖ2ߑU^Ƣ5cmCvi6epDkIĜ#>)͞ 6Hȑ٥ dR1()a݉:oͮd]Լ8YYg(,*rF&t &0dhC!p#Xvy&WZԅZnTcxkʵ;[%rn\>5 N*B> 2xK:B okl-momZk[W7֭u9[ж7:(ơ1NWkRбzx p̵sVGS}_ۮ:o?ՕI;-y ݿx*_zG{ 5jjZ0F5QBA{b3ڄJP Y4L]@Y*tfb,U)a;fsŇ,FﶃYs̸`it/~ 5-xInYj Fp~H0RD,ܹ%PќRCRdL$yC K{ޅu9f\h%#\qJ|s'~ ݝz-,rxD,1)<^e!d@һEݘw( A{(6,,tur5d=T͝(V$!|T" +#@CA6ڇ >u96Msuhzq{ ܟf/v,SVK@G8~8Y}U &:c:noor7/a_.:Qү1#ngUEZL@#݋b#b8 ΄Г.( D񎩐uMIRj+s dMVgXIu= my{h >B5:4Q[|Ix F@҃nz/܋c4R2Up1N(Y6dU.޾͇oEA;dbz pIuaJS*,VĔ|C#HlJA(7:)#튆XJAҙhi杵Z|:tGM 1"=@`U ";Ds][m{hд޵w~ , `a$!@OTFw Er9 B}FD6!*Z ӵ$4YD1z|XrꉉvI+hf1&hhQ܈)ÀaIcgv;n^s94X'p7Nnb?K|h #B!n 4$sEPQ Ö+EE ȵ!{ʃdʟ=v\w#4ĉ@A mZ7V|:455Gk!T|]*ub{m-cIUh.[/KN||\u\`oF@`!FD`|~ kU|(UN 3ĤY @ hCDI=ҙ`b 5 cR"GQ%DSd$+aƦwX `̅N ωuS{L:?l81rZyYkߴk屖rJyB]b5D:D{`ڰ"h9#eD:&L)et*YlϤt8^Xa#A>!(tƨcrC)RbMSH'3Okd>v;@ S$+i>Ф AYELڕzc9J`E1)g? H(]D\#M*nVGL7a,{|4*J[JHZ Jy!mm]Ӷml[9ob-ܭִs.VX(Է Ǡp :W=GǕո븎o'րy|PZp!؎b˽[$U|t]b&8k[X6s:o|ƆG}4Vљ5C6S,9YX,oi`UY^\u.9ܟ*VO(@G?:::ƴ3g[KyL1D 7jD sQb: Wxe0Oˏg!= BzzaB:3;FU Ebbc͹Pa˵k}ʒXHs#4p4ʤDYvN璐{-U$P.,]eT`ksj?(YєHrXԲc@;>J{#)NFnԂ4 ; P-v ;_̖EU!}y@nϞH@R ߁eR5@1L &g&x[{olmkocc蜭]1÷}Z[+`q?V!9ֹOa9k-VkCpYRx\u\OqwGܾƛ7+|{?9Is\_/S9{ov7w&0RG8;hŋ[@΁ 5Cɠ~Ti,3Q P#ϴM`fXW|<8Y/$-ۜ=pnKytd}]Z&$ w6Ӥ3m_3EPL޳@8M}PKN 4 D 6))=;Sv!Eayb?;# tt z+n-w΅\g_oZŮ0b ' >c x g|_n*B-Z, ,P x)FcG   (I}$mY4o)GW]IxǸ1B(2ؙZ1B́QݪYS.s4HF+U t](dM3!hihZn Bhh> #(C#,i:b>"sq֊kj֍sulm۶msuh][}9V[րGx&q5K'$U >: Nw5})wdd`P zdGN7 ~-WA腐 2M ݷ{Tu/ ֍a'cǍsX;%7N稒\؊v(Ӧ,,L]{.eՕHIIM5;aR B$){]PɊ.Wz }T[-%1 4vQXE`@c ؙ@Nhǒ^#`"˟1*~ wRJH|G@fǑ$<"jt#.0ZjfsE.s;,TLh=j2Mf~&r)i <:ѹYu=TGqa ,yNGG1@Z']*L`VƬ̲>^$iRm9(9/ɗ9--^ο_Otuuy䷌Xxr؟ H2yhsCt䔙o05G$9x.'~IAA/?g%`2L`* {@yw`0x8d|xE[:z;Z7 ;j;"h_p~Bְpr 1(#z`vf=z[nTu%F7f WBΐ+ۂ3L2H]'j&z/}wx]u@旙)c.4 Pu[GNjlÕ1 έ׵ r#Z[ڎ| @ 9PBC/`u/O?_ hY/Yv㠒zp#?0_gYZ2T9󢦎US;Wq6L-t P>ʿ /_ZvW,j[arq,|BuBf6ƿ{r`Jc'Tb7>մ$ zmU Me12L Uwhy뱀nqϋŚب4ufV\wcܝ]W /f%vUEkn=tH1+ˁٲFrFI6۪:K)u(41PЦr<0xjdɚeTn*|K])H,1/C/1]sc ,cR8%iQS` [xuR|6#U-sc6jBN=NW_<_m_ v_Ov8<vdz\p}TP4@TT0[6g)^ՙS5O:˕%`\A ね7_c 3YH75_ͣlkz@buwnnWrsO~$ b50ǀsڨ^ܪ {-UzxLR/7fk`6E2pk+Mq:xP Md* V^yk)9(ziZd5keJ2*|$BJhZ3:mcSժ}1fY>g5æuG-:|<{a#U4JRM/"b1].9~1~p9K [>_IHIN'mq9ou9=K(/|<" K܎h<dZ>*W$&5___~؛@U>)e\XpQw6|왌vjfy@}Aj{cS&31Su#*.CWzjy4FPiZwTy[\o*[{oV'ywrJ6zDCqy!@LlL03 }ֵPܠnfZh$Һ_Jߑe8f3Ȉn*t8%GMhn<2@6^2re%^Z` Y\n.sՒlRϕLWƭP,Bu5Ek{31njEo'f5ɨzEb%R\rpp9?S^YϏ\szqOXK-t{{q @OY՛ %/`j_U_bgvdjGuvPラm=5,Ess9frf.YrDֿ#V;7@PBVD ˕=BF B޲ !^6_<k݄IMm?u>،SScJ)kݛBntV;Ѭycոt"vZ‹^Ab͝SF>\&6٨a|&zU2>j20$W GzX'4' (-`A\ش쎮 cGGZAQFS xfd&Ւ硖J*Cq:ǜ.!sL{ ~I߿tScFk{|`<:b#[6j? oď{nA1ܢ?}'aOFUhIY~gxfrα.Y D{>Y]Gz׃yQ?_Ƥ9w#8f>!O-sKbm1uocGrcLL 'ZIidHQ\ǒQhmZz(/i;i\ֲ6څyPhrj&>h+WP&;-Ml_f% i*ky6^WF4i9դ[GJzvՂuy`"$Wۺu=}4cZ()J0I0%Ci *Eh%+.WQ%ŜSIL)%BxBxkgV[˾%.dQxU#6\?‚ @jwav SA{0Gv̟@1fv?dg'! xԋswà+Cu<\Ę;f=AXm_/U[HVAs]V1;wZ&na{rBZ_ IDAT.pVf vqmQھM`t*G[ fM+l_܎b>kz!kyPw3NGdvܘCg#|WӢ0*dxyva:J\ܮ  R": b5LV 4trt jJ)W[#Fa-[{cԀe*1$IIR,E"&%cN!tIS%jI!)rԔdEvz{Hf=AGoA{䏸X݂,?be [mo(z?+q?_y@s6T2Fݘ3Ia\b(X[mVAi_ vzާp;~១x9B}LTIN<8v;9W.:7,+38ճjz Fco DhHK99 !lkZ;n72VU?2fux}vgnMm @JAMM1S% =;Z>I}/DclUG[~jbpOTtisQ!{=`vfZZA"\ V^bƲvΣ>PǯGnT?8H \Ps>/)T:ZUZS-^IS/"bPe LV0)V x(H9ǘs/)s.{9o|5%I$?j[5\>2blVX`c @P?/Vz ?%z'g'pzTwʓJ~잜wι'{rywG9w;;j\ j:3N{ B΍k:6+mHt}4sCf?&^_u>NLʥ`:dVsV2MVoMf\Ђ zrJÌ- !KEmXۺ6)Lf#.*mQR,hs5\:"!zʼnuh2øM&2&1׷T Xi 6-#Ka:]^L&2'uAQMTۺmF)YI3$de`L%iRX,tJTMQ$G1!9$)g)5su!^$R="&#T}:?VlV;K<AHAyx:1%dgɱ{gܳws{~? ~HtyU98s_qY܌.T7Ʊ%֛L{i1d3,-g;Rwӯ `l() qk[B[lɠ5*l:atyUp=:P)nKlbƑ,7VnS0aYuZl1[ΠY m(5v#0 Q:f 0;uT]xʭ] ŔxT!V aQOW>dW(teӺT*IPfU `hZrPMA$9fQ$cb)r"9K$ɒ%IQSͼ XN[mV[݅e/ǧD gqp s'{r< ó/?wavvU̲eL 0 <&.k'VeakRWWmo_s3v'cŢ74 5`5>7ZȒM X^JzhQG PF4lH*ӳZј1C]i7A7t(Q^FVm%pQm[IL>v6̎jlskfG-Q;! ª|6<9ysqOCI6lDLrMA$]4 /1ŋKH%|rSR!{=D<{Ǩނܹtl [mzePn>8-P;7=8/_._9r/~ٻ! DDLGA p Tʦfy=ҺGnڮ^kQ0 ޜx7̭Pnz`Xv Ƌ@:Q'nwoZ:!l^#S͘" jm]v}.e3/M6M6= hбwT~Rlٺ@4`(U K| ЮۡU_vZ: xIJ0ULC+|HT(Rmt%t;%E%tp)|K_<W{iZ~ĉ=zG^{EplV[mTݳ>%Ҿ|NOx}x.cֳg~v'bzb#6`f^=3yfv+LD P| 1-  r{ ܆U5]};p9+ې}({~}Yu zv љ S4 g V皋`.8*m=a1nfts}vs(KtAjב:=?֪ў!Q$dYN9ViwGƝFְX良JOjis(eVpyemrubCDbI!b+B:t.kS7ki[Ѥr@1:~ǖױ [m_x_v  '*qAT*wwٹc1 L4'"OL<뎈9SӸ]wo[]dSĦٰc_ N\F,1mǨ!"[_f{e>Y3^nen m!V8eiJƆv:Uu'UrX3I;c,E+\k=Ur`CL`& ˀ ,` d& Zݬ4,9Y",Q$1s {kk_ >k{ *O<tl [mh:bw9wva/)w|paE䐳;z!1Ӂ;3{Ǽ'=3y:1j9fי`G8֌^uNOk Kxq+ZA1mw+ |eq%Nk]/>GseCzTesθLZjL[ce@ʌx?ZٜV%|na磻v"Z88I2nXbrkdg%  EmHM0 0KxUR8]*I4jYrRII4F)e 94SLs9UNtu)ޓP<2>=ܪ <dÁG;Jtq9տ ^N]ri}޹C~w;wtιcĎ޻gwtwO9'2E\i;~ 1V~O;faOf `Ɠ֕uJi,dYYD ]*_x1[/|\p[iwU]L:E7^/n$~oY!:f VsP7S'=OLGvgJ} VFV\l VNjM%vZ憪("AUZ *UsjNIs 9)hXܬYSfc{ : O' ~[mV[!c X6r0_|y8ș%Fs1p{iܳn= y7 O޹a.ιsz49Ͱ/fy1#\u7riia:oۍ Zvu7~owTƆuisKuQXMn6%xgk٫we]FXgvr!d=_aD.Dd,AF4݇U *Ƴhwwx(ޢhvAjqA-;kep@h6GYr0IrJ`9cP!KcL)&єDR9f\I涧4 +B_h5xo'q# qc`lV[m':Z0w\_~x8i>ߏpxw~f ; "`55a h?ej6o_/?Oj @4*|lgf;fiz"rZ\Y\[\TE$jdG7c-֭fwtc]6dȕ1dTD\D 3bn>74{~ DE}gw"- qO$Tָl M:Z5ԋ}2Jsv.װ׋kUJAIbT+5lXZyu*#P@slƮDYRJK~G!B:܎-5kuޛ9zM{mi?2^uO$oбVl:NBS{ +y!WՕ5f)9> sG@ 48꾊w̼#v>Pqx"r\됌p8& pQ]x֜lYAhG̯)WfTkb~h!Y,IAx:vV: (3TbFFWo X6ozk7q8gܷ?d-'lBAf#Xk?`Fc.yJ0?AX8[d ??U0<`Ɋ#jK.Z $%M1b?-l{Xsz _% Qxogcd+RHgY/Adi뺓W!sCrxW툩eĴg֗yGIȘ5{ 0 \ ]4Nj{%wcm,띴7lW݀ٹuKJRap%.LT9UGgffIv38:Fteݍփ4e]b 8#=)Cw3-JxȍԿqu2i)`*XLbսTc,jL5V0}nv.4'ɒUsVY%dQRYED$_½DG0{mp?9Vm [m;ac[!/O֜%I={T ļh;y39.ttGvGf>89W}yhlv-EFRC`Zͪ$%BƅUA-;бtߎwwt:PJ[D VF`̸O9T޹n; AVdzh f fr`P)]m>JUfGGňVW3V Տ͂d%8"DYʕ`*AM*A%5sPQrj9i ,fHAVJȢQSE^b8u#G:#^x+6jd mKvyOuM gZ_~xn?0w|dCq.g^W2-LLLb6:5 =N/bի]4b+`|T+UT׫#-\^vWʣ@1"{"4&iо,{[.n&]uE:aо\LQ6j6o02=VQbZRGxXDV<(QMIUAGN,񢒂$%M1|r1cI:5$R}TVT܃MDZVlV ^^Np:0i]yi-"͂w`e/?|8P8솣a8솽waa+]@}!T#uWT [Mj i"IDATVv<.]!6.[u9f+IL|iX6d9>[<ӕ|;`O&mwܬii'8etTk顣Oª]-yB,> v˱fZHu,G UI%#]TbB27c C|\ՋǗh*Pxt \~4p6VʀN'ˏ:N|Lqj R$g@<^z_wOi^{+YB{9܁3ۛNHфTW-hBz_WX2@\jE>^ ^iڳkbKFt >J`2UD R2,|,Yq䪱LPZup, p Uz5үuon74UC1 n-~^~-䙎P=ѠeE ݘ 5*BHڵ@[[8MD\2<$,)%F!bKL[L[L߾[ U2;DŽR=2VXlбVlV:ΝNhݐ 83 I/Bm} s<01P`"LPl;qsΗw<9i DC%j49|8b  Pec&&AdL F1나b҂Gsw=t Az kHnX_Chv!v#vֵt!\ZrYbV: Y@g`|rR?TƯ4 EZJ9ת\%9,9K?r|;Vǵ ?ҹxqvnU[m [m2 xG?UTi*KĞSQm05/.YbW 40T{ юv` @haGDrI.TQN] HB*@` <&HhՙU7! @އ}kлvݽMEX`SmK7V6_^N?ӕ5(wsl7*ssZ*i I0FXƭ*t,YӍ9-jRHJV&4g9&QI9 /9( U>ҝxB} jO\o ;Q+sǿӑ1c(8kff$%G.0y&. <(hD;0JgўJdļ@D;#D*@DZX:(ȷ 6+m>jAXz!=nugR 佽0 Me^;!|wWÈ,g+DD9auKbk:xM *:*00,)4$JRd&CTb[:V[RT[]X2j *YԒdJV@B"YEDsl? `[~t# 6Oxj*1@yyy~<EtB.Cs.-{s޹w0 ޻gv{ޑ۳s{"/6~۱s;<8vD#.&sq˗1-x3*830 l͈ZgCl:"CFq\i܁Nj oi⪃B3U.cն?v^ =}t?C`n7^1tauDjֽ"WU$j5rTը&AU#DJNJT(\ST䜒,)S!圣Ƨry ~i7 ^͍jV[mT;~_5!t:wZ8kr-e/q|<쎇~va{Ws~7 ~yW@;ǮZv@y!e `p#lV D 3ZIVmԮ|PEonezkDNo~!OtwOx8j`r~>3=W: cdRUs'|>L'm(Zs:rԜDUCԔhHB5ko3J,f{n >`c6jC2o-`]e(0ix>w~70 0ݰtO ػ@&" Y@iL #|$5ЊUDPM&t'!"`nRj&c_"0oSTzB:hL9u[o8V2k8]kׅ' A//:4,)iNI-%4(URT)}Mw4]K_[ z_#@HGco\d3ZpY9<^O׿/ xQ x:F082*r(it 8]N]L*|83XF6k: zG,`Ԓ.b~P Op뢒sòcL)CAmЮ6N.Pa ( 'AxG\AdXjL2Lj.Bq34'є5KN%}or|}T[F@K!-Q|d֭# Fz9dqGQ'PSUj^͜'&G`ղ8bp`#`LyFfp`6X*f.#fs2 OkE+щw9Ve]cU%Px\P.cR]P5IYjA4ح a @Ȕۡ)ǨefLfUQ)6*PU;-q'a\a*&(DD \Y,:ykadl V[mV[;B_^;s;W탙sx#Ñ9sy_ !3qU?uBJѻ!5p,AB f=|,3Tq&bVC/$Yr"ES8sm "Pk\#d 1}I?gaM=s~1܊Y@c6V[Qk\#-(8o'-'=N~z/N;onPǍ`p e+`Sظ&83f V&i*dX ]-Il!ݏ){;lpR(b. àPeQ͢i$K#jM ,*j"A$}1G$3ըi?T4ݟonT[m?x[mVK:B~CeRn9s?wޗۛ~ -GrnV[V;crIENDB`yagf-0.9.3.2/src/images/remove.png0000664000175000017500000000123602263216100017364 0ustar parallelsparallelsPNG  IHDRĴl;sBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<IDAT8nAc? ΆT AG(T DEX$ JM ^KfYZF(Wh~>=s8 ~(6#)\0Mi-ʤؖZ?AHhԯAl ~_7uh{ԫnI=z؝C!ޑEԭCgx*'X0gvDHݜt*Y~? nJ֢;fy ԭ t-7 ٬6;,2J/ )bl0$Z R:Ii 7U*nqEŸ,*o)j0/RK]E<ޑSpv#g7'xQgK}*>eì[J(P9R$umVXdZ+f>DYmq%a#3_uҀh[>5 q֧lNF!k h @R@kK&tg>q߱'::-RYIENDB`yagf-0.9.3.2/src/images/resize.png0000664000175000017500000000130702263216100017367 0ustar parallelsparallelsPNG  IHDRngAMA abKGD̿ pHYsaa0UtIME#2HIDAT(ύ_HSq޻߽sM6D҇0ODEYAFXRY+!H`cCe L%SktWszIiy9p>{8[*^oq^ ]ޞaЖkO)L&ϯvxEzq [ Θ!Ɩd4>_n1;>YM`^f! 3M-W<0sПt5/Ho'3cq4aac8*gaLHIMѥ֫1JEKDXGbua "9qkr %ɐ[Cbnݠ*2U.1T9ַ+'4+oIAi ADwt::D]:&Jh?ەvI6h 9¢|m֓qGIyH-?\OeyXRNlκ.gF;iSGzjY,KuEimP>3;3%/+=Je|sƽ:\Zp(v "p-J*>{9'":0TIENDB`yagf-0.9.3.2/src/images/recognition.png0000664000175000017500000001446102263216100020413 0ustar parallelsparallelsPNG  IHDR>agAMAu pHYsHHFk> vpAg01bKGDCIDATx]yywS{u Hb7( l!!lH G8TJ@UHQT?#BW6. &I0jIڕ>ݗzFRWus~r_BF 5¬cYuZgJD5?b%<,: ߾[𮓸&8e@|\JZ`֋z]Zd2ItK,I;w{/5TA!Ji{"nWNߋ?&5 Q8&Kpp/Fg0zM DD_~ %0Uцx ‡\ ߪBv%]t) @l Jxh[ P'''XlϞ=? n ?H k2l)8,׳1CqbO< :DA/fAl ,"}" A tq !7UY`Ԛ˜Ah. RdC9Z lU>; 2t 't+o$z{{ xgu$O8Ň. 6(Klb '|{4r* io+P .塡!TPP(D&kn_Mc 1<A<:66Fw2^>1MtGQ@<&+X珳=^$^1`sPi1~oS  TA`)X"`uS}Q:6O=ԏX 31scDT@^^Εk T',g{<w(?J@8 dD =03;2Jx{Dobr"Š?^1ؼrkv 0YAԫ^) ReA 2U kHa[Xaď3Ш 7J4y:%7) "|x>1My ߕ@(ʑW:%/eFTVVfe:TRR***Pyy9iÂ'W @8x:kAB\0)"5 bI'Z---in@Yj5@˗/G ਗ 83ʜB^.U ,"_RF'3hCc }}}pP}}#S|y9I5EYc64LINLd"ϤaVR^vn?=7s{MNuؗHLP0br9HX*,,@W0Bݻw+reS9'(ᅠeŅ~6;ppx/7DO?.A0@?F znDsn| ݹi5!0A(pr;ܤtoڵ9Un >]\)eE v{0?KO,~:<̿ ;L /l6޽sՇ̺97ăI9&쏏p:n*XkTby6X 2D 2CIQh0%w`ik|gCKxl%0Ia[Y>|EJLF<94SP^K4pRk0,"A4McJGJ|Al>[p:DH?} Z8 c8f R.Sh2\%ߜ R'2kYy:,ocQ@vSlOe|b %(py[}#ڰ@ldAělvU Ξ's].p+h8*t@a- }, !(]f oud6IlB ]g86zNvϜ.\Wz(ߣEʠ:Kl~r \>0>dT33jdA@LnU7ǿ@}7n;@ebG:K+NG!2ڇ\qc.cNm:o)bһ\Cw}aV,x {r$Jdž ]ǖ'w?*K@-E~uۈ2]79?~u4c;֢ߴ]%QÆBELc~l9@^MM-#!*&ʯh$yەgιxhfTSW-RȫF t"/P"wMoWUI߫TD@Ȳobvڟ:ﱇ#B19fVC+r&SlW~t ,Vm[~/-D 8f&K)kp:lv*D?3" m@O @p':uخvr}l 6*zYg0ؿ(P:a`'2Xwޅʱ|,N-m?AWm>? Aߒ~U=J3#*zV9 NؽLж?@KKEHU<:{-nu?O)v@+:=ۅ?ަtE'bm~03ooAERu MnS*AœVzރΜw>puU̘貒v:UeQ/ %]Rd~d)JW)NV? m ÖE_zF9+XW?x/s*"5EJ/`A5y@\j'ձ^DfY٫ıJ t+~W S>? /PoC}O!級0· "rZOt4)3HIA>id lLV-un ^ ?xEwy\8YJL\U4B2c30d eކds?hs =!ÇO2ndnVRwDvH~,0Oɻe:S/]9/Lr>8iT+i2rk{FD/nq6Y&@s6?j*\'K6rMeb{Wg!UNj S8?($y)888z{P7VzziVB9kʕKQwg{tv&R %@ӊz<# lB / =S7GeeU hIm' &D!lBXa<ˢEŨ:8r^`>1R*ԍnLJQSS3ӯЊK,.,=qT#!9G@UoP\\DRC:=~Gp8<+^R/aXkߺ.|]f ч+D+L!_.tw܃/.]  xi'? |Ak׮$K%%D=eӂ޿oi<+TuLg2V֮[֬Y!sfgVvH,o_9P~l5bӮD&XA>+q#~c;F/pi\2Yɜlb=7OעR5lRw@:S"@GJ)f'-ꪥ 6`%t  t.45W;D"FJmJ'%9BC$BS2[8}h7`Db+j" VQts҃99u@nFV s3D+3|x2Wu҈q^Vݶ kGl,X% %B=:y,:h"im>F?C|~>rUT_>;Rrq=ŠU44ͮ $ y ')Ć@ utŅB2l |a=}Kmx yu-0{Y&=X81炑MwABhDԱ1TYY$\!L xL47-ue 6A(//byﻎ4 ϽwmBW{zdK˝E/ Hy.KG(C˗ՠ%KkH n AiR1::Wi!ĩԆ#7,pbaw01' G?w+\RXh:9Z())&D0;'`r:xN| aѸbRh熍kй m$N0 P&HA 4<:F5`;w7em@) 5/K7uNjj0Bkڭ_![ eϱ0n|rCM(h!t&vSu+|A10Sg*qP.~7[BC"Ka L!ch W3'F-;5@0nlOQH:2{rM+dzYA$CaGnTOUiG4qqW:Q#F>|Kf@ƍ?@Q(q) D=}nf@H1l?p:Ans ć@\vIENDB`yagf-0.9.3.2/src/images/resize_block.png0000664000175000017500000000202302263216100020535 0ustar parallelsparallelsPNG  IHDRĴl;gAMA abKGD pHYstt~!gtIME/-IDAT8˥U_LSg}^%T-Z̥Pt&p &V>4:%Ie[̩pO˞->h42 D J g띍 rON;}P1+6XEXZz-[,h`y~w*"Y0[L-&{ƿI iN-ұU*ʤp$)_x<~맱@-8眯Z;8+o5i_+ҧ'OE(Ȳ YyE\n+GkfЎDߚ]ަT"j B9|Ϲ:IUUhhXxӹ)A<=!<0t{i.\({4Fv@]`s?lOM {fRj J&\W1•k?bf>zK+[ @@{Z/:|4Md2uCutC9*++EQ!\lWc6 EBYDp fנ5!DCs &m ~OGP]n\n.'JVk8rE߄@\\O_Z?N/RG `xͺe(b$q (0Ӂ=')j.=XK?;ENXB$Iz+-oэA@]`=ǰ}],mMZY($j|>̲i" T6ρ"Z;IjK9"`IENDB`yagf-0.9.3.2/src/images/revert.png0000664000175000017500000000637102263216100017403 0ustar parallelsparallelsPNG  IHDR "> pHYs4: MiCCPPhotoshop ICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/%ҟ3gAMA|Q cHRMz%u0`:o_FIDATxϫLar'! J Xa#eH66Rba#%UHFVBJ)Kdž{3;Lg̼sΙ=>{ށ&*Sha~[s /,dY[YOqzep?$X nֈ<^P ġ"U+ |LZvcǨު'LUiha0z` E2*PCc I@a.[1A GؘkӍ+"=lSOD)%0j-gX3W1a ,B -bعۏ3]X /p!B@lŷϲ|fs+'}c~I4ae>W_0;n.W iҨ>;yeU6)15X>P*o׻3IENDB`yagf-0.9.3.2/src/images/save_all.png0000664000175000017500000000500102263216100017647 0ustar parallelsparallelsPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org< ~IDAThZKG{fzfM|mc;G$L7@DbCVRXbD,YŠ   #,;{3w^]uaQ]Տ_ %Q5]w_k%"վڋ#Է#Ԅ) &p8=9y^DZ!so~ , Mvvʇ JT21Vakxp _{%\y3F,G1qU@q'1j`Lb}iY3z'ź@>޲(λOx,HHV+WklW\D+$na[+4`oY*׺[jILӐ,V.QVbQхB$ٝER b V *Sa[(M*c,>fiO,EHB|@=ǹ}D}?# vWSwr|$O BY{%+|7q3cE9"ܾ}8BY-1E) ŃfŢ5IlՖa`&xbHeLLY5"{{Yk ,kpp*QRAbq = mEܟRWk>L%2xns_{?<__((Xi6" c` 9ֺeu̡YIqDUYŒ!|~ց$ Ly8=;s1IkLvϾ^q0(dׇ JJ#(˲ӵ29zݲajan1b"%3E${Ͽxg/OUwSXkg~z'I8T"{$%QfyaUMlEGBHo=zS&eG}?6N8?Aւm6,#]MbCQ_z^7ߜyTM@M(ATxiE ӝ+t+K+Z,ONO?c^3|lnݺu^A>YM4MGY$%I2Z 031sIDTƘb\n޽;w[Vs ǚo IENDB`yagf-0.9.3.2/src/images/saveblock.png0000664000175000017500000000272302263216100020042 0ustar parallelsparallelsPNG  IHDR szz pHYs  IDATXOƿWU]=6^IcrABB)|!E#E$Q ;ho\$B{uٙzqAfo)i4~ݿUWf\}}'%ə^-DNFyy 2O ,zwPhY[z//nll0|ٗ`"+G,?X'3aprWw+8o[Y"[WO".? ZkpWؼ c W"y3S,W]988<}Nw|zQts+E`l$YZF2(5Ƃxj0 rA^):S@P޽H#uKMsdy {x<$G "Z\$I= $˯D *AU,@Uo"Xk"C*vQU(U[ X!"Pp0OUH9BP#9BY1(HdD)6T0M藷*=YȀТ4,ⷯ+^\ﻡ64U +t38ƦFW d&&4XJ0F 12$] @pk8z*CSHAny?kP̈́T ,)G(pGt ~1;!ӫH^~){,N5LeF\Wc/U$@I ,)|'.R)0EV ,?1&*Ps7z?ydɜ&Oq /40EYCdT`U$]E[2.MFbX8ׂE< S 6qJP"MwH|HRhi> #-C CHf^xTֿRLk wʢPB@u큒(3dY7g`s{!uiB{6ƭ[|xa~`?\'`;DX!wxaCdU7vȠ&5B`ږm{kSk mZ~k[[W_NiniUSŞxÞbfE!XKCƐ5ge50Za9Eč Y_dq be@.1ӡӁB!4(FHHIl64U! C46~am7DIXED Ëa*hFPHob:*SA|TUEtD(r엲,: R#zxt_v~ 04! fXDU%c8,""HM5OWW,",LXR 5iv~%Iպwq(8dd@̊"’0CKVP G텉W8Q U!/{mR*J \~!""+g:圻r哾0 ;O .@^YvRkf_Ɲ{~P,zF0bOD0cS+*50,%)@?h?<~:_(@0A Yl: (m. 7dz*Gʇ*6$ im$Qk,=zB{X,.-/?y`_akn ",u+~#Ms,B%h,&˵[6...&{+.- c|l[UoX}GC$هi##[[a CmnWxɒl` uۘHرフj_ЕrLnoۍLQ]#N;< CHUz+lW:1Y2/*rv"O?rZdX,QT4UYQDDQFchvU~[vBD$0DD@ Qh'ʥұc==z}~c} EkqH僙EXDYT3UXR5ѽ!2"':Yg<5R@Zn'wܹ $Lz]ÆvzBjEw[X@@DT0wbǏx9fkgs~lZ^;HxB1!8N***䭾**""J%##'_{n Ç66 YLӍM9NTTPRK/P}OMNݜqRo;wW}}UcL7o:꒪톏=QnV>_ PDjO4RV08;7?8-"QNCbYD@U<\PU/~~d7G*ީɩZɕ+ea᳹ \9V\>~ԳHڝLUN֙Թ'ON{|V+׮]̆!)0}(=I{VvUD8rT#w.SDET֞9szK3pO>b9mfǂĩ:N|wE3U}2!U 3/42tܫoOO߸ycC=奥ϟ?W^[L:ZM^~ZO ºqLhoM :֛o>G=|X,ʥoΙk's.}5t׮Bs̳!AGjAΞ:9vjZOé564Bv yQQj` 5 `}~gv^|[CSS|G/WŢt%%<L=&*"CaJ\f"j_d0uahhԩOvO?`չ_^^*(9NA@;.scBD#'>9[/^ǎU{.Lff/gZׯ_U(q3|۩F`\ ggK,qfJj l-ٱo\(*?_OVɘͫ I3;iZJk߾:6G(CƜ:5"PD°$.n5٥J=66f9O~0Ri@bkkˇk5wcC F ~VzzџAiݾx/1jfJsݶ(_>ZiT;A(}0\~Za0 {,Hҵs.IowlZ-FS.//<]_qF6Au0 =e8y4($ɖZ;ϡl1zP,f{OY++.u3)+ukP$JIENDB`yagf-0.9.3.2/src/images/scanner.png0000664000175000017500000001251502263216100017522 0ustar parallelsparallelsPNG  IHDR00WIDATigyuElI06X6NfiYI&30-mL62]!M;ͤ)6mbƒm, -H9:ۻ=sW%$aH'fdacs?}x(Y5Е&LH߰sҵLʀ% 1X2lM :U/s 2Q-j}8>fft:^3 ޽offscv˷[-DALȒI!d00 #$f@30LdDQ~(=ަ,/E}WRn eĹ_{gpȞ]p\%Hю$MNɘ3*fbR9%f afMEʙhl:O={w,/U_Bv$'xkkvH$#Y24 $ɨ`,fཐPh/ 3^2Y]]eyiG-|?޻ ^yv.-jsTjrFp{%  >F,B <`4 DMA uepYtj» 4\6A3/j0޺&3SmH` i\.N+L3dEP$@2pHVrZi#̰4G$#ٓ1, Mc4"eLU<DiM" ڔ7uG&=~Y]Y|c1׌s̜KJJ>2`Mv\s=ނ .%.=_(#Ə\6m|Uf)?{fU` Ni='e_+ /aÙ?O%Z%PWZtbiB\*t v +3x00@Db}w_;_, d#|D]L1q>*b@dr`hjG,5ЌQsqz?Q ~6,q'CMŅك7gTˉG?Ǿ">$12QVڌ=UmrUL H4zFJαf,\g\Am&"iq9B&&oþ[N8zxkaǞMȩĕ  Rv͜4NO2Vc3f0bՄyloPh VO o+>mc1/qprWwM<+Q.Ddc` xrΠ&  s )cN2f5D 3 a{oeK,gD%YRݾc].V* Ǔ4MՉfD]ZTbmHY2˩+$M]c|i}'NE!d mX-UI0"U H`5uonEk/P9ڼ7ͷ{,7qϏKDm3;Bf tZKP7b{ƨY(5KAi@tᝢNPfs:pfROCT6vҺ&N=8ߙ6KEy.l\ŋ(/}=yM:ð^[s;07垻J,A;dQ!RpO@O 1ϑEݫ_}ɓ1Rtj)ZKr`E%[X( )@h iE؜4/a㊺=%Zy*E(?);~}RGsnwR\; E, "[E+ReIV*i%i[' ➿~poɁ"MwC88h\"ezn YaС{n硯?ƻ~.#@DB"zbDъ"Z-\ro϶8n.&X b0.apHlp>Ggy7'G<1EP,p+0*p`n9m1?s 閈<5em;8ȑgxu81!DG">8Z'zG">8S@E),EpD'8~7Cnտu{-W/_8}|y V#xAG#gE;De=꜉'FG e)2zbpӊ2zFοp]_zNs)N<ʼnw냯ÒjڋvhCayC\&O|[q@Liݭz?BZ|ӟmZp} zE(STTfs; USsfu [-G;?s4%AN@"esL'sN9õwPՉhģ>z; #rjQ%lҀdLٌ3ٌ@Qt? 9,Ez>NKC,!)Qo3%DQUDZ>kٿ 8E,\ɓCQ%[)s~u]D*(>xT=j9rY]ػk/u]a8rsau@Pe-bB rSc@6rʀ3.j ݝv"b:.M<ߦp!"x9eyy^tBK$1EtPrɼϦgjDsk9u]SUU#sc/<*yk138p\7"rc2qΡPUe8qkO$&: nnl6c:SNSgLƈ(9' cﳹt:' Z{D@U>Or7%o{_PvAQUT a`&kn1-.`}c5" &Sw,h !wWW03De@\ڜ3WpΡ"*(8zdccٳ;{)5XuEY()'̌lҎ%nf99&"EDS\*"Bj "*9D.0v?Ēb z!&FFNLU՘ ++k_Ye8QS= 咢(PPpgΜj.u)K;D9SDS3se!;d8' %0:13,9ǧ4̫xLӢHӡ벴%+++x@'y:|̦3r6DA!j8UTQ 8daj YFM)B3@șy!!DNsB1y[̑&}_*O< \&8ܺoOq{ÑGi=5[x[o{|c~2pD@6{[ٵgG~1DqKٌO#|ay.z!</p`*`|ID~T5,,>~&*Dep4` tz#.NVġ4jIŴ2VW.p~E66irMJKDp4`9Wc.RpѨC=neY9*)²Ŝ)kΙ+0...3..2QޅZMմ6sRSŃnht}ԋfZXDΚ#uUlwQ We(N +PWJ53of ɒIb`ffTrFT́Nd漢 H@ENʹnj^mUUu!挈1 :X}3ey}}}1Μsgcvz`n\mw:B+ćwfg*Y$H"$3L l@k0rSͫjZWt:lN&͍͍|>[gkkk0of\iuE]˲]le`0(:njE)1.wwլ`fhrJ ͼթYU׳d:otccc^5|6'[[Fכhfft:^3 ޽offscv˷[-DALȒI!d00 #$f@30LdDQ~(=ަ,/E}WRn eĹ_{gpȞ]p\%Hю$MNɘ3*fbR9%f afMEʙhl:O={w,/U_Bv$'xkkvH$#Y24 $ɨ`,fཐPh/ 3^2Y]]eyiG-|?޻ ^yv.-jsTjrFp{%  >F,B <`4 DMA uepYtj» 4\6A3/j0޺&3SmH` i\.N+L3dEP$@2pHVrZi#̰4G$#ٓ1, Mc4"eLU<DiM" ڔ7uG&=~Y]Y|c1׌s̜KJJ>2`Mv\s=ނ .%.=_(#Ə\6m|Uf)?{fU` Ni='e_+ /aÙ?O%Z%PWZtbiB\*t v +3x00@Db}w_;_, d#|D]L1q>*b@dr`hjG,5ЌQsqz?Q ~6,q'CMŅك7gTˉG?Ǿ">$12QVڌ=UmrUL H4zFJαf,\g\Am&"iq9B&&oþ[N8zxkaǞMȩĕ  Rv͜4NO2Vc3f0bՄyloPh VO o+>mc1/qprWwM<+Q.Ddc` xrΠ&  s )cN2f5D 3 a{oeK,gD%YRݾc].V* Ǔ4MՉfD]ZTbmHY2˩+$M]c|i}'NE!d mX-UI0"U H`5uonEk/P9ڼ7ͷ{,7qϏKDm3;Bf tZKP7b{ƨY(5KAi@tᝢNPfs:pfROCT6vҺ&N=8ߙ6KEy.l\ŋ(/}=yM:ð^[s;07垻J,A;dQ!RpO@O 1ϑEݫ_}ɓ1Rtj)ZKr`E%[X( )@h iE؜4/a㊺=%Zy*E(?);~}RGsnwR\; E, "[E+ReIV*i%i[' ➿~poɁ"MwC88h\"ezn YaС{n硯?ƻ~.#@DB"zbDъ"Z-\ro϶8n.&X b0.apHlp>Ggy7'G<1EP,p+0*p`n9m1?s 閈<5em;8ȑgxu81!DG">8Z'zG">8S@E),EpD'8~7Cnտu{-W/_8}|y V#xAG#gE;De=꜉'FG e)2zbpӊ2zFοp]_zNs)N<ʼnw냯ÒjڋvhCayC\&O|[q@Liݭz?BZ|ӟmZp} zE(STTfs; USsfu [-G;?s4%AN@"esL'sN9õwPՉhģ>z; #rjQ%lҀdLٌ3ٌ@Qt? 9,Ez>NKC,!)Qo3%DQUDZ>kٿ 8E,\ɓCQ%[)s~u]D*(>xT=j9rY]ػk/u]a8rsau@Pe-bB rSc@6rʀ3.j ݝv"b:.M<ߦp!"x9eyy^tBK$1EtPrɼϦgjDsk9u]SUU#sc/<*yk138p\7"rc2qΡPUe8qkO$&: nnl6c:SNSgLƈ(9' cﳹt:' Z{D@U>Or7%o{_PvAQUT a`&kn1-.`}c5" &Sw,h !wWW03De@\ڜ3WpΡ"*(8zdccٳ;{)5XuEY()'̌lҎ%nf99&"EDS\*"Bj "*9D.0v?Ēb z!&FFNLU՘ ++k_Ye8QS= 咢(PPpgΜj.u)K;D9SDS3se!;d8' %0:13,9ǧ4̫xLӢHӡ벴%+++x@'y:|̦3r6DA!j8UTQ 8daj YFM)B3@șy!!DNsB1y[̑&}_*O< \&8ܺoOq{ÑGi=5[x[o{|c~2pD@6{[ٵgG~1DqKٌO#|ay.z!</p`*`|ID~T5,,>~&*Dep4` tz#.NVġ4jIŴ2VW.p~E66irMJKDp4`9Wc.RpѨC=neY9*)²Ŝ)kΙ+0...3..2QޅZMմ6sRSŃnht}ԋfZXDΚ#uUlwQ We(N +PWJ53of ɒIb`ffTrFT́Nd漢 H@ENʹnj^mUUu!挈1 :X}3ey}}}1Μsgcvz`n\mw:B+ćwfg*Y$H"$3L l@k0rSͫjZWt:lN&͍͍|>[gkkk0of\iuE]˲]le`0(:njE)1.wwլ`fhrJ ͼթYU׳d:otccc^5|6'[[Fכh뮻2sݻyp\B!N>L__QvQ y{L&V+,t:X,~, v˅j\2@*8vPk׮i&8@ww7ϟ,lh1.fxBUUt]硇o}d(i===l޼^y^~en6;wh4(7Ib6nHuu59q^/;wn>(0eOGn޽{s\.*++q8B!gD"8qyEB[,f3EEEy455Q[[Kyy9@OO>Ǐfm۶U^mZ #NիW $6V+ǎ!PDôse(sмʎ;eyY3?H055Eoo/GMӰX,$I&''㌌G^{?N%LO?Timm=S[[iDQ\Buu5!}}}tuuqwpa<21z !㧟~czz9$I"LrUFGGI&x<JKKikkC4^}U^xCt>#wzғO>)JJJzn)))v  tttPSSo}p\8XUUh4J8nq8 I 055dݺu( 'NX-5SEÑ7288-WZ[[ٿb}]>O>vNMM m۶sϱw^&''ikk/$a(**p( ذav [2KR}Qt]Lp^|^hBE&`50ch4@$HbV+yyy ݝy$QVVF0n#26 Պld2e D" avv6[dpp!ZZZV- K[okגNB022@ @A4X,\6y^p:\,333$I0X[D ??IJKKilldϞ=mڴsm}1'6 %ڹ_}f!2fh4J<e7Lljb\vD"A"KEEETVVr-{?hll< .HX* F)]'Ϟ=Kqq1#ȆL/HRqR0;; }>uuu477) 5kƯ[.lL F_coY7G4f3it:QU fff0Lx<f3%%%֯_O]]d9 D o669o駟z꒗^z)R[[ .\d2!2,cX+2k׮{cEEҒ0.-hCΝ; n1f3a0Wp#'4# #VϾ;qoߎRQQqZ3d_ @,0pGˆ@J[#G:3Z]:HoJB1.\nBK*AE e)*WNl1Xr4T!ĴL=w9sPV(<93~u#+m&?ntXX6çwfr1 3iIENDB`yagf-0.9.3.2/src/images/scanner_s2.png0000664000175000017500000000572102263216100020127 0ustar parallelsparallelsPNG  IHDR00WsRGB pHYs  tIME ;B> cIDATh}L\?敡3SB ѪDwR)]n5i1jLpѮѺ\hZjCKZN Ey)aP+Üs㞙"Pl޻pH$|w.s'qETY ϐ#| rJB03g$ \x׳{n9s gesIEv+,VMee%:<~xc``h4JQQ=?\#F2G^Bp [l9q.IEEv@ @SS|twws) PÇ3::$f3&B. kCaZCwxKK\.g3{&nߏ$IaX8x !9sP(D0s42ˣkײqFYfM31ѣGٷoǏ… v˖-#L2<yPVVF(PTTd"N2?Gl tuuOi&EYLb\t^8@?a6LMMF_^[ە;>tKKɚ4M#344DUUf!}}}tuuqww^~eebBp~94HD<󌏏qݔֆiڵˡ䐧?7|oy)))Eww7%%%\.~?>yپ};N3˴ tttv$Ibzz.]`N9||z=NYb211lK V_,v1 !2\.8###̐O~~>vݞ7288-WZZZعs iGO/[ YI$ Q\\fCQFGG رm۶155E[[_|HJaa!vUUD"xfdsKFdGrܹ={ /lՀ4)Jڶmχ(!$Q^^t:MOOdZ,l6,IӸn|8,ccch$Ih(q u=[3ivK/7sMHa+7O>v$I" (..bٚ'!I+VgfZX,L&dYnX gL|QUYܼ|.Ѐ6_"\_~7|SZT*1|>B0}}}D"fggɬBN3v"  D"qB!gjjRغurlsbB_8 {j( &p8L4dO<'Dx"XX,=?,TnRQQ7ވe4440jLsABQ(|:߰ݹsȩS(..Fuٰ9KD"$hd2 DlHx~W\9qŢp&K`moQ7i&ARX Mp8$DYqݘL&JJJi͚5{}}}WIIb 9B?$ P<@n޿/AQ?ٳgeEQPٌ(( 7t?uͣ ,7?+LIQ(+v!ڵk!l^Wr-b׮]:MjnhsZFl@~ 6n܈를W G4~-)|x8bbFwmx&afMwUƫ&%,)5rjUZLJ1ׯKY@KvbK[aBj!"Z9C9=WV a຿YX֜DQhF IENDB`yagf-0.9.3.2/src/images/selectmulti.png0000664000175000017500000001161502263216100020423 0ustar parallelsparallelsPNG  IHDR00W OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME "7)"IDAThYoW}띵7$A'I(/HR%R> !TE @G}A O!TH( ">I*Ƶ;;x8̜ݍw73svgwbf<̛C= x.fJǦwA @ qf3#v{:Woffx) οWnxObaaa<+%+Okx\(W0sLz%e+-<8}ts71y{iګ:4#$I iE CʄfF{R AC 8| [o;rucd/R 23R$P\Au-?P 53#Bxol\}֭;;]:q Ҹ""!;YD  }LA !9A!yP]^^g̃m0Z̠tmnI$1S t^WfV . !@ٴYP=D}$%°BJ1B/MpQDw'L,u1UF0 "z}OHz)7Ue :g2?X?AnqVh+*E(rd߰3 %} f1 & RR7AGփ/=Ƃ  Y7PL0]@\ن'D9$ C48s&|{PyTReS|5B)d:{0)v {})poWR'MYBۥǎ;, }—^AJ7T<5ٳOϞwT*Bf򺰷 o+_I IK~Eҧ-wVe֔Y3{S&n(0s(ڣ wRxD `@ {8)߬D ylm5L0 nW@0,@ Z_qPa6k7qccCO?4O՗4R<D7qJ&ŋyǹTb-ߺy/.[* H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 7v~IDAThՙo\gw|In6!M D !HE%(H V(|6,tĂPUH - " "@*TL|7홱gaq93EQq%3oB_tpu6waMs!AR5cn~0N~b)Unـ .L!kA&3W)0+WKr FbmyLNNF'Nj[ωzBi銙+zk̙㑧_ΠB*KK-e0|j) >z_ӱUH˒dNMFqMLNE)˗aqq z=jgΜG+o\Z^dP*C՘TMZ [4%uyJ(Ez* ܹMfy*Kgvvc7>KcR|j;a>dPS r[kZp8SZ-;kw"d, [/w>HJo1g>nڡVd׫`< 8 ;QƈŁyK޶%AV4st(Jn Y'FB9ZwiT"٘299g0B|9<h;a:%0}揀_hN7㻍0Q"@"PH݁Vh ,+" *11UƦPT&J"P,cM3LOϤ WR`v:MxZzZ9)*)(,q9"9b<#&m FCш$.I2K? -8P`|odBKu: L)_ hOTU3JeȈP7Z(0$*2i@B8 T& (CBF0CB%0I լ7-ap*cJZ듩*}j IST|B/!A3NQ<03}$>樔9XpRqhcH9--jjt]G)Y";Їv0 VN24GKeUo|gӲ"6B h^wA+Ņ,p?mDǠzbJƕةPr;!cW8TZ^_ o~ۃ x__3i^0"1~` h$*Z䳿'gޔbUT*{@ 0#"kXYY*LF`߹XF:_ڵߋ05ƫ޺}}zǴT膮 kz=7_{]xy첶h4a9jTKrSӲn]hiD$q8s}衇%K? //V3|IzǼ|M 6~[K$Oj{gxZ[2⟚?-^|Ev oI9K?1+WoZSx%'N`08Z-x$ `Μ9ttt{'Z{Zүc_`ʔ)tuu cqIL/y0vXN>mgΜi%5JZҠbq >-4h455 `oݻwqF咎H *~3Qmڴݻw#Z*)/TTUUԤ@ 0=2\X(.-ZDKK̼/3gQFXkOI*Oww;/9~I$o&ب+V_R9P0ưk.jkkq-3w( 6g$D!F%"JIIW(;X`&++ 2f̜9N;f̘lcLRI&e%T0zhͣ>j}sHʢYK,Ux0 98pjjI! @۷og֭1IʊϐڵkٷoƘ9vIcѣJKK+0MUPZZJ[[az$q3{' 3eM*鰤? &^9r$'O4%%%kɱcǴl2ZI$ Yf I%$FR:m@TWk"Kwr]`ҥ&==}rd}wMNNNt߹VϿƍ cޗfϽ+?N;}􀤦;ߵ}қ; Gˡow=w޻w/ׯC}lŽ;~yqI&_Mqq1_|E/@ `oܸ񈤎,^ 6?~y;~nܸB|zzz\^^^s?[ZOA{9=Aw@MM\0 }b\~燌1ݱwz ֯_K/pի&xJMMe6?|[xsDb֞/** zk+WÇ$5I544xs 63V^1|4a„P(憌1-K \}}'9+D`a9[PP~'׮]5!۹s'9W__킅IuAȑ#CW^  AP:Os7ވvI9Fꫯ<nԩj n͞8;킪Vfffʕ+!̚5u(xxyp }g=XZ{P^|$I՞| /K.yPVV欵&~mUUgΝP@@ >gO=3\$#Ƙk\pW@˒Ν aʕdk)//aJE7;$ kג>}sk2\ #bsn?LLq)IENDB`yagf-0.9.3.2/src/images/singlecolumn.png0000664000175000017500000000566002263216100020573 0ustar parallelsparallelsPNG  IHDR "L pHYs4: MiCCPPhotoshop ICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/%ҟ3gAMA|Q cHRMz%u0`:o_FIDATx얽 07|G"J(aP?*El6_U8jRDweYFgeeZ'IB*%"4Myo#"<[kam)oy0/>_9zZo_.(R3D4E"1MDb=E9R\cʲiﺮ(WEG5dIENDB`yagf-0.9.3.2/src/images/smaller.png0000664000175000017500000000573302263216100017534 0ustar parallelsparallelsPNG  IHDR "> MiCCPPhotoshop ICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/%ҟ3bKGD pHYs4:tIME !劍EIDATXVK0)Kx 6&츔'RHpWA AR?-y3Lg 0^1 H>\6mS4<c46}?H_|!B!;!;; @2$iR/Z$'_ aTZ$^yݾZ@Z$r/ϑr |eyBYwdqh?FZS*ڨ 6$2Pu۳lt5ֳwDo8uOSsD7\xC2_y7IENDB`yagf-0.9.3.2/src/images/stock_new_html.png0000664000175000017500000002735002263216100021114 0ustar parallelsparallelsPNG  IHDR>atIME #5bKGD.IDATxye]9{{W־uu[Kb0h9Ƃ fBt 4"Lƒ5&<<̀ #H@4n-VW/teU{97s߽****^ĭw-޻-r5}Szus[I 0Z!PxL}KnpoVM2 y['>^90\m`*(:noj D9`& DrKظce]N`LDw\w(~z0Lȿ cV$FaEQP);c掃ooLt^_ ak'xAb UJzAz}}a$QtrdǚdUV~#Kyn?>max뮠. R#Fa Aj~pZiBDڊ,S(%h-(Fu@690J5K S/W;xe`7bR;^}8~|;váp<l;/Pzp>ҡVZm ՞%Iti!g@%DGIwj4,Og>ðe-krx=3g2Zxqv{TϷb?RԨjDM Fڠ߇R?BJnϛ? BJ&MrZG~<(cr[ Z},=Spp 'a&lD+ʾWA#xptDBfZZՐ/[qK4*BYtZAX;ڏhwR&f'iLg#c=m\o#[ {f vhD՞Dz/4r+#[]9[=$:u)EC)ѐdsv &[J&f;t'&;|:sUd_!|mŞ)3'` i}WEm:h[IX}F7}-s)p^>`4Ab $Yid,DgCkORnvxibOWuh0߁6eq0OH:0*}J{<|=bcd ⊷eW♛;oN4 ( b RHj!Kj!)d'N?4UXߙ)vMl'PS t:4{a PFAB>.aPH ԑ$e: &A@_P4 Dje MD͔hMhMI:m~W^]pE_&K"M,]k*~DU9uؘi* ~I\+!H-zPHMxٷeaف1+7lZguIze1>ʮ@<)SQWd9|*IT1~*T 񳍏@`+q"5Q3>[l 3L&`bmS%Pap3.bE - ,t-XmyYE1TR3 hb~VgajVi`V` AɉIld2\(/or ?*qY|d D%BPE\EMP&Q-WiߊR3}j%pƄnVI`f2p3mD\*LdJ&]tu ԉaeO/~$6?v:LOe *K'#]sv OE 5}mg6M֤P^ L&5V A#DQTO"6|RVcu)l!K &[P~ (dT9 BBJ+ 0,]x}BqHႇ>?TBܖsQ_iVceb>Q#6AhI`Z$K; hqß/Pu}̬&hep)6|N2pdN jD!{7, B3<U3Ht}8\FLJVF *bҷ>XA[ Yڠ (UXB@|nbY &_A֌5q2 *'ս`2I O 7p_SNn CQux6Z4NBu/ M 0 Fiܘ҄aw&Z ߋ]Ht  GצW}EAKMMWfj uN;~I ti+n&  Wwp|\ bcf-8'x+8K8٨ppO+X g:=_?)d TFKM#WuTZinee&ѩԿZ`k+EzBH6Bh oU J+B4n޺M)&284lZ&bёjŕ \2(VmQ^SW}⺸p 1>5VSB5$/:_/|]sk.dCqwAjD PrQFst~ )uy>=IjE% ugͽe#I#b @2,|žY2_wqInjXfkrYhBVŗR8xaɣУ'|mbhBH-;BT쭦k#y8ԁ*PvΠۺx bԈ^E*FTE B"BBoů*Uu熹ԢS8=z/X-|/݇Ϝ=n*cCV_]>3‰9{Ae=tшcDXjޕ+P0Nea8\%8|ĂhAk.JS:VjRku9Y N/ uwy@i~4j  ݇p}:cj\| *ǻʳ*Y=vA܅e90?Q< t%*ݺ4e BZ3,+UK_y(^sn ;yDڠ0$IdL64ZTEr':v(5rETC!DՓA$_?->wa8G=wVLOkFJ _VfR./#Om]  -^i5T&ĔfpQm ΅] ٪2: sы|[*nŞe()N/_| :x횓gƗN -\*A6JӽJ|*" ߭]ybz;U财z [QxA· 'O^\ud\\S@\ O>H Ͻ$.K̪FO"Gя`? e0gA#ò(ea~V#R"" eUX+Eu] | &ZO1'f!,2 k3XϝyI$ Jw}XZKNA rGhzdvgLox .E oA!_!VpILNξHXucBN`V۷f7;'^ K2) ؾQ>]t+S| /{Y.(5Jc8F(XrؖC&(ŷ)qAx;"IBQBQ(ZR#bqHeUn?PtlEto3Ae9S|yBF$iÊW}/_v).PP@ EO~#KŋE\J EP<`*3AT=Y yb~E!ʮFc6hdYNA:l.f [+O4h>@!fxH+"RٕrmM^A8׾s/%h 8Ǘ1*΅+PI fV t󪘴G 輸UdETe|]k7}NȢS + aP Iӆ)2aKG{!SvoQ-Y/`y82 (ob*&y6J22{ªxzϲmEgN AC5M /f &'Be;,wO5qcw˧J@8yvJSZJᕄQjm,^5e*W\ďUf@x>*KQtauҢ *%/VԞ)=p-yR7Dly.`#1<-/ВT`L1%rus*<'Qh}]tQ۪ IFv6)(aE? ;Bbc-˰t&[LN$xg)J``wynNӾ FeWع1D}% G4!^ޠ qjAFo*&c)XU^+O@H'M--Yz"겲j q&`۰w2mO}w^'(u`iHXUBz/49n{j}=.:YmiL \hU&`΀9n +f*dyde2:n? 5LoEpxuѻw{RQE!cW ?o8\HbU&VR;5*Eoo8/*Ǩ_Csj| xhmK69]EI[dYY)-5AEF^w}m^(t1mP͘43?'̽IP:G_ZK:+_W|G<165B=ES[3,VJ/ZJ=ˁ= Mv- dH?JLdu &ާF[FB(/ڼ*9hv"X:{:M 305ȋ^R…%+]weމU`vEltW#3|xXIeB^oOcO7`wOh·BFǮZ+;WO 8{_Wf5PL`fg?5eI[paB8..[nسofKî['Z\~ c'=-U9UT@U}_URu 0#xj&&֖% sN9uBR3CXk8`#~MnC|? /Ԇ0 (U7M, *]/([GJ+V>-`*~U%,n~(  Μ+8u˅咽 O\VV7SW[t?@W6dGh1`C(81⾁>FNL"4͝^NaH3`P,-Z^Y9}+K2 YR +>4ԇ}/Y5U<;dx)\⃽Od M?.`%h&#V_yCTtJبR%K.9{칒K^uQu>pr/g -i%#zBS|#M$&$`Đu?ʿ]o|j-_@PZ4@/(s˩AɋWJ.+XX(8s48mD\.\GuZLs>Ə̋|㙼.$5.;\O UZך=PtMz3؄Kh%:+h-ynY9-/ %˧J.\,X\d ?=3xD>ck5r`##@.HGQOV 0A8ro.!s 3E[0/.j}| 'b +}K+ ˰kI^Y^ttKg~8~D㼏hߌ 4[z2h)5v4+??|?\,ιPHj$7xbSCf i$:8  l۶TUL`@_:uL-ˎ~rq`Y)-Ⴅ7,{g33EX nˁ/\ĪGNrAhbr;K6/!d<}4 X_-bZDv8`M D f6 `Gh(%(qC"qpǕNފee%^߂f}aԞԄaWHc6R*W=TJ ЩO9!)Y_ѵMFbcm~w`/>)J'hoQ DzT4 :tI E4(%(&d4VSP|~pG b;=ph,w9Ïk#$uBeV$>Fx_Bk5"0 8ʃ>~lt_b4Ą,Zw{,3< OPDcE&:hxP ya8S `4_: t.xdѱFbZ9|ypCRa11P%KKKt]}>/E(v JB2* 1B(4>Qp@{~(/.O>( "qk\k i!B9 "D[=ϰ GY*;|IjO=Z;k]Qh;s{Zi$!4I Iz}?G$T w>oOS1'ILAKf(ln?;|OyE.,ڠF[ [h03 [#YSH*Nh@)=yd)YGmx=pDZ0 dFH'UDYEâ[E]?8"~tBCdYF&^ .K\~Tk J2 q8t5X;ų/ Wx>g^㬠A틊5cUQӈ?$vnqS{mao $JH#š( e9戔x_">lH[k"xG$j~fSyS=`X; 5j҄N*KRjHH{j;a' _Kr^>p.׷( FiҖ&K bϞ8|=ol3=Q"6-Qa%Zxĕ_)A[q -ι`ǖdȑ#^?%Dسk*jw-(UiW7d*ިҹ)|׷NubDtT1HE"oC7 +^p(hAF + z }2+gχ89rcn#Iq~'~g#w\f; ^ Dbёͪ^=RNBaPȩV.cR &$mj+_k|=(R`0{8[b0;;޽8z@DVqV}T[8oʁx,pή+5o B?>oYIЎrs97c=l A3ITWl;+ˑ71ѹ,#?4½铝+X֊Qj}dI5EF^r+cY?1Se׏&SjA"[=Ux,xk?;;ڿ/B/~~>;j_2hI9|j߰׏ _ 5%z.2y!ٷom)xG?~S- n trelHqEVuI##'1Y#AIޭӕo,@8Çn[_WP[$+Pȗp uiIn$Wi8~TFB-(q8V}Ej8tι%O~=yϯv;tvS4|HtK)V++E$5@Yx9pJ+<9q|i;zřkU ̞رc&/~eY.e^ƟX=kԸ|Gpo_Ǐk'Nu J͘WzoP 4]~ӟzϮ0_>V ֚,˘͆Mǯ|ᇿUӻ:ob֚Uqqįʯ?=CpvD\6>?2ypVϿZ[_r_in˿'?oU<%?p9>>hZJJ~ٟ٩hwʿ:l50k#8t曌2~w&X%>טcZ>ګDM<4oZpfM_+%2fA^Z~=Af5`L%熶QmIENDB`yagf-0.9.3.2/src/images/tess.png0000664000175000017500000001175302263216100017052 0ustar parallelsparallelsPNG  IHDRIGKyjsRGBbKGD pHYs  tIME 17tEXtCommentCreated with GIMPWFIDATxyp_{+A•!$BHB t2`Jҙ:Jgd QK ::Upjl0LB1!9 $ȵdG6ov'viu&~}?<3g3p9Hs A4i A5w o h4DFFILL DGGr \v ]l,p% rB1xN.T*h4t: DEEP(D"<. v\ZNJ˗/c4d2aJ$$$Dtt4DEEh4DDDRd2b׋fbb qNqN'\.\.l۶͛7"##Y|9֭CRh4NCC߭ ;w$22FCVVJ ###X,}{RSS35NGRRP(|XVhkkŋ |J `8y$\xш핣ϟ^Gzoy}ttQZZZXp!$$$yN1͘Lo!~ˉ%;;ZMaa!V6jjjZ);; RRRBA]]]tuu&11{wKxp}';B|#y饗xٱcGvSTTD~~>7nܠ|H$"--ni0FC\\)))̟?y^GףVQ*D"^˅JفRػw/7nܠӧOSUU``ż _rML&SpR)J^OJJ _}ìXŋE\\$&&ZF"zq8l6, Wo\ff&111\v ZMDDČCb{|>++?LLLSOrIMME$a2z*ϟƍd (bccedd$NC"J'""`614v;V͆t?Oaa!1Lر$ADDĔ盚x9z(g? 6nHFF+W$77FJBףjQt!L|`466F?MMMavESS. ǃƒ&{_6mP\~='$HDGGࡇⷿ-| ?<|~! vZJJJjb6fhh!F#&lXVnwpLDDccc!e!$ÁZ혲2._[oEQQ< }}}>Xt_nNgis=͛/x'ijjeZf|||v`oqoǩ*yd2 .# IOO' !!Tv;R[[ ogϞ'? l۶G}k())aϞ=~x N>=E=߄4666{˒ɳLf4)((7dϞ=h4 ݎJN:;;gpp$%%޽{~:׿hnnf``Ųeؽ{7<~) 3^\.]HvFq JJJhll$55%//af\0{^B!ׯ_g͚5+Wp1IOOGkQXXd2Ooo/MMM]088ݻNOOϔA z=~>D"r;w~Riur9f'O;Cww7o8$QRRBMM Ni]C&x®RFEEzC.ԅTZZmXJ @|A%|>N'/l6\NLLLz|H$ON2,Brݳ nxt!}F LbxJvo“*Hө%Btn!ACVR`EzoK* (M B.rvhNgȳ0v~JzA̴$\NI C0k<aA3%PMw|ӪEPܕt:ccc! 9p8p(ij%MU%']L ̶*Iф‚dCVR&>ӹ ~AMgR [IJ2V+!dHzo ߌSӁ3Rt$@Jl6>ѐ@7B.zoPbRt:gtQL&C.3<<<{}fRSSCpoF U"Ɉ'//,Y(*++ٲe ݷͅFIrTVB X,fP*dh4$&&===\v .Ebb"k׮eʕ444yݿ[H2,,w bAT4t222B^^WFPÇy&x^FGGygjNi@*J・J^|EN:Eee1aAR(H$aǤɛ!f*C\t T?CCCdeea}YL&n;؟fv||ZB!+Wo2EIĤpfdِdHi+[_ /Lnn.-e>¢EX`-"))1l6&^z%***!ׄ&Car7D2-{`^}UN8ᠱlܸZMLL F1PTܹ3S$GGGMMM?~>D"< JyILL L ngə]رcΝ#??ʢ_bۃ[x"""(..F ˏ~#ٿ?SbVKnn.eb1ǁBHHm|AvڅD" o ̟?ŋذ騪"==o1|233 %%fի={vbbb̙3|>^{5x%Q(xް4,%,]_~{w:Xb?8ɨT*z{{iii xWq8p 6qFZ-N2-[Ƽy󈎎r¥K v{n*GE(!UVVRRRŋgR{&innfhhٌb!-- Joo/:u J9s@@QQ ( Z-\pӧO.(--Aٸq#?~|PVVFZZZ̓O?E(r:;;illt`5 pUZ-o:tZn݊b~˗+ܢB,zp88x cccX,أ|x<)+V Hk@Ga4a˖-|$&&NRBjf$v;Fvv6XYAfPJƵرSWW… !++k p.}"cǎLiiiHiii j:ov0WTTiӦ*&۹sp\"yw;\Vw_|?j*ܕ&+O>aҥ<3F$zټy3Fm3TWWO~~VZu\j 4o~[x<BIOOk>oul6mDAAfvۍn֭[Q(Ӻ@ --XN8Q*YX`111_sMھ[HDDDn:v) A tRb1g ̢Egݺu,_JnTWW7ZY(//GRXX裏9Ph  D"j X~=VBp8q>}Yfۑ#GZlذTʟ砒$ qqq!Dl^%KJJJ J2577sMBn[W!|>>s"""穧wv#  |3~NcʕY$$ CCCqauH9rZMVV7ovb)Ǫ DFFRPPOSfzzz"R @/a֭h '''LRSSrțoIkk+===HRcr1$ VQXXHii)zPHWW;w  Aȑ#̛7ATTTvԩ)gmV*ɴZ-twwc4wnsy9Hߙw*2dIENDB`yagf-0.9.3.2/src/images/tools_wizard.png0000664000175000017500000000715002263216100020610 0ustar parallelsparallelsPNG  IHDR00WsBIT|dIDAThYTW~ #e UD@D`E, UWMP,`b1bb)4ĸb( APP"efqW5݈=e߻w crk׮ Rz700xEq<{rIwI.99]U']?R Zrۇ> ###§~+QȥgS^h8YIoVmSS鴫E?,=Z[3E__ߑ, OS`$N~2O:t^'\H+\F+F+trnm#uWʌI(;HMQ(݆uPUU桢">p[u777pvvn {нdT:Ich9U|=:GZ/Nq)y dGD] _a/@ASZfc-y%g̙jǏpU3 ɛ :ebTdnnV(//iii˞x/J!K;G__IS.}E&=?.zP*2m P/""HDx<K]eرJ>yySǑ.$+51}4oѣ߿p]vI(oC"Lb4DǢ^vj2QݚM͇n?Џ{>S>ZK񒻭0%%E srrZ ƍt޽.M/i%De2m~?Rn%nmYAj 99qdmqF *BH$^p***$ȑ#4>KH'ft}nogB>"F-ʤ4ȵT%{"_z!^[_YYGǜ;K'NAAWW qqqH&*?I݅m}|"$gbsfWk t ٲ GT)xL#j]J ?j-#X}ƁZ&4[ N-Tr yuO)ھ7`#ׯ_wݱcGД)SX'[nh_̅Bm4Ȅq>{Ϩ]e("uń6_ G֑gu𔙪v)=kEii=3RuWR }%^+aDMco?y$ USNuL"u5SBAGGgo9^)QNbr" 6'IrRQ]c%; %@onLS=;wlW8iKFKYfyz߿Օ?{l~XX3FlRn"3Q?L^v!!!lL JmHp_b͵4 )wW""rp^S~ʖbB>oU8@P)bC0]0>l͊Ĉ 6𭭭?022RMID .RC0,%4@>̌ vœ$FJS/~-/:CiœoW꯼@ U~u@wwD-7o&hL80l۫3.8`ܹs⽵J\np,WhG?Vخt;nuq>^i7ӖsT_(M9Ui/4)8Ϩ3aL͝A"+j<%%%^HH!XYY񃃃]-MVlt֌q"-Kd+Z+Xi⎝&~7w/QULR( gP<2͐>Mō\m3Bj7Z.jѴն6O ˎ2Dp{H- a.ݘlA^(.QsӚz S%w<.`е:w[C' Jе:vSÏ'uUM) w;:,N`޼yh"@O))X⬯!u} [OhM͝\'?^NAc7 -W84QF+S,xxxH&6K%M)tcˬb٘秏Y 뀇ET(wI=>>:x{%:}e[,xYz`CL  [<=2?GOHCBi03tuu@ pfLl \"Ð6  P _jfRc \9DTUUzYM@3Fʘp+qݡZn ;oZFE5!҉mͻ7-ld-׸~(Yaq^a,,1ƞ Mv03Y bnlp#C`l$uD:-C ,`e4 /3U+?F`{@u!$jJmGEh667ڐ>>hgKUTTdQX*gh& 0T'1.O7K3Z Fl ͆"k*3ViV%W` D3R?<+^%XE8;Ϛ5k^xxohѸIENDB`yagf-0.9.3.2/src/images/trashcan1s.png0000664000175000017500000000433102263216100020135 0ustar parallelsparallelsPNG  IHDR00` sRGBPLTEk(b 24a ;s:>DG G#Gp+GjN QQTW=PeZ:QpW3U2T;T [$Z>YmbNY\c#a2_;[2^XYafP[sJ[3d+jLb4gZciaaj\bpoEfnrGg0m.p[jdbjqx1-m?}^x-y9b50%۷[W`lyB.ȗ@kY[{r k{S^x1 b =#ccKptj=}ǡΪQ-+'ol2FﰴDg{ ޟ{\ACSUaQM[m74g$9޼ONbP"  ܾൻ}v=(Q qXwCVjV0m jŻӓݾ}竴ר+a?<!eKDmUHtucccmN.ĉp?_8l7.l6()lDDO(__H*:Vw"F HGGG'''_>l ˍ#6f`0[['zW[Mm.#'C++FUӿyB,W끢J=!UſD"2 qQXfAAk}JKPRch+u7^&;3&5޳#E D4\Dz]mjv%? 0aRH"3ݔ72IObAY0j%4K}}tJC)555s`6bdnrh/RI`,fc:+ ګ$^un$1-TޘRd;4xH*( ޿kw Xi̴P ϻ_eB4=Kk4e^x>!!89d.5}gWDH={6fߖwt:~Tzm3m}bdfwgŧe!SR^|%S\_ H&]!qX8 fhh~dbb ^ d´H??(48@BكVm>Se:^K ^Ko000SvO155ťUeş C!njŞ=Z Nü;wgؙCݨ@/K s"##17M֙۹:k,olv2,=騫2u0"B! W7`M2Q>8[X@vvvI, wWS[ʔnP(tjSXu[,8QA>x.E2 i} PyIENDB`yagf-0.9.3.2/src/images/trashcan2-s.png0000664000175000017500000000432102263216100020212 0ustar parallelsparallelsPNG  IHDR00` sRGBPLTEk,^UV_OZ^5xe޽?(j~GbGsF:yhgGn B!ymmͤȯjY qD:kgO:^.k~LMKfrƸؼam蝈_g 6ܗv5ݪ<pd`PF:1xbM L&q* ))'MJIeR}IwKAAE51 `0I@uH.+h__o ~{t@"uu0'''$20c6`P"Pp_EjY!108>p\971ˢ'_蠄ZL D\MB<Ư,1?+&$Uur^&Do:ŽS-iPju:x^[[I ;)qG>5,E8Dej6f/N<~3<"Wuv2X#8=k@ӎ;:LzV3c\,g6挎MhA@ 1,ޱ318,cZ^;2(cÑυR&~߲;3[z=4rf9s8A1O/1-sks zznƳ{[Nz/.A-e/s]!Y(Iz1focxt9j:}W9棲qVK/h5J<e}]`S1ݵ'xjff&|Fε*L_ _l5@Ҏ>өSPZi!T'# z Kb%S(G3#C$7\>\.J%&ܻ/>A폅󻀩a}U 颳U82 +YN!?UFV<ӛAU^x8Bq܅  cY֨m<ϗQBdÉNPAmMR(ZF~FbV *2`p6$IrG"oK 2 Hf! z f(ð)eg|C^W,Zvb]kVXd~{IENDB`yagf-0.9.3.2/src/images/undo.png0000664000175000017500000000674702263216100017050 0ustar parallelsparallelsPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org< dIDAThՙyeU}?s򺧻gggw`aqBA4XhRa`ʔƢHLb.`R*hIhAh8Afaa_/[_x_կν~g/CWqզ <~߹|o-ĥ߱qIaD^⤗(jǻ >L^&Uvo:7~+z۶kT}r5yRP]/ ]ѽPĂ-&12GrBp2Qfb!\m4<7~iV/ `[fi_擽QB{&BZ5=#`(<^+xWttx)FntOwl۲X2[ڋw(v}2fd#~@ubޛ{G [W0ydOek;۲[2{:P% ~u^==o;sGW/cSGS"-^w;k钋Q/\?)W9W_vz{> mZ/-#QP~eV\fnmz㢫N/}͢_AtAL+yA9tILB}zٻZ(Os z* **BOӘ694&W+h=#xĥKx3;^@yedhw^81xA!REU}܍ՁԇFkS<_8N( 0}Jat_k31BR(GwѝwSE*!h} )N_ACpR/i.K[?8aoҜaT?"e!(3hp+ XE&sˑJtLCwIlȓ~:6]IfbvJ]{h;WV獁gȜS H.{-?cz@$g2:΂Z=ZjɱEq;%i_ZՓ x6O߾#["hQ< m 4S׎P݁FGc%s[oGa޳RsWg7 nʹp^މD m+6>;!²66=}4V.x(.)Hmf3ѸW͘؆0“m\uMGNgk6lsHHKHzݝOp`jGp V!xBpk5__Ē8a`x6<;M廬w]ݦYNVn4g;w i7,槄*#΋G<"Id:k$c)Ģ ‘u#g̅!4!lb\e9{y2uA*4"NZc3B5sU?;5 P?`>怈?9fOٝhB[Dp|:/GC ЈLKpx^׽OJ6>QkT>~qYͫm a5)j^g(!"=5_2)RN *ݭAZݭ2YmZ1Yc4O$O V}-9ID5&z[y䰏na#ϼ(6kBIT$6$y>'M}U#hF+NADe  ZSŚ*"5` U2뻆J7e `ZƴtO|ۯӈD^u$)B-:֤15oBTzR lU7XQk ESsѩO|o^u+v|7v֩OSo cMԱք3Y Yóo5:.(Fct)`^ {זޗG)F!1IQiwŌH5"x/PEZF4~8C_Glj7cTQqiviJRؤ`7/BH'(\m$GB*(m)X&F!=(:08[;96\XƫԴHkE:[0dC}j !%Acm\H/|uגxth0mW|CEiD1|+]Z)X+8 V1\#UE(Ţ/Fڽ3vxhحX O>_i3NQiB;mZAcE@U#A,Ac !!->}# G]wʙkY%t.C`-XfAQ6`WkƤX՘cr5cLF2iTJw +}n;mw-s;{칤z3l#!-!^p{ =~iCv#ߩ,! M9P B N~6~1iu3W:o^$`l\bt87NtR!6K32S,tBoܘv@{ ĊBIl,ԇ@-? {fm%n%8$mIˇE]A}X zf^,ZA[yQB^.~/hqIENDB`yagf-0.9.3.2/src/images/yagf.png0000664000175000017500000002332402263216100017017 0ustar parallelsparallelsPNG  IHDR``w8sRGBbKGD pHYs ^tIME #<= IDATxyeWy[Þ|s߾oꖺ5R,!`zXI ]8S;6v'eaC0’h!jVÝs|Ξc!@Tw~UޮZZm2dȐ!C 2dȐ!C 2dȐ!C 2dȐ!C 2d}7_F~2îcsjE{">T}5RB~z.^f O?zf%| Uz:=V.n$8Z 足@u58}xU}FΟ-n8 = `bmIX[b38 (*,oe J(n&I/$?w|@ r4W07i,\΄qm#à6ZgWuGU=qkP.CV S|Dppj#D̍C^QIl5a} 4LMfcپc/︓ͯ}V97Q]3Pp ? ^ZN"vlCa &!J%C\ 7T&F.<j5mT|ػ S#p^Madfn\&T,+c~mR榱̰_^nR4JDqv~G_^mU\77ͯ&wi@h ۠®mHa#DA4WŒM85ZVP!ٷtrUPH^h7a0&&jUnp oӏ|{{K4]Csp^5&1#c$rʥyey4Hb6_@D)"d^ z0R@?83bnz?|k'js]!Lµ0_tXcē;!&H*\ ,`BIDZ PBQp aqEIi7ʙ#Gɕs8tf}8n+BF}p,9\D$$(xЕ`bPH, KbLD$4DL2zz)3gΓ_*Y̹G XNyAqdl;bĤ2>y鲁A p _%q<b\Z~ NJIF^Za^:?k>M8cw_ɕr^@Ai!ZE;eZJ!5N) HӔ8tE*hI q]8(d?@cgNzIfե [C` G$#.H7M\!g@ UjDG$Tq  Da/$A16N::2耑˜N*9.-/Dދ{oX[nt {0:-VG3 l)o 1TiH JL]X T&%"QP"Mb&jc Y]˹ݷpQo<&P*p>3c0:N&&G"/[HZ9?PTvɩ"](, lJ8MVbME  趩 Kx _Vop?_f3m}2] Zi0F]KQ$4 \LBl,Qo1֢%mE+6 pH;nDavN|Y>rœtȚõ ˮeT=vp$ۈrU! Ghh]1˸G>Tı6$KE$r#p?aƐ D`RK,>Rߴ?7̀_ 82x)FHKhRR-J ̳ú_q} RK( -A g8AF|zA PdK(!P v.dDC}\4_~׿ p0RT$&HS&`BQ_@(DpB ́8E]D*BT)I"t$J$:O%@ ".W#U XnC"%? leJ痻`tOA8Bi R\"YM}h@\\I̒[@[[+ lU'g&Yj\pHy^f}10D/c :Im XH̕S {ہ}=b1# 5qz LA2"]%݉`FPA.- Kn[|8A)"$ 쾊8Wd@)N"I@zB 4QW(>|ku"Ґ-eX{t@"!p }XC' '? qna<@( 9g4fLUYL[8  ~&,wP16hZ ؊uf@O'Ybmz k1ޠ@+ Q;G(QyGzxR;Q("* +>IဴNbLd) W`]:UHc$cc:P9mtĒ"PQ%0<ғ)fIAH)ұ->+P.=]$!&^H?=ePF6;H%ԪXÌOt(s|pQh''3#HMnQRF{ x& 4UaXnR *Spq@!j/>F]O&쁗$B @JHah: NH )}5>|w"&(Q2w#\3@ZU$HWt B>KQl `ZOYF2@i{7 7QDCL GČ*?J2\J~X!u؉I'K I"/]b95 GK0*>ˈ0Aw+HrjQ6A p%SPƍأG>|Fc8xG"W2W,'ꐌ-] 0ңWtIEL\3NurDŽu@jyP, !RyDF'V"*mS80SV_@w! N Pw"n!!QqLɇ SO`s !z1#-s AjNA qJm2 (n]"q (,o[p8㨧E&t`p$6|5D KO㬞Al#$s}6Wf]Ľ_=3P]K͇7`]F vG0857rݱY^ `CԽ{/D7;8}0? ډp0BQ5ĭ{1q%`{Ƿ ov.^M קVV|b#r/޽$($<,ZQ YMzn U#_w6.]DuTKGBOezv!޶~ǰ{$Q(zQ(y[mrpxJF [8ZqtvmIZTy;i%ɋjCͥ>?w32NOB`|Q(bp[>-\ xQO%yA[G||spy3;sݻ6]T5BXWƹR*xpzY7zE,1A\׼tj x<32@vo E4w Iᕪ"ǁr{ZG~_zF6j%b q LH*jtzי Q݆v%LMIG!8v4Ћ@ nv+y p>_KxD>h7J.$pE$R#tFrS/P]u툷=xKOBO@I,cAȬ%4('s*%n;JU 0`xF&J2A ֕.,ѬEPE*(%IY7NSĹM;ߍ|~S/F8 iew::|saL>E.BxRbaɣ1A긃Տ1-"&eY8H}-i4J\d^hKKdSDs$\Z`Z M@̃ӧ"TlG_Ʀt} M|o Gie5} qF n5fg|?SdhuGoAQ)'ւ\ۆTOù݋0^`5_a3հ DF:0 g#ID3ȠRX1; ) dV 6D2A~zxM B'4Ž-@R?œ9{>?EduL};be@Y I|-\%J4DYscN˛$:v,3aĴJ0^xkD]Ԉf`P,^C:EznϖY݇V$X+AQ@9RL$Y?1q_ydߖcWAu;HT@Iw$bL_#ؠ@Vc ۬.mR9_,^sM+5e& "Mb/ț%=O[#U})~+5Ribrp"v^ا仐s5b0/!ZE?s"3k0Y~떷28|v#DGbz]PY<b$ -BPB̿:- 'ь"DkJĩ83ai 67`Fso_x"I>JcqL3аևV`umDvq!(kq^sG0RSyEM4̺."_WĆ-r[E@2 4YKNc)+hs&_ 5 \܂|.ZP Ľ:G}I曂W>π80YR$,X-D#xu_$[!^?wh`q|B]9IX\F*aqx  Tk\s#"m i!sh10i+8B$+Up=~I+ 2bk^'Q鷨ʩd.*& +Y7Lo"+oq2}- L!fMf֒( /J+wJ '. ZA·HfX[$X#o(cQ"!1=C @ܯЖ?N/?N1 CtCG=JRSR%%m_[ktރ/B}8tyIz]"r|4(t+]x<$12Zr4M{8iH"ٹv'p/IcQ4$M l`+eӈKˈ."֪U~[[G!~MLh~yWO=^_Z3 18i$n5`P 3qHIqjnBNIBEǖ\RNzvbk {b}oIx*>@[㤉[o e_dyaqgP6J4&P h >q*Yى)ab̀1ٍIRk -p4c(Σo{K7R1a§HYh[ H ڤZD}*O'T"S %‹&!L80&qr|P / ^}wexaay0+\N}o{ _'+P-d-ɂ-v\<."JP@X ,PK#";$fHUl8I?_ZVo*bi=/H…ok{0g,JTRlwJޅZ(*1"E#3Idkb \1pˤA=L\X@l @93L[Y-:|  `lD1T΢u)0%VNc4p%{ Q\#hFFc'`7PN: /'gPߌn_oG%~dRAyK9[xZJHĕh/d@*$G%ˆ:HIDAT#r&N{ |v-x,aD򧓳{6N.6Ys#fmdmFTP!F=7@ Eb$&FU "O^U)]TP&M"r8kKǐM8'gd9|ǿW}}\ꆸ*!6cr5Qm=IDlcGx~?$C}Sp| N-edee˟F}ͽ{ }{XVGK*8l[\GֳTBG"<>E8i*•8qz/po<D>};WT`v7o/erZ.|Y>0E edR@K ë#)JE*+*NIح%Q-r6 l4ZNАry'x/z)>4H3|Qc#`ԙ`|4"lצ .:wnepiQ,oh'? kYw)4ri>\AM*_nXLWvp}!T"۶tцc:YV6`#[|~el-ѽ^gdV'R Q+hm( R{ O^5sHB"+Gq.ZZp#0VF#$EEc8ʂZ \3͹53sp`{4LN`GY9A lJ`q+_LƋ,T|2 WoƹfƋGhNFffƲS+ vLabwm&b"ڝ'NZ'3~6eg& t<ɟS?97orR˰@,L 6BaE6Togg 03BWe^257m-\3qi 3~ݬ-gxg|) deܬq#Ho{(C~w88>O7hcNVA5Q ف*: lo$bĤɡ ޾Omflރ0zI֕*(jzթ )o6Y?xӜ׫ n}/PfmT*o}d!ѕρhZ=X;qm%+dkwpw._@jqn6EF~ĆCs?>y$kWGaBq,}ޜ'pNAZxm^_*׵ /}5Q\ؠ ᫡"ūBf/$*j_Iu,}a ?=r 7Q0|?#0dȐ!C 2dȐ!C 2dȐ!C 2dȐ!C 2dȐ6c[IENDB`yagf-0.9.3.2/src/images/warning.png0000664000175000017500000000526502263216100017542 0ustar parallelsparallelsPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org< 2IDAThZ[lΙbM8e.:Fun$ƐD!OQE`P7+C>!E*(XIJD m$!AXrVP0 `^_a;sٹ>ewצQGuz97ݨe`,B<ZA0`1ZEжq+#<nii +Wb7?~˟SVa؊ @ǣsss 4e2N1hMXWGEQȲfffX<g,q6112 E1}<+m] ?t/9y1j>4v͛aV>]!*zՂ R BSf' xf=b.IRX@!pH&j߷oN뭺JQj";U[[$*J_2`rdv9~BfcRpާ`PU<Y!@evy@i@klo`HU՜/lL0 {@)kqvee%Lb*~KQa X ߿px}02.iYza~A@ ܿ3¥PM`-]׮]U(ju~ Ԑ}q0jEˑJƚT(FB~SSSu }\hiK_9dYF(j,Ub((ojjry_3)22ş>7tG `tkAQԙˑL&KRa)3::H$ 7Y,r6d|sEqFk? y~%*,Evԩ{^.0̋VHTf>̹O Iit LM~4(zv.M$ïOc.@ 򢏪( i_2!Q {oާ4Msa\{ ;.e!2dYeIMcP b'zw*EpD"9w\g4|#swӡ 7M| -= ]A܆,y\Jkh`'CBQB<] lӭ===O*7 aSL.OC\Cwō7'bi0]t54X}}}Q ɤgpp0{(w{-iЂ(++[>-~ lpV! (ĉSSSm/S,;1i||W&dl6a h KӞ/2smuuu ,& ;q ge(8ǁŃ:q  UUV9>x@ АdrKEC~( vus⻛;CB4_~AIK .omZ"mm_>%ES/6o`$itͺԮiJk03D;::Z @+lj[8h!~V_ٲeKbW [WzC0z=[fY_ypAi.X0oYEU&RYWTUUٓ/0!dȑ#_=sO֒[nRЈFN1uΰ1$! ?_ơtɓ'`1!>4777%IV4@ZkjjTU17xIߏ 65bϭ~L٫:MԃVU*4%:}@u L~?-۫}.)*Ip?'SWr_ڛᆴ^{m[GGG>R ۻp[|(61?z,SBhcIENDB`yagf-0.9.3.2/src/images/trashcan_full.png0000664000175000017500000001470002263216100020714 0ustar parallelsparallelsPNG  IHDR@@iq pHYsHHFk> vpAg@@`bKGDIDATx[ \U_[^ldB  F@DGef̌0a< QF1E @֡ӝZz՝Nҝ43u}|o+ra  $I+vd'|J SKS[[+_jW_I'@s:Aș_~~H$ᣟxHDAz"sc. .]zٓp֨t&3x1Duv7:(x{vjdznRpo:b޹eepժUSFVlvk mPWW7e'' ?} \+6pKMwBxUyy5?v{{[i#g;Z^u R%WQP:Xb5)x<;hN -tPЁͤ)Kɡ0ۇ<džo,^a.$ /61m#ްWUΞ= k֬la-x< lwBK =4.CLIc|NE4@fNH{; XzTIV~b8J>o67-B 2 fpss;V=|-p@#h 8]="Yp|:^::JKHEcH0-LwTF~;>.6L`qvlKKAFV=7'̓!uɴ4|ow DG#"`^t19Rb5arKЉNO["0l.*,56XM;RNgbPҡ#PQ0H%|CrrOfZ5h{Dʐ(Cƞ ;TTqR*:,q^hϷ0d8.>ݕqlϧ`*+j\\|aF8PӀNFg>PdGRYP8σENb>tro^Q$ n(:_⹨|&i]8rdMPP( "%TA%xq  \i9yMDki:F#9EVҬ*;q)Fsf& \jff2Ӊ_&1@D*hw1Db&w/gVK"#A\Hzɣd2GR`8n.f35daqv Yv2|f^Z!!IZWnBfZ!:|hhD3RY TKW5^W Tb"ufx Rp}xF\9ڴ v ޫ'@X'XgdP / 頋 ;\ŌLʤjrG$˴**Z-}E P !4a"CC$XN(6CדvV]dg;47 !Ue`H6@/pIÙĈk(t{g. @͒5BU,OVI[ `2JEƘ1\!K)D(Q2őx+1yR1LY3g `8B( nT!?(NOf4p\H5,_\pQ响m͞KES 8LC4wmC!$CB")a-Yj sDJv(DSB"P_otW@$F.J[ $HбTaȃ0tP'iMq>_ppdyNQA4II% 9`rGuFQvC20MAMB/B"G{$1t)_?uk -Q "=XrT_Eא%}`b30e}`!K 통1m3" H6,4c PiP4 #o'(9o8/;ng vqwnЬs> p7\mn sP ;0U+Ѱ1/Ew_x\ G;͵> u?0'tB*w}h1-& U6hU7o\>ud9N@ȔEاwHs;YM/2,_-RGFC.G*tb)nZm]: Ouo:uX&98Qh5" _LXZ3cR{8ݡHct>C*mfӺDKjGFNT[M%]WO| wFIv*Bv>H DC jaN@u}JZvMܱeyڿT%#(ɖU6g G`c58u'oU+|w0BlfV9j(*i; T쑼sx<̰KECߒx{M~lV_d&`XA+nfI`3UJa'~tvl] ^*`=P40,G$b.ckO]+~@I /Ry|2l[ s&W{+k! fksi~:}Nx}>q޲TwUjcNH0mDØ~O&r $h5epGH LԛP HQ@ʩWĮըWw0{?c;s*6,.R#j41MT(Gn!'%@5 "BAN 5PQi;AI KtgGTsQiPf@($(RS“+Qx+!@qeKɄȏ7NT$ zoth04TW,IO 4^huNUms!eL+/GU02|^t\'р|u>;k,M|?K>$-XPy}_X}l&CWV{ X,>o%z-YP+%(uU1wTj%u99 :PRGȞ zܔP%&kx"IJ{ {!!2zBUR bIY[I * D'-h7~2, 6;f-kc!qk{aZ9$M@V ?CQ`|AOG ePSS͎ x&f=MhIҠ*4P<d endPj WnDBk o?h`[@2,vWF0#;ZF`Ȗʧ#}RonZ7ت.2+>۰t8q80RJLb^G* G`bb{Q_Pn“_>JA30ckèP쏦ai􏅁iYJĢTxz>s,$ 8`R8'/߿CڶvaX6QQ'GGƑ ΨH3$8L$x&dD0OƊL% w`Y uu'˷zΔ/Owyk}Dz:ub$KF L "YYU}$t"4rUIJ{C̐hĐF"Z(d`XlJz9Z 9יXD[2Ë::2ڑ{1O+JM'%sdp \93-t$I 1  w&}J{QhZMks疲]S UTgf#Ӝ%d(pS=l]^V8L,nwHz Tc_d5аGR`f_,~$\4'Ф$Jnq-Zشr>C'ckӏ0;I$?LLoW%^S9^Rdrт;O)G֝- lq~/$LrwUrd}ﶞ=wI_7\4GCD2=UB4&sGd rJ{I'&E>3S#x`\uvx4n /7W{wl/ [ [1~irPdR=]z3pͽ=}M8y.-ٶsOIfi;]5,jt cҩ{9ꃧ~=s,2txZ^|QVշu/hNw/kGq3ArooYQ]spJ$/Y-rX|l{2EP7쇗wό%ވ3[&Otei鮯0=mGrۚFcjVZLa6]QU3L׌  Rcn8:z{ϑWYw>R.2аtm¥tp` u(|n̲lB"V9VZUFEuY 1գ*Ъg ž MxP|dR|n.W1閦7˝re屠[iR㫴PRWŨ  A&W\g 3 )6@H%+rNg^vގzLseq&q tlZ ConfigDialog YAGF Settings Les paramètres du YAGF OCR and Languages OCR et langues Image Processing Traitement des images Program Appearance Apparence du programme OCR Engine Engin OCR Cuneiform Cuneiform Tesseract Tesseract Path to Tesseract Data Files L'accès aux données de tesseract ... ... Recognize Languages Reconnaître les langues Use Single Language: Une seule langue: Choose Among Several Languages Choisir de quelques langues Languages to Use... Les langues utilisées... Enhance Image Quality Améliorer la qualité de l'image Crop Image When Loaded Recadrer les images lors de l'ouverture de fichiers Deskew Loaded Images Corriger l'inclinaison des images Preprocess Images When Loaded Le prétraitement des images Interface Language La langue de l'interface Select Language of the Interface: Choisir la langue de l'interface: Toolbar Icon Size Les dimensions des images sur le toolbar Normal Normal Large Large Tesseract Data Directory Le dictionnaire des données du tesseract LangSelectDialog YAGF Languages Setup Choix des langues Select the recognition languages Choisir les langues pour la reconnaissance Cuneiform Cuneiform Tesseract Tesseract Installed installé Not installed Non installé MainForm Recognition language La langue pour la reconnaissance Using Cuneiform Utilisation de Cuneiform Using Tesseract Utilisation de Tesseract Recognition Language La langue pour la reconnaissance Loading files... Chargement des fichiers... Cuneiform doesn't support any of selected recognition langualges. Falling back to tesseract. Please install tesseract. Cuneiform ne fonctionne pas avec les langualges de reconnaissance sélectionnés.Mise à tesseract. Tesseract doesn't support any of selected recognition langualges. Falling back to cueniform. Please install cuneiform. Tesseract ne fonctionne pas avec les langualges de reconnaissance sélectionnés.Mise à cuneiform. No PDF converter installed Pas de convertisseur de PDF est installé No compatible PDF converter software could be found. Please install either the pdftoppm utility or the GhostScript package (from this the gs command will be required). Pas de convertisseur de PDF est installé.Installez soit pdftoppm ou GhostScript s'il vous plaît. Error Erreur PDF file name may not be empty Nom du fichier PDF ne peut pas être vide Open Image Charger un fichier image Image Files (*.png *.jpg *.jpeg *.bmp *.tiff *.tif *.gif *.pnm *.pgm *.pbm *.ppm) Fichiers image (*.png *.jpg *.jpeg *bmp *.tiff *.tif *.pnm *.ppm) There is an unsaved text in the editor window. Do you want to save it? Il est un texte non enregistré dans l'éditeur Voulez-vous l'enregistrer? Scanning is impossible La numérisation est impossible No scanning front-end is found. Please install XSane in order to perform scanning. Aucun numérisation frontal est disponible. Installez XSane s'il vous plaît. Failed to Load Image Impossible de charger le fichier Cannot open file %1. Make sure imagemagick and tifftopnm are installed. Impossible de charger le fichier %1. Assurez-vous que tifftoppm et imagemagick sont installés. You have selected recognising %1 language using tesseract OCR. Currently the data for this language is not installed in your system. Please install the tesseract data files for "%2" from your system repository. Vous avez choisi reconnaissant %1 langue à l'aide tesseract OCR. Actuellement, les données pour cette langue n'est pas installé sur votre système. S'il vous plaît installer les fichiers de données de tesseract pour \ "%2 \" de votre référentiel de système. Starting tesseract failed Impossible to activate tesseract The system said: Le système dit: program not found Programme introuvable Starting cuneiform failed Impossible to activate cuneiform No image loaded Aucun fichier d'image est chargé About YAGF <p align="center"><b>YAGF - Yet Another Graphical Front-end for cuneiform and tesseract OCR engines</b></p><p align="center">Version %1</p> <p align="center">Ⓒ 2009-2014 Andrei Borovsky</p> This is a free software distributed under GPL v3. Visit <a href="http://symmetrica.net/cuneiform-linux/yagf-en.html">http://symmetrica.net/cuneiform-linux/yagf-en.html</a> for more details. http://symmetrica.net/cuneiform-linux/yagf-en.html Recognizing pages... Reconnaissant les pages ... Abort Abandonner Importing pages from the PDF document... Importation de pages du document PDF... Cancel Annuller Warning Avertissement cuneiform not found, switching to tesseract pas de cunéiforme, utilisant tesseract No recognition engine found. Please install either cuneiform or tesseract Aucun programme de reconnaissance est disponible. Installez cunéiforme ou tesseract s'il vous plaît tesseract not found, switching to cuneiform pas de tesseract, utilisant cuneiform Save Image Enregistrer le fichier Failed to detect text areas on this page. The page possibly lacks contrast. Try to select blocks manually. Impossible de détecter les modules de texte. The selected directoy is not empty. Please select or create another one. Failed to save the project. Impossible d'enregistrer leprojet. Failed to load project. Impossible de charger le projet. MainWindow MainWindow &File &Fichier &Help A&ide &Edit &Edition toolBar toolBar_2 toolBar_3 S&can... S&canner... Scanning images using XSane... Numériser des images avec XSane... Ctrl+N &Save text... &Enregistrer le text... Ctrl+S &Open Image... &Charger un fichier image... Open Image Charger un fichier image Ctrl+O &Recognize &Reconnaître Recognizing text... reconnaissance... Ctrl+R Choose &Language Choisir la &langue Quit Quitter Ctrl+Q Previous page Page précédente Move to previous image Déplacer à l'image précédente Next page Page suivante Move to next image Déplacer à l'image suivante Online Help Aide en ligne About... Sur le programme... Copy Text to Clipboard Copier le texte dans le presse-papier Copy recognized text to clipboard Copier le texte dans le presse-papier Recognize &All Pages Reconnaître &toutes les pages Recognize All Pages Reconnaître toutes les pages Ctrl+A Clear all blocks Enlever tous les modules Delete the current block Enlever le module actif Recognize block Reconnaître le module Recognize this block Reconnaître cet module Clear numbers Enlever numération Crear block numbering Enlever numération des modules Check spelling Vérifier l'orthographe Save current image... Enregistrer l'image active... Save currently opened image in its real size Enregistrer l'image active dans sa taille réelle Save block Enregistrer le module Save the current block to the image file Enregistrer le module actif comme l'image Deskew Corriger inclinaison Correct the page skew Corriger la page inclinaison Select HTML format Choisir le format HTML Select HTML format as recognition output Choisir le format HTML Larger view Pllus large Smaller view Plus petit Rotate 90 CCW Tourner 90 CCW Rotate 180 Tourner 180 Rotate 90 CW Tourner 90 CW << Hide/Show Toolbar Cacher/montrer la barre d' instruments Import from PDF... Importater de PDF... Import pages from PDF documents Importer des pages des documents PDF Settings Réglages Paste Image Coller une image Paste image from clipboard Coller une image du presse-papiers Select Text Area Sélectionner le texte module Deskew Block Corriger l'inclinaison du module Deskew the current block Corriger l'inclinaison du module actif select multiple blocks Sélectionner plusieurs modules Splits text into several blocks Diviser la page en plusieurs modules Toggle Large/Small Icons Changer la taille de boutons Select Recognition Language Choisir la langue pour la reconnaissance Save Project... Enregistrer le projet... Load Project... Charger le projet... Select Recognition Languages... Choisir les langues pour la reconnaissance... Select Recognition Languages Choisir les langues pour la reconnaissance Select All Text Sélectionner tout le texte Select all the recognized text Sélectionner tout le texte reconnu PopplerDialog Import from PDF Importer de pdf File Name Nom de fichier Select... Choisir... Pages Pages From de To jusqu'à Entire Document Tout le document Don't Deskew Pages Ne pas corriger des inclinaisons QObject Warning Avertissement Failed to save the image Impossible d'enregistrer le fichier JPEG Files (*.jpg) Fichiers JPEG PNG Files (*.png) Fichiers PNG Select Project Directory Choisissez le catalogue du projet Required spelling dictionary (%1) is not found. Spell-checking is disabled. Try to install an appropriate aspell dictionary. Le dictionnaire d'orthographe (%1) n'est pas trouvé. Essayez de télécharger le dictionnaire convenable. Russian-English Russe-Englaise Bulgarian Bulgare Czech Tchèque Danish Danoise Dutch Hollandaise English Anglaise French Française German Allemande Hungarian Hongroise Italian Italienne Latvian Lettonne Lithuanian Lituanienne Polish Polonaise Portuguese Portugaise Romanian Roumaine Russian Russe Spanish Espagnole Serbian Serbe Slovenian Slovène Swedish Suédoise Ukrainian Ukrainienne Albanian Albanaise Ancient Greek Grec ancien Azerbaijani Azerbaïdjanaise Croatian Croate Danish Gothic Danoise gothique Estonian Estonienne Finnish Finnoise German Gothic Allemande gothique Greek Grecque Hebrew Hébreu Icelandic Islandaise Macedonian Macédonienne Middle English Anglaise du moyen-âge Middle French Française du moyen-âge Norwegian Norvégienne Slovakian Slovaque Slovakian Gothic Slovaque gothique Swedish Gothic Suédoise gothique Turkish Turque SideBar Drop files here Déposez les fichiers ici yagf-0.9.3.2/src/translations/yagf_ru.ts0000664000175000017500000007627602263216100020661 0ustar parallelsparallels ConfigDialog OCR Engine Программа распознавания YAGF Settings Настройки YAGF OCR and Languages Распознавание Image Processing Обработка изображений Program Appearance Внешний вид программы Cuneiform Tesseract Path to Tesseract Data Files Расположение данных Tesseract ... ... Recognize Languages Распознавать языки Use Single Language: Только один язык: Choose Among Several Languages Несколько языков Languages to Use... Языки... Enhance Image Quality Улучшить качество изображения Crop Image When Loaded Обрезать изображение при загрузке Deskew Loaded Images Автоматически корректировать наклон Preprocess Images When Loaded Корректировать изображения при загрузке Interface Language Язык интерфейса Select Language of the Interface: Выбрать язык интерфейса: Toolbar Icon Size Размер иконок Normal Обычный Large Крупный Tesseract Data Directory Расположение данных Tesseract LangSelectDialog YAGF Languages Setup Выбор языков для распознавания Select the recognition languages Выбрать языки для распознавания Cuneiform Tesseract Installed Установлен Not installed Не установлен MainForm Recognition language Язык распознавания Open Image Открыть файл Image Files (*.png *.jpg *.jpeg *.bmp *.tiff *.tif *.gif *.pnm *.pgm *.pbm *.ppm) Графические файлы (*.png *.jpg *.jpeg *.bmp *.tiff *.tif *.gif *.pnm *.pgm *.pbm *.ppm) There is an unsaved text in the editor window. Do you want to save it? В окне редактора несохранённый текст. Сохранить? Warning Предупреждение Error Ошибка No image loaded Изображение не загружено Starting cuneiform failed Не удалось запустить cuneiform The system said: Ответ системы: program not found программа не найдена About YAGF О программе http://symmetrica.net/cuneiform-linux/yagf-en.html http://symmetrica.net/cuneiform-linux/yagf-ru.html Save Image Сохранить изображение Recognizing pages... Распознаем страницы... Abort Прервать Importing pages from the PDF document... Импорт страниц из документа PDF... Cancel Отменить No compatible PDF converter software could be found. Please install either the pdftoppm utility or the GhostScript package (from this the gs command will be required). Не найдены подходящие конвертeры PDF. Установите, пожалуйста, утилиту pdftoppm или пакет GhostScript (из которого требуется программа gs). PDF file name may not be empty Необходимо указать имя файла PDF Starting tesseract failed Не удалось запустить tesseract cuneiform not found, switching to tesseract Программа cuneiform не найдена, переключаемся на tesseract No recognition engine found. Please install either cuneiform or tesseract Программа распознавания не найдена. Установите, пожалуйста, cuneiform или tesseract tesseract not found, switching to cuneiform Программа tesseract не найдена, переключаемся на cuneiform No PDF converter installed Конвертер PDF не установлен Using Cuneiform Используется Cuneiform Using Tesseract Используется Tesseract Scanning is impossible Сканирование невозможно No scanning front-end is found. Please install XSane in order to perform scanning. Программа сканирования не найдена. Необходимо установить XSane. Failed to Load Image Невозможно загрузить выбранное изображение Failed to detect text areas on this page. The page possibly lacks contrast. Try to select blocks manually. Не удалось найти текстовые области на странице. Возможно, изображение было сканировано со слишком низким контрастом. Попробуйте выделить блоки вручную. The selected directoy is not empty. Please select or create another one. Выбранный каталог содержит файлы. Выберите пустой каталог или создайте новый. Failed to save the project. Ошибка при сохранении проекта. Failed to load project. Невозможно открыть проект. Loading files... Загрузка файлов... You have selected recognising %1 language using tesseract OCR. Currently the data for this language is not installed in your system. Please install the tesseract data files for "%2" from your system repository. Вы выбрали %1 язык для распознавания, используя программу tesseract. Данные для распознавания этого языка в настоящее время не установлены. Пожалуйста установите языковой пакет %2 из репозитория вашей системы. <p align="center"><b>YAGF - Yet Another Graphical Front-end for cuneiform and tesseract OCR engines</b></p><p align="center">Version %1</p> <p align="center">Ⓒ 2009-2014 Andrei Borovsky</p> This is a free software distributed under GPL v3. Visit <a href="http://symmetrica.net/cuneiform-linux/yagf-en.html">http://symmetrica.net/cuneiform-linux/yagf-en.html</a> for more details. <p align="center"><b>YAGF — графическая оболочка для программ распознавания текста cuneiform и tesseract</b></p><p align="center">Версия %1</p> <p align="center">Ⓒ 2009-2014 Андрей Боровский</p> YAGF — открытое программное обеспечение, которое распространяется на условиях лицензии GPL v3. Вы найдёте подробности на странице <a href="http://symmetrica.net/cuneiform-linux/yagf-en.html">http://symmetrica.net/cuneiform-linux/yagf-ru.html</a>. Recognition Language Язык распознавания Cannot open file %1. Make sure imagemagick and tifftopnm are installed. Невозможно открыть файл %1. Проверьте, установлен ли imagemagick. Cuneiform doesn't support any of selected recognition langualges. Falling back to tesseract. Please install tesseract. Cuneiform не поддерживает ни один из выбранных языков. Используйте Tesseract. Tesseract doesn't support any of selected recognition langualges. Falling back to cueniform. Please install cuneiform. Tesseract не поддерживает ни один из выбранных языков. Используйте Cuneiform. MainWindow S&can... С&канировать... Scanning images using XSane... Сканируем страницу с помощью XSane... &Save text... &Сохранить текст... &Open Image... &Открыть графический файл... Open Image Открыть графический файл &Recognize &Распознать Recognizing text... Распознать текст... Choose &Language &Выбрать язык Quit Выйти Previous page Предыдущее изображение Move to previous image Перейти к предыдущему изображению Next page Следующее изображение Move to next image Перейти к следующему изображению Online Help Справка онлайн About... О программе... Clear all blocks Очистить все блоки Delete the current block Удалить этот блок Recognize block Распознать блок Recognize this block Распознать этот блок Clear numbers Очистить нумерацию Crear block numbering Очистить нумерацию блоков Check spelling Проверить орфографию Save current image... Сохранить изображение... Save currently opened image in its real size Сохраняет полученное изображение в реальном размере Save block Сохранить блок Save the current block to the image file Сохранить этот блок в графическом файле Select HTML format Выбрать формат HTML Select HTML format as recognition output Выбрать HTML в качестве формата вывода распознанного текста Larger view Увеличить Smaller view Уменьшить Rotate 90 CCW Повернуть на 90 градусов против часовой стрелки Rotate 180 Повернуть на 180 градусов Rotate 90 CW Повернуть на 90 градусов по часовой стрелке &File &Файл &Help &Справка Copy recognized text to clipboard Копировать текст в буфер обмена MainWindow toolBar Ctrl+N Ctrl+N Ctrl+S Ctrl+S Ctrl+O Ctrl+O Ctrl+R Ctrl+R Ctrl+Q Ctrl+Q Recognize &All Pages Распознать &всё Recognize All Pages Распознать все страницы Ctrl+A Ctrl+A << << Hide/Show Toolbar Показать/убрать панель Import from PDF... Импорт из документа PDF... Import pages from PDF documents Импорт страниц из документа PDF Paste Image Вставить изображение Paste image from clipboard Вставить страницу из буфера обмена Select Text Area Автоматически выделить текстовую область Deskew Block Выровнять блок Deskew the current block Выровнять выделенный блок Deskew Выровнять Correct the page skew Исправить наклон страницы select multiple blocks Разделить текст на блоки Splits text into several blocks Разбить текст на блоки Toggle Large/Small Icons Большие/маленькие значки toolBar_2 Select Recognition Language Выбор языка распознавания Save Project... Сохранить проект... Load Project... Загрузить проект... toolBar_3 Select Recognition Languages... Выбрать языки для распознавания... Select Recognition Languages Выбрать языки для распознавания &Edit &Правка Copy Text to Clipboard Скопировать текст в буфер обмена Settings Настройки Select All Text Выделить весь текст Select all the recognized text Выделить весь распознанный текст PopplerDialog Import from PDF Импорт из документа PDF File Name Имя файла PDF Select... Выбрать... Pages Страницы From С To по Entire Document Весь документ Don't Deskew Pages Отключить исправление наклона страниц QObject JPEG Files (*.jpg) Файлы JPEG (*.jpg) PNG Files (*.png) Файлы PNG (*.png) Failed to save the image Не удалось сохранить файл Required spelling dictionary (%1) is not found. Spell-checking is disabled. Try to install an appropriate aspell dictionary. Требуемый орфографический словарь (%1) не найден. Проверка орфографии будет отключена. Попробуйте установить соответствующий словарь aspell. Warning Предупреждение Select Project Directory Выбор каталога проекта Bulgarian Болгарский Czech Чешский Danish Датский Dutch Голландский English Английский French Французский German Немецкий Hungarian Венгерский Italian Итальянский Latvian Латышский Lithuanian Литовский Polish Польский Portuguese Португальский Romanian Румынский Russian Русский Russian-English Русский-английский Spanish Испанский Serbian Сербский Slovenian Словенский Swedish Шведский Ukrainian Украинский Finnish Финский German Gothic Немецкий готический Greek Греческий Hebrew Иврит Norwegian Норвежский Swedish Gothic Шведский готический Turkish Турецкий Albanian Албанский Ancient Greek Древнегреческий Azerbaijani Азербайджанский Croatian Хорватский Danish Gothic Датский готический Estonian Эстонский Icelandic Исландский Macedonian Македонский Middle English Старо-английский Middle French Старо-французский Slovakian Словацкий Slovakian Gothic Словацкий готический SideBar Drop files here Перетащите файлы сюда yagf-0.9.3.2/src/BlockAnalysis.h0000664000175000017500000000277402263216100017033 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BLOCKANALYSIS_H #define BLOCKANALYSIS_H #include #include class QPixmap; class QImage; class QRect; class BlockAnalysis { public: BlockAnalysis(QPixmap *pixmap); ~BlockAnalysis(); int getSkew(); int getSkew1(); int getSkew2(); QRect getCoords(); QPixmap getPixmap(); private: QImage *m_image; char **m_blockMap; int longestLine[3]; int longestCount[3]; QRect *m_coords; qint64 mtreshold; quint16 *linesInfo; QRgb * * lines; void newBlock(); void deleteBlock(); void countLinesInImg(int factor, int d); void countLinesInImg(QImage *_image); void preScan1(); void deleteLines(); bool badImage; void createLinesInfo(); }; #endif // BLOCKANALYSIS_H yagf-0.9.3.2/src/BlockAnalysis.cpp0000664000175000017500000003034102263216100017355 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "BlockAnalysis.h" #include #include #include #include #include //#define DEBUG BlockAnalysis::BlockAnalysis(QPixmap *pixmap) { m_image = new QImage((pixmap->toImage().convertToFormat(QImage::Format_RGB32))); badImage = m_image->isNull(); this->m_coords = new QRect(m_image->rect()); m_blockMap = NULL; } BlockAnalysis::~BlockAnalysis() { deleteBlock(); delete m_image; } void BlockAnalysis::newBlock() { /*deleteBlock(); m_blockMap = new char * [m_coords->height() - m_coords->top()]; for (int i = 0; i < m_coords->height() - m_coords->top(); i++) m_blockMap[i] = new char[m_coords->width() - m_coords->left()]; for (int i = m_coords->top(); i < m_coords->height(); i++) { QRgb * line = (QRgb *) m_image->scanLine(i); for (int j = m_coords->left(); j < m_coords->width(); j++) { if ((qRed(line[j]) + qGreen(line[j]) + qBlue(line[j])) < treshold) m_blockMap[i-m_coords->top()][j-m_coords->left()] = 0; else m_blockMap[i-m_coords->top()][j-m_coords->left()] = 128; } }*/ } void BlockAnalysis::deleteBlock() { if (m_blockMap != NULL) { for (int i = 0; i < m_coords->height() - m_coords->top(); i++) delete m_blockMap[i]; delete m_blockMap; m_blockMap = NULL; } } void BlockAnalysis::countLinesInImg(int factor, int d) { int ftmp = factor; if (m_image->isNull()) return; for (int i = m_coords->top(); i < m_coords->height(); i++) { int l = i; linesInfo[i] = 0; ftmp = factor; for (int j = m_coords->left(); j < m_coords->width(); j++) { int k = qRed(lines[l][j]) + qGreen(lines[l][j]) + qBlue(lines[l][j]); if (k >= mtreshold - 1) { linesInfo[i]++; #ifdef DEBUG if (d != 0) m_image->setPixel(j, l, (1 << 32) + 1); #endif } else break; ftmp--; if (ftmp == 0) { ftmp = factor; l -= d; if ((l < m_coords->top()) || (l == m_coords->height())) break; } } } longestLine[2] = longestLine[1]; longestLine[1] = longestLine[0]; longestLine[0] = linesInfo[0]; for (int i = m_coords->top(); i < m_coords->height() ; i++) if (linesInfo[i] > longestLine[0]) longestLine[0] = linesInfo[i]; longestCount[2] = longestCount[1]; longestCount[1] = longestCount[0]; longestCount[0] = 0; for (int i = m_coords->top(); i < m_coords->height(); i++) { if (longestLine[0] - linesInfo[i] < 5) longestCount[0]++; } #ifdef DEBUG QImage im = m_image->copy(*m_coords); im.save("/home/andrei/ttt.jpg", "JPEG"); #endif } void BlockAnalysis::countLinesInImg(QImage *_image) { mtreshold = 0; linesInfo = new quint16[_image->rect().height() + _image->rect().top()]; for (int i = 0; i < _image->rect().height() + _image->rect().top(); i++) linesInfo[i] = 0; lines = new QRgb * [_image->rect().height() + _image->rect().top()]; for (int i = _image->rect().top() + _image->rect().height() / 8; i < _image->rect().height() - _image->rect().height() / 8; i++) { QRgb *line = (QRgb *)(m_image->scanLine(i)); lines[i] = line; for (int j = _image->rect().left() + _image->rect().width() / 8; j < _image->rect().width() - _image->rect().width() / 8; j++) { mtreshold = mtreshold + qRed(line[j]) + qGreen(line[j]) + qBlue(line[j]); } } int div = (_image->rect().height() - _image->rect().top()) * (_image->rect().width() - _image->rect().left()); mtreshold = mtreshold / div; if (_image->isNull()) return; for (int i = _image->rect().top() + _image->rect().height() / 8; i < _image->rect().height() - _image->rect().height() / 8; i++) { for (int j = _image->rect().left() + _image->rect().width() / 8; j < _image->rect().width() - _image->rect().width() / 8; j++) { int k = qRed(lines[i][j]) + qGreen(lines[i][j]) + qBlue(lines[i][j]); if (k >= mtreshold - 1) { linesInfo[i]++; #ifdef DEBUG if (d != 0) m_image->setPixel(j, l, (1 << 32) + 1); #endif } else break; } } longestLine[2] = longestLine[1]; longestLine[1] = longestLine[0]; longestLine[0] = linesInfo[0]; for (int i = _image->rect().top(); i < _image->rect().height() ; i++) if (linesInfo[i] > longestLine[0]) longestLine[0] = linesInfo[i]; longestCount[2] = longestCount[1]; longestCount[1] = longestCount[0]; longestCount[0] = 0; for (int i = _image->rect().top(); i < _image->rect().height(); i++) { if (longestLine[0] - linesInfo[i] < 5) longestCount[0]++; } #ifdef DEBUG QImage im = m_image->copy(*m_coords); im.save("/home/andrei/ttt.jpg", "JPEG"); #endif delete lines; delete linesInfo; } int BlockAnalysis::getSkew2() { int ll[7], lc[7]; for (int i = 0; i < 7; i++) { QMatrix m; m.rotate((i - 3) * 2); QImage img = m_image->transformed(m, Qt::SmoothTransformation).convertToFormat(QImage::Format_RGB32); img = img.copy(img.rect()); countLinesInImg(&img); ll[i] = longestLine[0]; lc[i] = longestCount[0]; } int imax = 0; for (int i = 0; i < 7; i++) { if ((ll[i] > ll[imax]) && (lc[i] > lc[imax])) { imax = i; } } int result = (imax - 3) * 2; return result; } int BlockAnalysis::getSkew1() { createLinesInfo(); preScan1(); int ll, lc; int ll1, lc1, div1, dir1; int ll2, lc2, div2, dir2; if (m_coords->width() > 1024) m_coords->setWidth(1024); if (m_coords->height() > m_coords->width() / 2) m_coords->setHeight(m_coords->width() / 2); countLinesInImg(m_coords->width(), 0); ll = longestLine[0]; lc = longestCount[0]; int div = 2; int direction = 1; countLinesInImg(m_coords->width() / div, direction); int safe = 0; while ((longestLine[0] >= longestLine[1]) && (longestCount[0] >= longestCount[1]) && (safe < 512)) { div++; countLinesInImg(m_coords->width() / div, direction); safe++; } ll1 = longestLine[0]; lc1 = longestCount[0]; div1 = div; dir1 = direction; longestLine[0] = ll; longestCount[0] = lc; div = 2; direction = - direction; countLinesInImg(m_coords->width() / div, direction); safe = 0; while ((longestLine[0] >= longestLine[1]) && (longestCount[0] >= longestCount[1]) && (safe < 512)) { div++; countLinesInImg(m_coords->width() / div, direction); safe++; } ll2 = longestLine[0]; lc2 = longestCount[0]; div2 = div; dir2 = direction; int rdir; int rdiv; if ((ll1 > ll2) && (lc1 > lc2)) { rdir = dir1; rdiv = div1; } else { rdir = dir2; rdiv = div2; } float result = atan(((float)(rdiv - 1)) / ((float)m_coords->width()) * rdir) / M_2_PI * 360; QMatrix m; m.rotate(result); QImage *tmp = m_image; m_image = new QImage(tmp->transformed(m, Qt::SmoothTransformation).convertToFormat(QImage::Format_RGB32)); delete tmp; tmp = m_image; m_image = new QImage(tmp->scaled(QSize(tmp->width(), tmp->height()) * 1.1).convertToFormat(QImage::Format_RGB32)); delete tmp; *m_coords = m_image->rect(); delete linesInfo; delete lines; createLinesInfo(); preScan1(); countLinesInImg(m_coords->width(), 0); if ((ll > longestLine[0]) && (lc > longestCount[0])) { result = 0; } // if (((float)longestLine[0])/((float)m_coords->width()) < 0.8) // result = 0; delete linesInfo; delete lines; return result; } int BlockAnalysis::getSkew() { createLinesInfo(); preScan1(); int ll, lc; if (m_coords->width() > 1536) m_coords->setWidth(1536); if (m_coords->height() > m_coords->width() / 2) m_coords->setHeight(m_coords->width() / 2); countLinesInImg(m_coords->width(), 0); ll = longestLine[0]; lc = longestCount[0]; countLinesInImg(m_coords->width() / 4, -1); countLinesInImg(m_coords->width() / 4, 1); int maxRes = 0; if ((longestLine[1] >= longestLine[maxRes]) && (longestCount[1] > longestCount[maxRes])) maxRes = 1; if ((longestLine[2] >= longestLine[maxRes]) && (longestCount[2] > longestCount[maxRes])) maxRes = 2; int direction = 0; if (maxRes == 0) direction = 1; else if (maxRes == 1) direction = -1; else return 0; int div = 4; countLinesInImg(m_coords->width() / div, direction); int safe = 0; while ((longestLine[0] >= longestLine[1]) && (longestCount[0] >= longestCount[1]) && (safe < 512)) { div++; countLinesInImg(m_coords->width() / div, direction); direction = - direction; safe++; } float result = atan(((float)(div - 1)) / ((float)m_coords->width()) * (direction)) / M_2_PI * 360; QMatrix m; m.rotate(result); QImage *tmp = m_image; m_image = new QImage(tmp->transformed(m, Qt::SmoothTransformation).convertToFormat(QImage::Format_RGB32)); *m_coords = m_image->rect(); delete linesInfo; delete lines; delete tmp; createLinesInfo(); preScan1(); countLinesInImg(m_coords->width(), 0); if ((ll > longestLine[0]) && (lc > longestCount[0])) result = 0; if (((float)longestLine[0]) / ((float)m_coords->width()) < 0.8) result = 0; delete linesInfo; delete lines; return result; } void BlockAnalysis::preScan1() { mtreshold = 0; for (int i = m_coords->top(); i < m_coords->height(); i++) { QRgb *line = (QRgb *)(m_image->scanLine(i)); lines[i] = line; for (int j = m_coords->left(); j < m_coords->width(); j++) { mtreshold = mtreshold + qRed(line[j]) + qGreen(line[j]) + qBlue(line[j]); } } int div = (m_coords->height() - m_coords->top()) * (m_coords->width() - m_coords->left()); mtreshold = mtreshold / div; for (int i = m_coords->top(); i < m_coords->height(); i++) { //qint16 pixel1 = qRed(lines[i][m_coords->left()]) + qGreen(lines[i][m_coords->left()]) + qBlue(lines[i][m_coords->left()]); //qint16 pixel2 = qRed(lines[i][m_coords->left() + 1]) + qGreen(lines[i][m_coords->left() + 1]) + qBlue(lines[i][m_coords->left() + 1]); for (int j = m_coords->left() + 2; j < m_coords->width(); j++) { qint16 pixel3 = qRed(lines[i][j]) + qGreen(lines[i][j]) + qBlue(lines[i][j]); if (pixel3 >= mtreshold) { lines[i][j-1] = lines[i][j]; lines[i][j-2] = lines[i][j]; } //pixel1 = pixel2; //pixel2 = pixel3; } lines[i][m_coords->width()-1] = lines[i][m_coords->width()-2]; } for (int i = m_coords->height() - 1; i >= 0; i--) { if (longestLine[0] - linesInfo[i] > 2) { m_coords->setHeight(i + 1); break; } } for (int i = m_coords->top(); i < m_coords->height(); i++) { if (longestLine[0] - linesInfo[i] > 2) { m_coords->setTop(i); break; } } } void BlockAnalysis::createLinesInfo() { linesInfo = new quint16[m_coords->height() + m_coords->top()]; for (int i = 0; i < m_coords->height() + m_coords->top(); i++) linesInfo[i] = 0; lines = new QRgb * [m_coords->height() + m_coords->top()]; } yagf-0.9.3.2/src/CCAnalysis.h0000664000175000017500000000265402263216100016263 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CCANALYSIS_H #define CCANALYSIS_H #include #include #include "ycommon.h" class QImage; class RotationCropper { public: RotationCropper(QImage * image, QRgb color); ~RotationCropper(); QRect crop(); private: void recolor(); bool checkHorzLine(int y); bool checkVertLine(int x); private: QImage * image; QRgb whitePixel; QRgb replaceColor; int darksCount; int lightsCount; int minval; int maxval; //int clAltCount; int whitesCount; int whitetr; int whiteAlt; qreal clBrighttoWidth; qreal clBrighttoWidthtr; int clWhiteCount; int y1, y2, x1, x2; }; #endif yagf-0.9.3.2/src/CCAnalysis.cpp0000664000175000017500000001407502263216100016616 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "CCAnalysis.h" #include #include RotationCropper::RotationCropper(QImage * image, QRgb color) { this->image = image; darksCount = 0; lightsCount = 0; minval = 0; maxval = 0; //clAltCount = whitetr = 650; clBrighttoWidthtr = 0.1; //QRgb * line = (QRgb *) image->scanLine(0); whitePixel = color; //replaceColor = color; } RotationCropper::~RotationCropper() {} QRect RotationCropper::crop() { y1 = 0; int tolerance = 3; recolor(); for (int y = 0; y < image->height(); y ++) { if (checkHorzLine(y)) { tolerance--; if (tolerance == 0) { y1 = y; break; } } else tolerance = 3; } y2 = image->height()-1; tolerance = 3; for (int y = y2; y >= 0; y--) { if (checkHorzLine(y)) { tolerance--; if (tolerance == 0) { y2 = y; break; } } else tolerance = 3; } x1 = 0; tolerance = 3; for (int x = x1; x < image->width(); x++) { if (checkVertLine(x)) { tolerance--; if (tolerance == 0) { x1 = x; break; } } else tolerance = 3; } x2 = image->width()-1; tolerance = 3; for (int x = x2; x >= 0; x--) { if (checkVertLine(x)) { tolerance--; if (tolerance == 0) { x2 = x; break; } } else tolerance = 3; } return QRect(x1, y1, x2-x1, y2-y1); } void RotationCropper::recolor() { QColor repColor(1,2,3); for (int y = 0; y < image->height(); y++) { QRgb * line = (QRgb *) image->scanLine(y); for (int x = 0; x < image->width(); x++) { if (line[x] == whitePixel ) line[x] = repColor.rgb(); else break; } for (int x = image->width() - 1; x >= 0; x--) { if (line[x] == whitePixel ) line[x] = repColor.rgb(); else break; } } } bool RotationCropper::checkHorzLine(int y) { int skipCount = 0; darksCount = 0; lightsCount = 0; minval = 800; maxval = 0; whitesCount = 0; //clAltCount = 0; whiteAlt = 0; //int maxlstripe = 0; //int currentlstripe = 0; QRgb * line = (QRgb *) image->scanLine(y); for (int i = 0; i < image->width(); i++) { if ((qRed(line[i]) == 1) && (qGreen(line[i]) == 2) && (qBlue(line[i]) == 3)) { skipCount++; continue; } int pixel = qRed(line[i]) + qGreen(line[i]) + qBlue(line[i]); if (pixel <= 382) { darksCount++; // if (currentlstripe > maxlstripe) maxlstripe = currentlstripe; // currentlstripe = 0; } else { lightsCount++; // currentlstripe++; if (pixel >= whitetr) { whitesCount++; if (whiteAlt == 0) { whiteAlt = 1; clWhiteCount++; } } else whiteAlt = 0; } minval = pixel < minval ? pixel : minval; maxval = pixel > maxval ? pixel : maxval; } qreal d = image->width() - skipCount; if (d == 0) return false; clBrighttoWidth = (qreal)lightsCount/d; if (clBrighttoWidth <= clBrighttoWidthtr) return false; if (maxval - minval < 40) return false; //if (clAltCount < 10) // return false; //if (maxlstripe < 10) // return false; // if ((clWhiteCount > 0)&&(clWhiteCount < 3)) // return false; return true; } bool RotationCropper::checkVertLine(int x) { int skipCount = 0; darksCount = 0; lightsCount = 0; minval = 800; maxval = 0; whitesCount = 0; //clAltCount = 0; whiteAlt = 0; //int curline =0; for (int y = 0; y < image->height(); y++) { QRgb * line = (QRgb *) image->scanLine(y); if ((qRed(line[x]) == 1) && (qGreen(line[x]) == 2) && (qBlue(line[x]) == 3)) { skipCount++; continue; } int pixel = qRed(line[x]) + qGreen(line[x]) + qBlue(line[x]); if (pixel <= 382) { darksCount++; // if (curline) { // clAltCount++; // curline = 0; // } } else { lightsCount++; // if (!curline) { // clAltCount++; // curline = 1; // } if (pixel >= whitetr) { whitesCount++; if (whiteAlt == 0) { whiteAlt = 1; clWhiteCount++; } } else whiteAlt = 0; } minval = pixel < minval ? pixel : minval; maxval = pixel > maxval ? pixel : maxval; } qreal d = image->height() - skipCount; if (d == 0) return false; clBrighttoWidth = (qreal)lightsCount/d; if (clBrighttoWidth <= clBrighttoWidthtr) return false; if (maxval - minval < 40) return false; // if (clAltCount < 10) // return false; if ((clWhiteCount > 0)&&(clWhiteCount < 3)) return false; return true; } yagf-0.9.3.2/src/PageAnalysis.h0000664000175000017500000000305302263216100016644 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PAGEANALYSIS_H #define PAGEANALYSIS_H #include #include #include #include #include #include "ycommon.h" #include "analysis.h" class QPixmap; class QImage; class QRect; class BlockSplitter { public: void setImage(const QImage &image, qreal rotation, qreal scale); QRect getRootBlock(const QImage &image); Bars getBars(); // call after something calls blockAllText(); void splitBlocks(); QRect getRotationCropRect(const QImage &image); QList getBlocks(); private: QRect blockAllText(); void splitVertical(); void splitHorisontal(); bool isBlockRecogniseable(const Rect &block); private: QImage img; qreal m_rotate; qreal m_scale; Bars bars; Lines lines; QList blocks; }; #endif yagf-0.9.3.2/src/PageAnalysis.cpp0000664000175000017500000001622502263216100017204 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PageAnalysis.h" #include "CCAnalysis.h" #include "ccbuilder.h" #include "analysis.h" #include "utils.h" #include #include #include #include #include void BlockSplitter::setImage(const QImage &image, qreal rotation, qreal scale) { img = image; m_rotate = rotation; m_scale = scale; } QRect BlockSplitter::getRootBlock(const QImage &image) { if (image.isNull()) return QRect(0,0,0,0); QImage img1 = image; QRect result = blockAllText(); RotationCropper rc(&img1, QColor("white").rgb()); CCBuilder cb(img1); //img1.save("/home/anre/pictures/ttt.png"); QRect r = cb.crop();//rc.crop(); QRect r1(result.x() + r.x(), result.y() + r.y(), result.width(), result.height()); result = r1; foreach (Rect rc, bars) { bars.removeOne(rc); rc.x1 += r.x(); rc.x2 += r.x(); rc.y1 += r.y(); rc.y2 += r.y(); bars.append(rc); } return result; } Bars BlockSplitter::getBars() { return bars; } QRect BlockSplitter::blockAllText() { //img = img.transformed(QTransform().translate(-x, -y).rotate(m_rotate).translate(x, y), Qt::SmoothTransformation); if (!img.isNull()) { CCBuilder * cb = new CCBuilder(img); cb->labelCCs(); CCAnalysis * an = new CCAnalysis(cb); an->analize(); lines = an->getLines(); foreach(TextLine l, lines) if (l.count() < 3) lines.removeOne(l); int minX = 100000; int minY = 100000; int maxX = 0; int maxY = 0; for (int i =0; i < lines.count(); i++) { int x1 = lines.at(i).at(0).x; int y1 = lines.at(i).at(0).y; int x2 = lines.at(i).at(lines.at(i).count()-1).x; int y2 = lines.at(i).at(lines.at(i).count()-1).y; //graphicsInput->drawLine(x1,y1,x2,y2); if (x1 > x2) { int t = x2; x2 = x1; x1 = t; } minX = minX < x1 ? minX : x1; maxX = maxX > x2 ? maxX : x2; if (y1 > y2) { int t = y2; y2 = y1; y1 = t; } minY = minY < y1 ? minY : y1; maxY = maxY > y2 ? maxY : y2; } minX = minX - 2*an->getMediumGlyphWidth(); maxX =maxX + 2*an->getMediumGlyphWidth(); minY = minY - 2*an->getMediumGlyphHeight(); maxY = maxY + 2*an->getMediumGlyphHeight(); //graphicsInput->clearBlocks(); //graphicsInput->addBlock(QRectF(ox + minX*2*sideBar->getScale(), oy + minY*2*sideBar->getScale(), (maxX-minX)*2*sideBar->getScale(), (maxY-minY)*2*sideBar->getScale())); bars = an->addBars(); delete an; delete cb; minX = minX <= 0 ? 1 :minX; minY = minY <= 0 ? 1 :minY; return QRect(minX*m_scale, minY*m_scale, (maxX-minX)*m_scale, (maxY-minY)*m_scale); } return QRect(0, 0, 0, 0); } void BlockSplitter::splitVertical() { bool didSplit = true; while(didSplit) { didSplit = false; for (int i = blocks.count() - 1; i >=0; i--) { Rect block = blocks.at(i); foreach(Rect bar, bars) { if (abs(bar.x2 - bar.x1) > (bar.y2-bar.y1)) continue; int xmid = (bar.x1 + bar.x2)/2; if ((block.x1 < (xmid - 5)) &&(block.x2 > (xmid + 5))) { Rect block1 = block; block1.x2 = xmid -2; Rect block2 = block; block2.x1 = xmid + 2; blocks.removeAll(block); blocks.append(block1); blocks.append(block2); didSplit = true; break; } } } } } void BlockSplitter::splitBlocks() { QRect r = getRootBlock(img); Rect b; b.x1 = r.x(); b.y1 = r.y(); b.x2 = b.x1 + r.width(); b.y2 = b.y1 + r.height(); blocks.clear(); blocks.append(b); splitVertical(); splitHorisontal(); // qSort(blocks.begin(), blocks.end(), rectLessThan); for (int i = blocks.count() -1; i >=0; i--) { Rect r = blocks.at(i); if (!isBlockRecogniseable(r)) blocks.removeAll(r); } } QList BlockSplitter::getBlocks() { return blocks; } QRect BlockSplitter::getRotationCropRect(const QImage &image) { const QImage * img = ℑ QImage * img2 = const_cast(img); RotationCropper rc(img2, QColor("white").rgb()); return rc.crop(); } void BlockSplitter::splitHorisontal() { bool didSplit = true; while(didSplit) { didSplit = false; for (int i = blocks.count() - 1; i >=0; i--) { Rect block = blocks.at(i); foreach(Rect bar, bars) { if (abs(bar.y2 - bar.y1) > (bar.x2-bar.x1)) continue; int ymid = (bar.y1 + bar.y2)/2; if ((block.y1 < (ymid - 5)) &&(block.y2 > (ymid + 5))) if (_contains(bar.x1, bar.x2, block.x1)||_contains(bar.x1, bar.x2, block.x2)||_contains(block.x1, block.x2, bar.x1)||_contains(block.x1, block.x2, bar.x2)) { Rect block1 = block; block1.y2 = ymid -2; Rect block2 = block; block2.y1 = ymid + 2; blocks.removeAll(block); blocks.append(block1); blocks.append(block2); didSplit = true; break; } } } } } inline bool between(int a, int b, int c) { return (c > a) && (c < b); } bool BlockSplitter::isBlockRecogniseable(const Rect &block) { int contains = 0; int maxl = 0; foreach(TextLine l, lines) { if (!between(block.x1, block.x2, l.first().x)) continue; if (!between(block.x1, block.x2, l.last().x)) continue; if (!between(block.y1, block.y2, l.first().y)) continue; if (!between(block.y1, block.y2, l.last().y)) continue; contains++; if (l.count() > maxl) maxl = l.count(); } if ((maxl > 3)) return true; return false; } yagf-0.9.3.2/src/QBusyIndicator.cpp0000664000175000017500000000745702263216100017533 0ustar parallelsparallels/* QBusyIndicator by James Aleksans. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "QBusyIndicator.h" #include const int defaultInterval = 64; QBusyIndicator::QBusyIndicator(QWidget* parent) : QWidget(parent), m_showBackground(false), m_angle(0), m_timerId(-1), m_speed(1), m_displayedWhenStopped(false), m_color(Qt::black), m_backgroundColor(Qt::white) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setFocusPolicy(Qt::NoFocus); } bool QBusyIndicator::isAnimated () const { return (m_timerId != -1); } void QBusyIndicator::setDisplayedWhenStopped(bool state) { m_displayedWhenStopped = state; update(); } bool QBusyIndicator::isDisplayedWhenStopped() const { return m_displayedWhenStopped; } void QBusyIndicator::startAnimation() { m_angle = 0; if (m_timerId == -1) m_timerId = startTimer(m_speed*defaultInterval); } void QBusyIndicator::stopAnimation() { if (m_timerId != -1) killTimer(m_timerId); m_timerId = -1; update(); } void QBusyIndicator::setSpeed(double speed) { if (m_timerId != -1) killTimer(m_timerId); m_speed = speed; if (m_timerId != -1) m_timerId = startTimer(m_speed*defaultInterval); } void QBusyIndicator::setColor(const QColor & color) { m_color = color; update(); } void QBusyIndicator::setShowBackground(bool state) { m_showBackground = state; update(); } void QBusyIndicator::setBackgroundColor(const QColor &color) { m_backgroundColor = color; update(); } QSize QBusyIndicator::sizeHint() const { return QSize(20,20); } int QBusyIndicator::heightForWidth(int w) const { return w; } void QBusyIndicator::timerEvent(QTimerEvent * /*event*/) { m_angle = (m_angle+20)%360; update(); } void QBusyIndicator::paintEvent(QPaintEvent * /*event*/) { if (!m_displayedWhenStopped && !isAnimated()) return; int width = qMin(this->width(), this->height()); QPainter p(this); p.setRenderHint(QPainter::Antialiasing); int outerRadius = m_showBackground ? (width-8)*0.5 : (width-4)*0.5; int innerRadius = m_showBackground ? (width-8)*0.5*0.38 : (width-4)*0.5*0.38; int boundingRadius = outerRadius + 2; int capsuleHeight = outerRadius - innerRadius; int capsuleWidth = (width > 32 ) ? capsuleHeight *.23 : capsuleHeight *.35; int capsuleRadius = capsuleWidth/2; if (m_showBackground) { p.setBrush(QColor(m_backgroundColor)); p.setPen(QColor(m_backgroundColor)); p.drawEllipse(rect().center(), boundingRadius, boundingRadius); } for (int i=0; i<18; i++) { QColor color = m_color; color.setAlphaF(qMax(1.0 - (i/16.0), 0.)); p.setPen(Qt::NoPen); p.setBrush(color); p.save(); p.translate(rect().center()); p.rotate(m_angle - i*20.0f); p.drawRoundedRect(-capsuleWidth*0.5, -(innerRadius+capsuleHeight), capsuleWidth, capsuleHeight, capsuleRadius, capsuleRadius); p.restore(); } } yagf-0.9.3.2/src/SkewAnalysis.h0000664000175000017500000000236102263216100016702 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SKEWANALYSIS_H #define SKEWANALYSIS_H #include "ycommon.h" class QPixmap; class SkewAnalysis { public: SkewAnalysis(QPointList *pointList, int width, int height); ~SkewAnalysis(); signed int getSkew(); double getPhi(); QPixmap drawTriangle(QPixmap &pm); private: //int bin[360][2000]; double getRightPhi(); double getLeftPhi(); QPointList *pointList; int m_height; int m_width; double phi; QPoint * p1; QPoint * p2; }; #endif yagf-0.9.3.2/src/QBusyIndicator.h0000664000175000017500000000444102263216100017166 0ustar parallelsparallels/* QBusyIndicator by James Aleksans. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef QBUSYINDICATOR_H #define QBUSYINDICATOR_H #include #include class QBusyIndicator : public QWidget { Q_OBJECT Q_PROPERTY(bool showBackground READ getShowBackground WRITE setShowBackground) Q_PROPERTY(double speed READ getSpeed WRITE setSpeed) Q_PROPERTY(bool displayedWhenStopped READ isDisplayedWhenStopped WRITE setDisplayedWhenStopped) Q_PROPERTY(QColor color READ color WRITE setColor) Q_PROPERTY(QColor backgroundColor READ getBackgroundColor WRITE setBackgroundColor) public: QBusyIndicator(QWidget* parent = 0); bool getShowBackground() const { return m_showBackground; } double getSpeed() const { return m_speed; } bool isAnimated () const; bool isDisplayedWhenStopped() const; const QColor & color() const { return m_color; } const QColor & getBackgroundColor() const {return m_backgroundColor; } virtual QSize sizeHint() const; int heightForWidth(int w) const; public slots: void startAnimation(); void stopAnimation(); void setSpeed(double speed); void setDisplayedWhenStopped(bool state); void setColor(const QColor & color); void setShowBackground(bool state); void setBackgroundColor(const QColor & color); protected: virtual void timerEvent(QTimerEvent * event); virtual void paintEvent(QPaintEvent * event); private: bool m_showBackground; int m_angle; int m_timerId; double m_speed; bool m_displayedWhenStopped; QColor m_color; QColor m_backgroundColor; }; #endif // QBUSYINDICATOR_H yagf-0.9.3.2/src/analysis.h0000664000175000017500000000557002263216100016115 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ANALYSIS_H #define ANALYSIS_H #include "ccbuilder.h" #include //#include #include #include #include #include typedef struct _Rect { qint32 x1, x2, y1, y2; int dotCount; } Rect; bool operator==(Rect r1, Rect r2); typedef QHash ComponentParameters; typedef QList Strings; typedef QList StringsBoxes; typedef QMultiHash GlyphField; typedef struct ginfo { ginfo(int a1, int a2, int a3); int h; int x; int y; } GlyphInfo; bool operator==(GlyphInfo g1, GlyphInfo g2); typedef QList TextLine; typedef QMultiHash LineField; typedef QList Lines; typedef QList Bars; const int BarRatio = 24; class CCAnalysis : public QObject { public: CCAnalysis(CCBuilder * builder); ~CCAnalysis(); bool analize(bool extractBars = false); Bars addBars(); TextLine extractLine(); int getGlyphCount(); QList getGlyphs(); int getMediumGlyphHeight(); int getMediumGlyphWidth(); int getMediumLetterSpace(); int getMediumWordSpace(); int getStringsCount(); int getGlyphBoxCount(); Rect getGlyphBox(int index); QRect getStringBox(const int index) const; Lines getLines(); qreal getK(); void rotateLines(qreal phi, const QPoint &c = QPoint(0,0)); private: bool extractComponents(bool extractBars = false); void classifyGlyphs(); int findAdjacent(Rect &r); void normalizeLines(); void rotatePhi(qreal phi, const QPoint &c, QPoint &p); void addBarsHorizontal(int hoffset = 0, int height = -1, int woffset = 0, int width = -1); void addBarsHorisontalAfterVertical(); void addBarsVertical(); private: CCBuilder * builder; ComponentParameters components; Strings strings; StringsBoxes boxes; GlyphField glyphField; Lines lines; int glyphCount; int mediumGlyphHeight; int mediumGlyphWidth; int mediumLetterSpace; int mediumWordSpace; int stringsCount; qreal k; Bars bars; QVector verts; }; #endif yagf-0.9.3.2/src/analysis.cpp0000664000175000017500000003245702263216100016454 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "analysis.h" #include "utils.h" #include bool operator==(Rect r1, Rect r2) { if (r1.x1 != r2.x1) return false; if (r1.y1 != r2.y1) return false; if (r1.x2 != r2.x2) return false; if (r1.y2 != r2.y2) return false; return true; } bool operator==(GlyphInfo g1, GlyphInfo g2) { if (g1.x != g2.x) return false; if (g1.y != g2.y) return false; if (g1.h != g2.h) return false; return true; } CCAnalysis::CCAnalysis(CCBuilder * builder) { this->builder = builder; glyphCount = 0; mediumGlyphHeight = 0; mediumGlyphWidth = 0; mediumLetterSpace = 0; mediumWordSpace = 0; stringsCount = 0; } CCAnalysis::~CCAnalysis() { } bool CCAnalysis::analize(bool extractBars) { if (extractComponents(extractBars)) { classifyGlyphs(); normalizeLines(); return true; } return false; } Bars CCAnalysis::addBars() { extractComponents(true); addBarsHorizontal(); addBarsVertical(); addBarsHorisontalAfterVertical(); return bars; } bool CCAnalysis::extractComponents(bool extractBars) { components.clear(); for (int y = 0; y < builder->height(); y++) { for (int x = 0; x < builder->width(); x++) { quint32 label = builder->label(x,y); if (label) { Rect r; r.dotCount = 0; r.x1 = builder->width(); r.x2 = 0; r.y1 = builder->height(); r.y2 = 0; if (!components.contains(label)) components.insert(label, r); r = components.value(label); r.dotCount++; if (x < r.x1) r.x1 = x; if (x > r.x2) r.x2=x; if (y r.y2) r.y2 = y; components.remove(label); components.insert(label,r); } } } quint32 wacc, hacc, count; wacc = 0; hacc= 0; count = 0; foreach(Rect r, components.values()) { wacc += (r.x2 - r.x1); hacc +=(r.y2 - r.y1); count++; } quint32 wmed; quint32 hmed; if (count != 0) { wmed = wacc/count; hmed = hacc/count; } else return false; if (extractBars) { foreach(quint32 k, components.keys()) { Rect r = components.value(k); int deltaX = abs(r.x2 - r.x1); // TODO: remove abs() if not needed int deltaY = abs(r.y2 - r.y1); // TODO: remove abs() if not needed if ((deltaX > 10)&&(deltaY > 10)) continue; if (deltaX != 0) { if (deltaY/deltaX >= BarRatio) { components.remove(k); bars.append(r); continue; } } else { if (deltaY >= BarRatio) { components.remove(k); bars.append(r); continue; } } if (deltaY != 0) { if (deltaX/deltaY >= BarRatio) { components.remove(k); bars.append(r); continue; } } else { if (deltaX >= BarRatio) { components.remove(k); bars.append(r); continue; } } } } foreach(quint32 k, components.keys()) { Rect r = components.value(k); if ((r.x2 - r.x1) > 6*wmed) components.remove(k); else { int s = (r.y2 - r.y1)*(r.x2 - r.x1); if (( s < 30) || ((r.y2 - r.y1) > 4*hmed)) components.remove(k); else { if (s == 0) components.remove(k); else if (((double)r.dotCount/(double)s) < 0.1) components.remove(k); } } } wacc = 0; hacc= 0; count = 0; foreach(Rect r, components.values()) { wacc += (r.x2 - r.x1); hacc +=(r.y2 - r.y1); count++; } if (count == 0) return false; wmed = wacc/count; hmed = hacc/count; mediumGlyphWidth = wmed; mediumGlyphHeight = hmed; glyphCount = count; return true; } int CCAnalysis::getGlyphBoxCount() { return components.count(); } Rect CCAnalysis::getGlyphBox(int index) { return components.values().at(index); } void CCAnalysis::classifyGlyphs() { foreach (Rect r, components) { glyphField.insert(r.x1, r); } } TextLine CCAnalysis::extractLine() { Rect first; //QPoint firstpt; TextLine line; for (int x =0; x < builder->width(); x++) { if (glyphField.values(x).count()) { first = glyphField.values(x).at(0); glyphField.remove(x, first); line.append(ginfo((first.x1+first.x2)/2, (first.y1+first.y2)/2, abs(first.y1-first.y2))); break; } } if (line.count()) { Rect temp = first; while (findAdjacent(temp) >= 0) { line.append(ginfo((temp.x1+temp.x2)/2, (temp.y1+temp.y2)/2, abs(temp.y1-temp.y2))); } } return line; } int CCAnalysis::findAdjacent(Rect &r) { int startx = (r.x1+r.x2)/2; int xspan = (r.x2 - r.x1)*4; int ymid = (r.y1+r.y2)/2; int endx = startx + xspan; for (int x=startx; x = r1.y1) && (ymid <= r1.y2)) { r = r1; glyphField.remove(x, r1); return x; } } } return -1; } void CCAnalysis::normalizeLines() { k = 0; int count = 0; TextLine l = extractLine(); while (l.count()) { lines.append(l); if (l.count() > 4) { if (abs (l.last().h - l.first().h) < 3) { qreal d = l.last().x - l.first().x; if (d != 0) { k = k + ((qreal)(l.last().y - l.first().y))/d; count++; } } else { int lastX = l.at(l.count()-1).x; int lastY = l.at(l.count()-1).y; int lastH = l.at(l.count()-1).h; int firstX = l.at(1).x; int firstY = l.at(1).y; int firstH = l.at(1).h; if (abs (lastH - firstH) < 3) { qreal d = lastX - firstX; if (d != 0) { k = k + ((qreal)(lastY - firstY))/d; count++; } } } } //graphicsInput->drawLine(l.at(0).x()*2, l.at(0).y()*2, l.at(l.count()-1).x()*2,l.at(l.count()-1).y()*2); l = extractLine(); } if (count) k = k/count; } Lines CCAnalysis::getLines() { return lines; } qreal CCAnalysis::getK() { return k; } void CCAnalysis::rotatePhi(qreal phi, const QPoint &c, QPoint &p) { int x = p.x() - c.x(); int y = p.y() - c.y(); int x1 = x*cos(phi) - y*sin(phi); int y1 = x*sin(phi) + y*cos(phi); p.setX(x1+c.x()); p.setY(y1+c.y()); } void CCAnalysis::addBarsHorizontal(int hoffset, int height, int woffset, int width) { bool * li = new bool[builder->height()]; if (height < 0) height = builder->height(); if (width < 0) width = builder->width(); for (int i = hoffset; i < height; i++) li[i] = false; foreach (TextLine tl, lines) { if (tl.count() == 1) continue; foreach(GlyphInfo gi, tl) // OPTIMIZE if (_contains(woffset, width, gi.x)) { for (int i = gi.y - gi.h/2; i < gi.y + gi.h/2; i++) li[i] = true; } } int him = 0; int hlm = 0; int lcount = 0; int icount = 0; int chl = 0; int chi = 0; for (int i= hoffset; i < height; i++) { if (!li[i]) { // empty space if (chl > 0) { hlm += chl; lcount++; chl = 0; } chi++; } else { if (chi > 0) { if (icount > 0) him += chi; icount++; chi = 0; } chl++; } } if ((icount < 3)||(lcount == 0)) { delete[] li; return; } him -= chi; him = him/(icount-2); hlm /= lcount; int ilcount = 0; int llcount = 0; for (int i = hoffset; i < height; i++) { if (!li[i]) { ilcount++; } else { if (ilcount >= 5*him) { Rect r; r.x1 = woffset; r.x2 = width-1; r.y1 = 0; for (int j = i - 3*him; j < i; j++) { if ((!li[j-1]) && (!li[j]) && (!li[j+1])) { r.y1 = j; break; } } if (r.y1 > 0) { r.y2 = r.y1; bars.append(r); } } ilcount = 0; llcount++; } } delete[] li; } void CCAnalysis::addBarsHorisontalAfterVertical() { Rect r; r.y1 = 0; r.y2 = builder->height(); r.x1 = builder->width(); r.x2 = r.x1; verts.append(r); for (int i = 1; i < verts.count(); i++) { addBarsHorizontal(0, -1, verts[i-1].x2, verts[i].x1); } } /*void CCAnalysis::addBarsHorizontal() { bool * li = new bool[builder->height()]; for (int i = 0; i < builder->height(); i++) li[i] = false; int hlm = 0; foreach (TextLine tl, lines) hlm += tl.first().h; hlm /= lines.count(); foreach (TextLine tl, lines) { if (tl.count() < 3) if (tl.first().x > lines.first().first().x + 64) continue; for (int i = (tl.first().y - hlm < 0 ? 0 : tl.first().y - hlm); i < (tl.last().y + hlm > builder->height() ? builder->height() : tl.last().y + hlm); i++) li[i] = true; //for (int i = (tl.last().y - tl.last().h < 0 ? 0 : tl.last().y - tl.last().h); i < (tl.last().y + tl.last().h > builder->height() ? builder->height() : tl.last().y + tl.last().h); i++) // li[i] = true; } int fcount = 0; for (int i = 0; i < builder->height(); i++) { if (!li[i]) fcount++; else { if (fcount >= hlm*1.5) { Rect r; r.x1 = 0; r.x2 = builder->width()-1; r.y1 = i - hlm/2; r.y2 = r.y1; bars.append(r); fcount = 0; } } } delete[] li; }*/ void CCAnalysis::addBarsVertical() { int * li = new int[builder->width()]; for (int i = 0; i < builder->width(); i++) li[i] = 0; foreach(Rect c, components.values()) { for (int j = c.x1; j <= c.x2; j++) li[j]++; } int liprev = 1000; for (int i = 0; i < builder->width(); i++) { if (li[i] < 3) { if (liprev >= 3) { Rect r; r.x1 = i < builder->width() -5 ? i + 5 : i; r.x2 = r.x1; r.y1 = 0; r.y2 = builder->height()-1; bars.append(r); verts.append(r); } } else if (liprev < 3) { Rect r; r.x1 = i > 3 ? i - 3 : i-1; r.x2 = r.x1; r.y1 = 0; r.y2 = builder->height()-1; bars.append(r); verts.append(r); } liprev = li[i]; } delete[] li; } void CCAnalysis::rotateLines(qreal phi, const QPoint &c) { for (int i = 0; i < lines.count(); i++) { TextLine l = lines.at(i); for (int j =0; j < l.count(); j++) { QPoint p = QPoint(l.at(j).x, l.at(j).y); rotatePhi(phi, c, p); ginfo g = l.at(j); g.x = p.x(); g.y = p.y(); l.replace(j, g); } lines.replace(i, l); } } int CCAnalysis::getMediumGlyphWidth() { return mediumGlyphWidth; } int CCAnalysis::getMediumGlyphHeight() { return mediumGlyphHeight; } ginfo::ginfo(int a1, int a2, int a3) { x = a1; y = a2; h = a3; } int CCAnalysis::getGlyphCount() { return glyphCount; } QList CCAnalysis::getGlyphs() { return components.values(); } yagf-0.9.3.2/src/busyform.cpp0000664000175000017500000000210002263216100016455 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "busyform.h" #include "ui_busyform.h" BusyForm::BusyForm(QWidget *parent) : QSplashScreen(parent), ui(new Ui::BusyForm) { ui->setupUi(this); //setAttribute(Qt::WA_DeleteOnClose, true); } BusyForm::~BusyForm() { delete ui; } void BusyForm::textOut(const QString &text) { } yagf-0.9.3.2/src/busyform.ui0000664000175000017500000000464602263216100016331 0ustar parallelsparallels BusyForm 0 0 493 326 Form QFrame::StyledPanel QFrame::Raised Qt::Horizontal 194 20 50 50 50 50 Qt::Horizontal 193 20 background-color: darkgray; color: rgb(255, 255, 255); QSplashScreen QWidget

qsplashscreen.h
1 QBusyIndicator QWidget
QBusyIndicator.h
1
yagf-0.9.3.2/src/busyform.h0000664000175000017500000000214002263216100016126 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BUSYFORM_H #define BUSYFORM_H #include namespace Ui { class BusyForm; } class BusyForm : public QSplashScreen { Q_OBJECT public: explicit BusyForm(QWidget *parent = 0); ~BusyForm(); public slots: void textOut(const QString &text); private: Ui::BusyForm *ui; }; #endif // BUSYFORM_H yagf-0.9.3.2/src/ccbuilder.h0000664000175000017500000000471202263216100016223 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CCBUILDER_H #define CCBUILDER_H #include #include #include #include #include //#define DEBUG_CC typedef QList IntList; /* This class builds a 2-dimensional array for an image in which the connected pixels are labeled with the same numbers. The elements of the resulting array may be accesed using the label() method. */ class CCBuilder : public QObject { Q_OBJECT public: /* pixmap is the image to be analysed */ explicit CCBuilder(const QImage &img, QObject *parent = 0); ~CCBuilder(); /* This method resets CCs labels so that they occupy a continuos range of numbers */ void compactLabels(); /* This method returns the label value for original image's x and y. */ quint32 label(int x, int y); /* This method performs the actual connected component's labeling */ int labelCCs(); /* Returns image width */ int width(); /* Returns image height */ int height(); QRect crop(); signals: public slots: private: void initialScan(); void backwardScan(); void forwardScan(); int labelChecked(int x, int y); void setLabel(int x, int y, int newValue); void relabelLineLR(int y); void relabelLineRL(int y); inline bool isForeground(QRgb value); void scanFirstLineLR(); void scanLineLR(int y); void labelLeftmostPoint(int y); void labelRightmostPoint(int y); QImage image; quint32 * labels; bool * flags; bool skipNext; IntList recolor; quint32 maxlabel; bool didRecolor; int w, h; QRect cropRect; #ifdef DEBUG_CC int dcounter; int gcounter; #endif friend class Cropper; }; #endif // CCBUILDER_H yagf-0.9.3.2/src/configdialog.cpp0000664000175000017500000001016302263216100017244 0ustar parallelsparallels#include "configdialog.h" #include "ui_configdialog.h" #include "settings.h" #include "langselectdialog.h" #include #include #include ConfigDialog::ConfigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ConfigDialog) { ui->setupUi(this); ui->stackedWidget->setCurrentIndex(0); ui->listWidget->setCurrentRow(0); //ui->listWidget->setMinimumWidth(ui->listWidget->sizeHintForColumn(0)); for (int i = 0; i < ui->listWidget->count(); i++) ui->listWidget->item(i)->setToolTip(ui->listWidget->item(i)->text()); init(); } ConfigDialog::~ConfigDialog() { delete ui; } void ConfigDialog::accept() { Settings * settings = Settings::instance(); if (ui->radioButtonCuneiform->isChecked()) settings->setSelectedEngine(UseCuneiform); else settings->setSelectedEngine(UseTesseract); settings->setTessdataPath(ui->lineEditTessData->text()); if (ui->checkBoxSingleLang->isChecked()) { QStringList sl; sl << ui->comboBoxSingleLang->currentText(); settings->setSelectedLanguages(sl); settings->setLanguage(settings->getShortLanguageName(sl[0])); } settings->setCropLoaded(ui->checkBoxCrop->isChecked()); settings->setAutoDeskew(ui->checkBoxDeskew->isChecked()); settings->setPreprocessed(ui->checkBoxPreprocess->isChecked()); settings->setNoLocale(false); if (ui->comboBoxInterfaceLang->currentText() == "Russian") { settings->setRussianLocale(true); settings->setNoLocale(false); } else if (ui->comboBoxInterfaceLang->currentText() == "English") { settings->setRussianLocale(false); settings->setNoLocale(true); } else { settings->setRussianLocale(false); settings->setNoLocale(false); } if (ui->radioButtonNormIcons->isChecked()) settings->setIconSize(QSize(24,24)); else settings->setIconSize(QSize(32,32)); QDialog::accept(); } void ConfigDialog::init() { Settings * settings = Settings::instance(); ui->radioButtonCuneiform->setChecked(settings->getSelectedEngine() == UseCuneiform); ui->radioButtonTesseract->setChecked(settings->getSelectedEngine() == UseTesseract); ui->lineEditTessData->setText(settings->getTessdataPath()); QStringList sl = settings->getSelectedLanguages(); ui->checkBoxSingleLang->setChecked(sl.count() == 1); QStringList sl2 = settings->fullLanguageNames(); sl2.prepend(settings->getFullLanguageName(settings->getLanguage())); sl2.removeDuplicates(); ui->comboBoxSingleLang->clear(); ui->comboBoxSingleLang->addItems(sl2); ui->checkBoxCrop->setChecked(settings->getCropLoaded()); ui->checkBoxDeskew->setChecked(settings->getAutoDeskew()); ui->checkBoxPreprocess->setChecked(settings->getPreprocessed()); QStringList sl3; if (settings->useNoLocale()) sl3 << "English"; if (settings->useRussianLocale()) sl3 << "Russian"; sl3 << QLocale::languageToString(QLocale::system().language()) << "English" << "Russian"; sl3.removeDuplicates(); ui->comboBoxInterfaceLang->clear(); ui->comboBoxInterfaceLang->addItems(sl3); if (settings->getIconSize().width() > 24) { ui->radioButtonNormIcons->setChecked(false); ui->radioButtonLargeIcons->setChecked(true); } else { ui->radioButtonNormIcons->setChecked(true); ui->radioButtonLargeIcons->setChecked(false); } } void ConfigDialog::on_pushButtonTessData_clicked() { QString dir = QFileDialog::getExistingDirectory(this, trUtf8("Tesseract Data Directory"), "/"); if (dir != "") { if (dir.endsWith("tessdata/")) dir.truncate(dir.length()-9); if (dir.endsWith("tessdata")) dir.truncate(dir.length()-8); if (!dir.endsWith("/")) dir = dir + "/"; ui->lineEditTessData->setText(dir); } } void ConfigDialog::on_pushButtonLangs_clicked() { ui->checkBoxSingleLang->setChecked(false); LangSelectDialog lsd; lsd.exec(); } void ConfigDialog::itemClicked(QListWidgetItem *item) { ui->stackedWidget->setCurrentIndex(ui->listWidget->row(item)); } yagf-0.9.3.2/src/configdialog.h0000664000175000017500000000100602263216100016705 0ustar parallelsparallels#ifndef CONFIGDIALOG_H #define CONFIGDIALOG_H #include #include namespace Ui { class ConfigDialog; } class ConfigDialog : public QDialog { Q_OBJECT public: explicit ConfigDialog(QWidget *parent = 0); ~ConfigDialog(); void accept(); private slots: void on_pushButtonTessData_clicked(); void on_pushButtonLangs_clicked(); void itemClicked(QListWidgetItem * item); private: void init(); private: Ui::ConfigDialog *ui; }; #endif // CONFIGDIALOG_H yagf-0.9.3.2/src/ccbuilder.cpp0000664000175000017500000003660302263216100016562 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ccbuilder.h" #include "settings.h" #include #include #define XDEBUG #ifdef XDEBUG #include #endif class Cropper { public: Cropper(CCBuilder * builder) { this->builder = builder; darksCount = 0; lightsCount = 0; minval = 0; maxval = 0; //clAltCount = 0; whitetr = 650; clBrighttoWidthtr = 0.1; } ~Cropper() {} QRect crop() { y1 = 0; int tolerance = 3; for (int y = 0; y < builder->h; y ++) { if (checkHorzLine(y)) { tolerance--; if (tolerance == 0) { y1 = y; break; } } else tolerance = 3; } y2 = builder->h-1; tolerance = 3; for (int y = y2; y >= 0; y--) { if (checkHorzLine(y)) { tolerance--; if (tolerance == 0) { y2 = y; break; } } else tolerance = 3; } x1 = 0; tolerance = 3; for (int x = x1; x < builder->w; x++) { if (checkVertLine(x)) { tolerance--; if (tolerance == 0) { x1 = x; break; } } else tolerance = 3; } x2 = builder->w-1; tolerance = 3; for (int x = x2; x >= 0; x--) { if (checkVertLine(x)) { tolerance--; if (tolerance == 0) { x2 = x; break; } } else tolerance = 3; } return QRect(x1, y1, x2-x1, y2-y1); } bool checkHorzLine(int y) { quint32 clWhiteCount = 0; darksCount = 0; lightsCount = 0; minval = 800; maxval = 0; whitesCount = 0; //clAltCount = 0; whiteAlt = 0; //int maxlstripe = 0; //int currentlstripe = 0; QRgb * line = (QRgb *) builder->image.scanLine(y); //int curline =0; for (int i = 0; i < builder->w; i++) { int pixel = qRed(line[i]) + qGreen(line[i]) + qBlue(line[i]); if (pixel <= 382) { darksCount++; // if (currentlstripe > maxlstripe) maxlstripe = currentlstripe; // currentlstripe = 0; } else { lightsCount++; // currentlstripe++; if (pixel >= whitetr) { whitesCount++; if (whiteAlt == 0) { whiteAlt = 1; clWhiteCount++; } } else whiteAlt = 0; } minval = pixel < minval ? pixel : minval; maxval = pixel > maxval ? pixel : maxval; } clBrighttoWidth = (qreal)lightsCount/builder->w; if (clBrighttoWidth <= clBrighttoWidthtr) return false; if (maxval - minval < 40) return false; //if (clAltCount < 10) // return false; //if (maxlstripe < 10) // return false; // if ((clWhiteCount > 0)&&(clWhiteCount < 3)) // return false; return true; } bool checkVertLine(int x) { quint32 clWhiteCount = 0; darksCount = 0; lightsCount = 0; minval = 800; maxval = 0; whitesCount = 0; //clAltCount = 0; whiteAlt = 0; //int curline =0; for (int y = 0; y < builder->h; y++) { QRgb * line = (QRgb *) builder->image.scanLine(y); int pixel = qRed(line[x]) + qGreen(line[x]) + qBlue(line[x]); if (pixel <= 382) { darksCount++; // if (curline) { // clAltCount++; // curline = 0; // } } else { lightsCount++; // if (!curline) { // clAltCount++; // curline = 1; // } if (pixel >= whitetr) { whitesCount++; if (whiteAlt == 0) { whiteAlt = 1; clWhiteCount++; } } else whiteAlt = 0; } minval = pixel < minval ? pixel : minval; maxval = pixel > maxval ? pixel : maxval; } clBrighttoWidth = (qreal)lightsCount/builder->h; if (clBrighttoWidth <= clBrighttoWidthtr) return false; if (maxval - minval < 40) return false; // if (clAltCount < 10) // return false; if ((clWhiteCount > 0)&&(clWhiteCount < 3)) return false; return true; } private: CCBuilder * builder; int darksCount; int lightsCount; int minval; int maxval; //int clAltCount; int whitesCount; int whitetr; int whiteAlt; qreal clBrighttoWidth; qreal clBrighttoWidthtr; int y1, y2, x1, x2; }; CCBuilder::CCBuilder(const QImage &img, QObject *parent) : QObject(parent) { if (img.isNull()) { w = h = 0; return; } image = img; image.convertToFormat(QImage::Format_RGB32); labels = NULL; //new quint32 [image.height()*image.width()]; flags = NULL; //new bool[image.height()]; //memset(flags, 0, sizeof(bool)*image.height()); skipNext = false; maxlabel = 1; w = image.width(); h = image.height(); } CCBuilder::~CCBuilder() { delete [] labels; delete [] flags; } int CCBuilder::width() { return w; } int CCBuilder::height() { return h; } void CCBuilder::compactLabels() { QList l; l.append(0); for( int i = 0; i < w; i++) for( int j = 0; j < h; j++) if (!l.contains(label(i,j))) l.append(label(i,j)); for( int i = 0; i < w; i++) for( int j = 0; j < h; j++) setLabel(i, j, l.indexOf(label(i,j))); } quint32 CCBuilder::label(int x, int y) { int index = y*w+x; if (index < w*h) return labels[index]; return 0; } int CCBuilder::labelChecked(int x, int y) { if ((x<0)||(y<0)) return 0; if ((x>=w)||(y>=h)) return 0; return labels[y*w+x]; } void CCBuilder::setLabel(int x, int y, int newValue) { recolor.replace(label(x, y), newValue); labels[y*w+x] = newValue; didRecolor = true; } void CCBuilder::relabelLineLR(int y) { for (int x = 0; x < w; x++) { if (labelChecked(x, y)) { int oc = labelChecked(x, y); if (recolor.at(labelChecked(x-1, y))) { int nc = recolor.at(labelChecked(x-1, y)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } if (recolor.at(labelChecked(x+1, y-1))) { int nc = recolor.at(labelChecked(x+1, y-1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } } else { if (recolor.at(labelChecked(x, y-1))) { int nc = recolor.at(labelChecked(x, y-1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } if (recolor.at(labelChecked(x-1, y-1))) { int nc = recolor.at(labelChecked(x-1, y-1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } if (recolor.at(labelChecked(x+1, y-1))) { int nc = recolor.at(labelChecked(x+1, y-1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } } } } } void CCBuilder::relabelLineRL(int y) { for (int x = w-1; x >= 0; x--) { if (labelChecked(x, y)) { int oc = labelChecked(x, y); if (recolor.at(labelChecked(x+1, y))) { int nc = recolor.at(labelChecked(x+1, y)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } if (recolor.at(labelChecked(x-1, y+1))) { int nc = recolor.at(labelChecked(x-1, y+1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } } else { if (recolor.at(labelChecked(x, y+1))) { int nc = recolor.at(labelChecked(x, y+1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } if (recolor.at(labelChecked(x+1, y+1))) { int nc = recolor.at(labelChecked(x+1, y+1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } if (recolor.at(labelChecked(x-1, y+1))) { int nc = recolor.at(labelChecked(x-1, y+1)); if (nc < oc) { setLabel(x, y, nc); oc = nc; } } } } } } bool CCBuilder::isForeground(QRgb value) { int b = qRed(value) + qGreen(value) + qBlue(value); if (b > 382) return false; return true; } void CCBuilder::initialScan() { recolor.clear(); recolor.append(0); scanFirstLineLR(); for (int j = 1; j < h; j++) { labelLeftmostPoint(j); scanLineLR(j); labelRightmostPoint(j); } } void CCBuilder::backwardScan() { bool rdid = false; didRecolor = false; relabelLineLR(h-1); rdid = rdid|didRecolor; skipNext = flags[h-1]; flags[h-1] = !didRecolor; skipNext = skipNext&flags[h-1]; for (int j = h-2; j >= 0; j--) { didRecolor = false; skipNext = skipNext&flags[j]; if (!skipNext) { relabelLineRL(j); rdid = rdid|didRecolor; } #ifdef DEBUG_CC else dcounter++; gcounter++; #endif skipNext = flags[j]; flags[j] = !didRecolor; skipNext = skipNext&flags[j]; } didRecolor = rdid; } void CCBuilder::forwardScan() { bool rdid = false; didRecolor = false; relabelLineLR(0); rdid = rdid|didRecolor; skipNext = flags[0]; flags[0] = !didRecolor; skipNext = skipNext&flags[0]; for (int j = 1; j < h; j++) { didRecolor = false; skipNext = skipNext&flags[j]; if (!skipNext) { relabelLineLR(j); rdid = rdid|didRecolor; } #ifdef DEBUG_CC else dcounter++; gcounter++; #endif skipNext = flags[j]; flags[j] = !didRecolor; skipNext = skipNext&flags[j]; } didRecolor = rdid; } int CCBuilder::labelCCs() { delete labels; delete flags; labels = 0; flags = 0; if (w*h == 0) return 0; labels = new quint32 [image.height()*image.width()]; flags = new bool[image.height()]; memset(flags, 0, sizeof(bool)*image.height()); #ifdef DEBUG_CC dcounter = 0; gcounter = 0; #endif int count = 0; initialScan(); didRecolor = true; int turn = 1; while (didRecolor) { didRecolor = false; if (turn) backwardScan(); else forwardScan(); turn = 1 - turn; count++; } #ifdef DEBUG_CC qDebug() << dcounter << gcounter; #endif return count; } void CCBuilder::scanFirstLineLR() { if (!(h*w)) return; QRgb * line = (QRgb *) image.scanLine(0); if (isForeground(line[0])) { labels[0] = maxlabel; recolor.append(maxlabel); maxlabel++; } else labels[0] = 0; for (int i = 1; i < w; i++) { if (isForeground(line[i])) { if (labels[i-1]) labels[i] = labels[i-1]; else { labels[i] = maxlabel; recolor.append(maxlabel); maxlabel++; } } else labels[i] = 0; } } void CCBuilder::labelLeftmostPoint(int y) { QRgb * line = (QRgb *) image.scanLine(y); if (isForeground(line[0])) { int prlabel = labels[w*(y-1)]; if (prlabel) labels[w*y] = prlabel; else { if ((w > 1) && (labels[w*(y-1) + 1] > 0)) { labels[w*y] = labels[w*(y-1) + 1]; } else { labels[w*y] = maxlabel; recolor.append(maxlabel); maxlabel++; } } } else labels[w*y] = 0; } void CCBuilder::labelRightmostPoint(int y) { if (w < 2) return; QRgb * line = (QRgb *) image.scanLine(y); if (isForeground(line[w-1])) { if (labels[w*y-1]) labels[w*(y+1) - 1] = labels[w*y-1]; else { if (labels[w*y-2]) { labels[w*(y+1) - 1] = labels[w*y-2]; } else if (labels[w*(y+1)-2]) { labels[w*(y+1) - 1] = labels[w*(y+1)-2]; } else { labels[w*(y+1) - 1] = maxlabel; recolor.append(maxlabel); maxlabel++; } } } else labels[w*(y+1) - 1] = 0; } void CCBuilder::scanLineLR(int y) { QRgb * line = (QRgb *) image.scanLine(y); for (int i = 1; i < w-1; i++) { labels[w*y + i] = 0; int nc; int oc; oc = maxlabel; if (isForeground(line[i])) { nc = labels[w*(y - 1) + i]; if (nc) { labels[w*y + i] = nc; oc = nc; } nc = labels[w*(y - 1) + i - 1]; if ((nc) && (nc <= oc)) { labels[w*y + i] = nc; oc = nc; } nc = labels[w*(y - 1) + i + 1]; if ((nc) && (nc <= oc)) { labels[w*y + i] = nc; oc = nc; } nc = labels[w*y + i - 1]; if ((nc) && (nc <= oc)) { labels[w*y + i] = nc; oc = nc; } if (!labels[w*y + i]) { labels[w*y + i] = maxlabel; recolor.append(maxlabel); maxlabel++; } } } } QRect CCBuilder::crop() { Cropper cropper(this); return cropper.crop(); } yagf-0.9.3.2/src/droplabel.cpp0000664000175000017500000000354002263216100016564 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "droplabel.h" #include #include #include #include #include #include #include #include DropLabel::DropLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f) { setAcceptDrops(true); } void DropLabel::dragEnterEvent(QDragEnterEvent *event) { //event->setDropAction(Qt::IgnoreAction); const QMimeData * md = event->mimeData(); QStringList sl = md->formats(); if (!md->formats().contains("text/uri-list")) { setCursor(Qt::ForbiddenCursor); } else { event->setDropAction(Qt::MoveAction); event->accept(); //QCursor cur(lw->selectedItems().at(0)->icon().pixmap(96, 128)); //setCursor(cur); } } void DropLabel::dragLeaveEvent(QDragLeaveEvent *event) { setCursor(Qt::ArrowCursor); event->accept(); } void DropLabel::dropEvent(QDropEvent *event) { if (event->dropAction() == Qt::MoveAction) event->ignore(); else event->accept(); } void DropLabel::setListWidget(QListWidget *w) { lw = 0; } yagf-0.9.3.2/src/configdialog.ui0000664000175000017500000003430602263216100017104 0ustar parallelsparallels ConfigDialog 0 0 686 369 YAGF Settings 1.000000000000000 0 0 210 16777215 11 false QAbstractItemView::DragDrop 48 48 Qt::ElideNone QListView::Snap QListView::TopToBottom true QListView::Fixed QListView::SinglePass 5 QListView::IconMode true 190 false OCR and Languages 11 AlignHCenter|AlignVCenter|AlignCenter :/images/recognition.png:/images/recognition.png ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled Image Processing AlignHCenter|AlignVCenter|AlignCenter :/images/eISet.png:/images/eISet.png ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled Program Appearance AlignHCenter|AlignVCenter|AlignCenter :/images/appearence.png:/images/appearence.png ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled 1 0 0 OCR Engine Cuneiform true Tesseract Path to Tesseract Data Files false 30 16777215 ... Recognize Languages Use Single Language: true 190 16777215 Choose Among Several Languages 0 0 190 16777215 Languages to Use... Qt::Vertical 20 35 Enhance Image Quality Crop Image When Loaded true Deskew Loaded Images true Preprocess Images When Loaded Qt::Vertical 20 129 Interface Language Select Language of the Interface: Toolbar Icon Size Normal true Large Qt::Vertical 20 93 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ConfigDialog accept() 248 254 157 274 buttonBox rejected() ConfigDialog reject() 316 260 286 274 listWidget itemClicked(QListWidgetItem*) ConfigDialog itemClicked(QListWidgetItem*) 109 167 401 184 itemClicked(QListWidgetItem*) yagf-0.9.3.2/src/SkewAnalysis.cpp0000664000175000017500000001432202263216100017235 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "SkewAnalysis.h" #include #include #include #include #include #include #include SkewAnalysis::SkewAnalysis(QPointList *pointList, int width, int height) { /* for (int i = 0; i < 360; i++) for (int j = 0; j < 2000; j++) bin[i][j] = 0; for (int i = 0; i < 360; i++) { for (int j = 0; j < pointList->count(); j+=2) { long int r = pointList->at(j).x()*cos(i/360*M_2_PI) + pointList->at(j).y()*cos(i/360*M_2_PI); r = r/4; bin[i][r]++; } } long int sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0; int phi1 = 0, phi2 = 0, phi3 = 0, phi4 = 0; long int tmpsum = 0; //for (int i = 0; i < 360; i++) // for (int j = 0; j < 2000; j++) // if (bin[i][j] < 9) bin[i][j] = 0; for (int i = 0; i < 360; i++) { for (int j = 0; j < 2000; j++) // tmpsum += bin[i][j]; if (tmpsum < bin[i][j]) tmpsum = bin[i][j]; if ((tmpsum > sum4) || (tmpsum > sum3) || (tmpsum > sum2 || (tmpsum > sum1))) { if (sum1 < sum2) {sum1 = sum2; phi1 = phi2;} if (sum2 < sum3) {sum2 = sum3; phi2 = phi3;} if (sum3 < sum4) {sum3 = sum4; phi3 = phi4;} sum4 = tmpsum; phi4 = i; tmpsum =0; } } FILE * f = fopen("/home/andrei/hough.txt", "w"); for (int i = 0; i < 360; i++) { for (int j = 0; j < 1000; j++) fprintf(f, "%i:, %i, ", i, bin[i][j]); fprintf(f, "\n"); } fclose(f);*/ this->pointList = pointList; m_width = width; m_height = height; p1 = new QPoint(); p2 = new QPoint(); } SkewAnalysis::~SkewAnalysis() { delete p1; delete p2; } signed int SkewAnalysis::getSkew() { int minLeftDist = 10000; int minRightDist = 0; for (int i = 0; i < pointList->count(); i++) { if (!(i%2)) { if (minLeftDist > pointList->at(i).x()) minLeftDist = pointList->at(i).x(); } else { if (minRightDist < pointList->at(i).x()) minRightDist = pointList->at(i).x(); } } minRightDist = m_width - minRightDist; signed int res; if (minRightDist > minLeftDist) { phi = getRightPhi(); } else { phi = getLeftPhi(); } res = (phi/(2*M_PI))*360+1; if (res > 45) return 90 - res; if (res < 45) return -(90 + res); return -res; } double SkewAnalysis::getPhi() { return getSkew()*2*M_PI/(double)360; //if (phi > 0.7854) return M_PI_2 - phi; //if (phi < 0.7854) return -(M_PI_2 + phi); } double SkewAnalysis::getRightPhi() { double phi = 0; int maxx = 0, maxy = 0, minx = 10000, miny = 10000; for (int i = 1; i < pointList->count(); i +=2) { if(maxx< pointList->at(i).x()) { maxx = pointList->at(i).x(); maxy = pointList->at(i).y(); } if(miny > pointList->at(i).y()) { minx = pointList->at(i).x(); miny = pointList->at(i).y(); } } phi = 0; if (maxy-miny < m_height - maxy) { miny = 0; for (int i = 1; i < pointList->count(); i +=2) if(miny < pointList->at(i).y()) { minx = pointList->at(i).x(); miny = pointList->at(i).y(); } } else { miny = 10000; for (int i = 1; i < pointList->count(); i +=2) if(miny > pointList->at(i).y()) { minx = pointList->at(i).x(); miny = pointList->at(i).y(); } } if (maxx-minx == 0) return 0; if (m_height/abs(maxx-minx) >=100 ) return 0; // if (maxy > miny) p1->setX(maxx); p1->setY(maxy); p2->setX(minx); p2->setY(miny); phi = atan((double)(maxy-miny)/(double)(maxx-minx)); //else //phi = atan((double)(miny - maxy)/(double)(maxx-minx)); // phi = - (M_PI_2 -phi); return phi; } double SkewAnalysis::getLeftPhi() { double phi = 0; int maxx = 0, maxy = 0, minx = 10000, miny = 10000; for (int i = 0; i < pointList->count(); i +=2) { if(minx > pointList->at(i).x()) { minx = pointList->at(i).x(); miny = pointList->at(i).y(); } if(maxy > pointList->at(i).y()) { maxx = pointList->at(i).x(); maxy = pointList->at(i).y(); } } phi = 0; if (miny < m_height - miny) { maxy = 0; for (int i = 0; i < pointList->count(); i +=2) if((maxy < pointList->at(i).y()) && (pointList->at(i).y() < 0.9*m_height)) { maxx = pointList->at(i).x(); maxy = pointList->at(i).y(); } } else { maxy = 10000; for (int i = 0; i < pointList->count(); i +=2) if((maxy > pointList->at(i).y()) && (pointList->at(i).y() < 0.9*m_height)) { maxx = pointList->at(i).x(); maxy = pointList->at(i).y(); } } p1->setX(maxx); p1->setY(maxy); p2->setX(minx); p2->setY(miny); if (maxx-minx == 0) return 0; if (m_height/abs(maxx-minx) >=100 ) return 0; phi = atan((double)(maxy-miny)/(double)(maxx-minx)); return phi; } QPixmap SkewAnalysis::drawTriangle(QPixmap &pm) { QImage img = pm.toImage(); QPainter p(&img); QPen pen; pen.setColor(QColor(255, 0, 0)); p.setPen(pen); p.drawLine(*p1, *p2); // p. return QPixmap::fromImage(img); } yagf-0.9.3.2/src/droplabel.h0000664000175000017500000000236002263216100016230 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DROPLABEL_H #define DROPLABEL_H #include class QListWidget; class DropLabel : public QLabel { Q_OBJECT public: DropLabel(QWidget * parent = 0, Qt::WindowFlags f = 0); void setListWidget(QListWidget * w); signals: void pageRemoved(int id); protected: void dragEnterEvent(QDragEnterEvent *event); void dragLeaveEvent(QDragLeaveEvent *event); void dropEvent(QDropEvent *event); private: QListWidget * lw; }; #endif // DROPLABEL_H yagf-0.9.3.2/src/ghostscr.cpp0000664000175000017500000000305102263216100016451 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ghostscr.h" #include #include GhostScr::GhostScr() : PDFExtractor() { } void GhostScr::exec() { QString command = "gs"; QStringList args; args << "-SDEVICE=jpeg" << "-r1200x1200" << "-sPAPERSIZE=letter" << "-dNOPAUSE" << "-dBATCH"; if ((!getStopPage().isEmpty()) || (getStopPage().toInt() > 0)) { if (getStartPage().toInt() == 0) this->setStartPage("1"); args << QString("-dFirstPage=").append(getStartPage()) << QString("-dLastPage=").append(getStopPage()); } setOutputPrefix(getOutputDir().append("page")); args << QString("-sOutputFile=").append(getOutputPrefix()).append("_%04d.jpg"); args << "--" << this->getSourcePDF(); setOutputExtension("jpg"); execInternal(command, args); } yagf-0.9.3.2/src/ghostscr.h0000664000175000017500000000170402263216100016121 0ustar parallelsparallels /* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef GHOSTSCR_H #define GHOSTSCR_H #include "pdfextractor.h" class GhostScr : public PDFExtractor { public: GhostScr(); virtual void exec(); }; #endif // PDF2PPT_H yagf-0.9.3.2/src/langselectdialog.h0000664000175000017500000000246402263216100017572 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef LANGSELECTDIALOG_H #define LANGSELECTDIALOG_H #include #include #include #include namespace Ui { class LangSelectDialog; } class LangSelectDialog : public QDialog { Q_OBJECT public: explicit LangSelectDialog(QWidget *parent = 0); ~LangSelectDialog(); protected: void accept(); private: QStringList getRecognitionLanguages() const; void fillLangs(); private: Ui::LangSelectDialog *ui; QList items; }; #endif // LANGSELECTDIALOG_H yagf-0.9.3.2/src/langselectdialog.cpp0000664000175000017500000000614202263216100020122 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "langselectdialog.h" #include "ui_langselectdialog.h" #include "settings.h" #include #include #include const int max_lang = 48; LangSelectDialog::LangSelectDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LangSelectDialog) { ui->setupUi(this); fillLangs(); } LangSelectDialog::~LangSelectDialog() { delete ui; } QStringList LangSelectDialog::getRecognitionLanguages() const { QStringList res; foreach(QListWidgetItem * item, items) { if (item->checkState() == Qt::Checked) res.append(item->text()); } res.removeDuplicates(); return res; } void LangSelectDialog::accept() { QStringList sl = getRecognitionLanguages(); if (sl.count() == 0) return; Settings::instance()->setSelectedLanguages(sl); QDialog::accept(); } void LangSelectDialog::fillLangs() { QStringList sl = Settings::instance()->languagesAvailableTo("cuneiform"); sl.sort(); foreach (QString s, sl) { QListWidgetItem * item = new QListWidgetItem(ui->listWidgetCuneiform); item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); item->setText(s); items.append(item); item->setCheckState(Qt::Unchecked); if (Settings::instance()->selectedLanguagesAvailableTo("cuneiform").contains(s)) item->setCheckState(Qt::Checked); } sl.clear(); ui->listWidgetTesseract->setIconSize(QSize(18, 18)); sl = Settings::instance()->languagesAvailableTo("tesseract"); foreach (QString s, sl) { QListWidgetItem * item = new QListWidgetItem(ui->listWidgetTesseract); item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); item->setText(s); items.append(item); item->setCheckState(Qt::Unchecked); if (Settings::instance()->selectedLanguagesAvailableTo("tesseract").contains(s)) item->setCheckState(Qt::Checked); if (Settings::instance()->installedTesseractLanguages().contains(s)) { item->setIcon(QIcon(QPixmap(":/images/box.png"))); item->setToolTip(trUtf8("Installed")); } else { item->setIcon(QIcon(QPixmap(":/images/notinst.png"))); item->setToolTip(trUtf8("Not installed")); } } } yagf-0.9.3.2/src/langselectdialog.ui0000664000175000017500000001137202263216100017756 0ustar parallelsparallels LangSelectDialog 0 0 665 556 YAGF Languages Setup 1.000000000000000 800 24 12 75 true Select the recognition languages Qt::AlignCenter 16 Qt::Horizontal 40 20 Cuneiform Qt::AlignCenter 256 0 0 0 Tesseract Qt::AlignCenter 256 0 Qt::Horizontal 40 20 Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() LangSelectDialog accept() 248 254 157 274 buttonBox rejected() LangSelectDialog reject() 316 260 286 274 yagf-0.9.3.2/src/main.cpp0000664000175000017500000000565302263216100015553 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "mainform.h" #include "settings.h" #include "langselectdialog.h" //#define MEM_DEBUG #ifdef MEM_DEBUG #include #endif void parseCmdLine(const QStringList &args) { foreach (const QString &arg, args){ if (arg == "-h" || arg == "--help") { printf("Using:\n" " yagf\n" " yagf \n" " yagf [file name [file name]...]\n" "YAGF is a graphical interface for cuneiform and tesseract.\n" "\n" "Keys:\n" " -h, --help\t Show this message and exit\n" " -V, --version\t Show version string and exit\n"); exit(0); } else if (arg == "-V" || arg == "--version") { printf("YAGF version: %s\n", version.toUtf8().constData()); exit(0); } } } int main(int argc, char *argv[]) { #ifdef MEM_DEBUG mtrace(); #endif QApplication app(argc, argv); parseCmdLine(app.arguments()); Settings * settings = Settings::instance(); settings->readSettings(settings->workingDir()); settings->writeSettings(); QTranslator translator; QString qmName = "yagf_" + QLocale::system().name(); if (settings->useRussianLocale()) qmName = "yagf_ru"; if (!settings->useNoLocale()) { translator.load(qmName, QString(QML_INSTALL_PATH)); app.installTranslator(&translator); } settings->makeLanguageMaps(); QTranslator translator2; if (settings->useRussianLocale()) translator2.load("qt_ru_RU", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); else translator2.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); if (!settings->useNoLocale()) app.installTranslator(&translator2); if (settings->getSelectedLanguages().count() == 0) { //LangSelectDialog lsd; //lsd.exec(); } MainForm window; window.show(); int res = app.exec(); #ifdef MEM_DEBUG muntrace(); #endif return res; } yagf-0.9.3.2/src/mainform.h0000664000175000017500000001106202263216100016073 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "settings.h" #include #include #include "ui_mainform.h" #include #include //#include "ui_popplerdialog.h" class QComboBox; class QCheckBox; class QLabel; class QCloseEvent; class QPixmap; class QProcess; class QByteArray; class QFile; class SpellChecker; class QCursor; class QGraphicsInput; class QMenu; class PDFExtractor; class ccbuilder; class QLabel; class QAction; const QString version = "0.9.3.2"; class PageCollection; class ScannerBase; class MainForm : public QMainWindow, public Ui::MainWindow { Q_OBJECT public: MainForm(QWidget *parent = 0); ~MainForm(); signals: void windowShown(); private slots: //void on_actionRecognize_activated(); void on_actionSelect_HTML_format_activated(); void on_actionDeskew_activated(); //void on_alignButton_clicked(); void on_actionCheck_spelling_activated(); void on_actionSave_block_activated(); void on_actionSave_current_image_activated(); void on_actionCheck_spelling_triggered(); void on_actionRecognize_block_activated(); void on_ActionDeleteBlock_activated(); void on_ActionClearAllBlocks_activated(); void loadImage(); void rotateCWButtonClicked(); void rotateCCWButtonClicked(); void rotate180ButtonClicked(); void enlargeButtonClicked(); void decreaseButtonClicked(); void singleColumnButtonClicked(); void newLanguageSelected(int index); void scanImage(); void loadNextPage(); void loadPreviousPage(); void recognize(); void recognizeAll(); void showAboutDlg(); void showHelp(); void unalignButtonClicked(); void hideToolBar(); void importPDF(); void showConfigDlg(); void addPDFPage(QString pageName); void finishedPDF(); void pasteimage(); void deskewByBlock(); void selectTextArea(); void selectBlocks(); void selectHTMLformat(); void loadFiles(const QStringList &files); void LangTextChanged(const QString &text); void SelectRecognitionLanguages(); void cancelPDF(); void selectLanguages(); private: virtual void closeEvent(QCloseEvent *event); void initSettings(); void loadFile(const QString &fn, bool loadIntoView = true); void loadTIFF(const QString &fn, bool loadIntoView = true); //void loadFileWithPixmap(const QString &fn, const QPixmap &pixmap); void delTmpFiles(); void delTmpDir(); void preparePageForRecognition(); void prepareBlockForRecognition(const QRect &r); void prepareBlockForRecognition(int index); void recognizeInternal(); bool useCuneiform(const QString &inputFile, const QString &outputFile); bool useTesseract(const QString &inputFile); QString getFileNameToSaveImage(QString &format); void loadFromCommandLine(); void clearTmpFiles(); void fillLangBox(); private: QComboBox *selectLangsBox; QGraphicsInput *graphicsInput; QString fileName; QCursor *resizeCursor; QCursor *resizeBlockCursor; bool useXSane; ScannerBase * scanner; QByteArray *ba; //SpellChecker *spellChecker; QMenu *m_menu; PDFExtractor * pdfx; QProgressDialog pdfPD; int ifCounter; Settings * settings; PageCollection * pages; QLabel * engineLabel; QLabel * langLabel; QAction * slAction; bool globalDeskew; private slots: bool findEngine(); void readyRead(int sig); void setResizingCusor(); void setUnresizingCusor(); void loadPage(); void rightMouseClicked(int x, int y, bool inTheBlock); void setupPDFPD(); void onShowWindow(); void addSnippet(int index); void preprocessPage(); void saveProject(); void loadProject(); }; yagf-0.9.3.2/src/mainform.cpp0000664000175000017500000011412602263216100016433 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2013 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "sidebar.h" #include "droplabel.h" #include "popplerdialog.h" #include "pdfextractor.h" #include "pdf2ppt.h" #include "ghostscr.h" #include "configdialog.h" #include "mainform.h" #include "tpagecollection.h" #include "scanner.h" #include "projectmanager.h" #include "langselectdialog.h" #include "tiffimporter.h" #include "busyform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qgraphicsinput.h" #include "utils.h" #include "qxtunixsignalcatcher.h" #include "PageAnalysis.h" #include #include #include #include #include const QString outputBase = "output"; const QString outputExt = ".txt"; const QString inputFile = "input.bmp"; const QString outputFile = "output.txt"; const QString scanOutputFile = "input.png"; MainForm::MainForm(QWidget *parent): QMainWindow(parent) { setupUi(this); pages = PageCollection::instance(); setWindowTitle("YAGF"); //spellChecker = new SpellChecker(textEdit); textEdit->enumerateDicts(); frame->show(); //toolBar->addWidget(label1); //toolBar->addWidget(selectLangsBox); graphicsInput = new QGraphicsInput(QRectF(0, 0, 2000, 2000), graphicsView) ; graphicsInput->addToolBarAction(actionHideShowTolbar); graphicsInput->addToolBarAction(this->actionTBLV); graphicsInput->addToolBarAction(this->actionSmaller_view); graphicsInput->addToolBarSeparator(); graphicsInput->addToolBarAction(actionRotate_90_CCW); graphicsInput->addToolBarAction(actionRotate_180); graphicsInput->addToolBarAction(actionRotate_90_CW); //graphicsInput->addToolBarAction(actionPrepare_Page); graphicsInput->addToolBarAction(actionDeskew); graphicsInput->addToolBarSeparator(); graphicsInput->addToolBarAction(actionSelect_Text_Area); graphicsInput->addToolBarAction(actionSelect_multiple_blocks); graphicsInput->addToolBarAction(ActionClearAllBlocks); label->setListWidget(sideBar); pdfx = NULL; connect(sideBar, SIGNAL(pageSelected(int)), pages, SLOT(pageSelected(int))); connect(label, SIGNAL(pageRemoved(int)), pages, SLOT(pageRemoved(int))); statusBar()->show(); useXSane = TRUE; scanner = NULL; //rotation = 0; m_menu = new QMenu(graphicsView); ifCounter = 0; connect(actionOpen, SIGNAL(triggered()), this, SLOT(loadImage())); connect(actionQuit, SIGNAL(triggered()), this, SLOT(close())); connect(this, SIGNAL(windowShown()), this, SLOT(onShowWindow()), Qt::QueuedConnection); connect(actionScan, SIGNAL(triggered()), this, SLOT(scanImage())); connect(actionPreviousPage, SIGNAL(triggered()), this, SLOT(loadPreviousPage())); connect(actionNextPage, SIGNAL(triggered()), this, SLOT(loadNextPage())); connect(actionRecognize, SIGNAL(triggered()), this, SLOT(recognize())); connect(action_Save, SIGNAL(triggered()), textEdit, SLOT(saveText())); connect(actionAbout, SIGNAL(triggered()), this, SLOT(showAboutDlg())); connect(actionOnlineHelp, SIGNAL(triggered()), this, SLOT(showHelp())); connect(actionCopyToClipboard, SIGNAL(triggered()),textEdit, SLOT(copyClipboard())); connect(actionSelect_All_Text, SIGNAL(triggered()),textEdit, SLOT(selectAll())); connect(graphicsInput, SIGNAL(rightMouseClicked(int, int, bool)), this, SLOT(rightMouseClicked(int, int, bool))); connect(actionSelect_HTML_format, SIGNAL(triggered()), this, SLOT(selectHTMLformat())); connect(graphicsInput, SIGNAL(increaseMe()), this, SLOT(enlargeButtonClicked())); connect(graphicsInput, SIGNAL(decreaseMe()), this, SLOT(decreaseButtonClicked())); connect(sideBar, SIGNAL(filesDropped(QStringList)), SLOT(loadFiles(QStringList))); connect(pages, SIGNAL(loadPage()), this, SLOT(loadPage())); connect(graphicsInput, SIGNAL(blockCreated(QRect)), pages, SLOT(addBlock(QRect))); connect(graphicsInput, SIGNAL(deleteBlock(QRect)), pages, SLOT(deleteBlock(QRect))); connect(sideBar, SIGNAL(fileRemoved(int)), pages, SLOT(pageRemoved(int))); connect (pages, SIGNAL(addSnippet(int)), this, SLOT(addSnippet(int))); connect(actionSelect_languages, SIGNAL(triggered()), this, SLOT(selectLanguages())); selectLangsBox = new QComboBox(); selectLangsBox->setToolTip(trUtf8("Recognition language")); // connect(selectLangsBox->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(LangTextChanged(QString))); initSettings(); engineLabel = new QLabel(); statusBar()->addPermanentWidget(engineLabel, 0); if (settings->getSelectedEngine() == UseCuneiform) { //fillLanguagesBoxCuneiform(); engineLabel->setText(trUtf8("Using Cuneiform")); } if (settings->getSelectedEngine() == UseTesseract) { //fillLanguagesBoxTesseract(); engineLabel->setText(trUtf8("Using Tesseract")); } slAction = toolBar->insertWidget(actionRecognize, selectLangsBox); langLabel = new QLabel(); statusBar()->addPermanentWidget(langLabel); if (settings->getSelectedLanguages().count() == 1) { slAction->setVisible(false); langLabel->setText(trUtf8("Recognition Language") + ": " + settings->getFullLanguageName(settings->getLanguage())); } fillLangBox(); delTmpFiles(); QXtUnixSignalCatcher::connectUnixSignal(SIGUSR2); ba = new QByteArray(); connect(QXtUnixSignalCatcher::catcher(), SIGNAL(unixSignal(int)), this, SLOT(readyRead(int))); QPixmap l_cursor; l_cursor.load(":/resize.png"); resizeCursor = new QCursor(l_cursor); graphicsInput->setMagnifierCursor(resizeCursor); l_cursor.load(":/resize_block.png"); resizeBlockCursor = new QCursor(l_cursor); // textEdit->setContextMenuPolicy(Qt::ActionsContextMenu); this->sideBar->show(); connect(actionRecognize_All_Pages, SIGNAL(triggered()), this, SLOT(recognizeAll())); QPixmap pm; pm.load(":/align.png"); //alignButton->setIcon(pm); pm.load(":/undo.png"); //unalignButton->setIcon(pm); //connect(unalignButton, SIGNAL(clicked()), this, SLOT(unalignButtonClicked())); //clearBlocksButton->setDefaultAction(ActionClearAllBlocks); loadFromCommandLine(); emit windowShown(); if (findProgram("pdftoppm")) { pdfx = new PDF2PPT(); } else if (findProgram("gs")) { pdfx = new GhostScr(); } if (pdfx) { connect(pdfx, SIGNAL(addPage(QString)), this, SLOT(addPDFPage(QString)), Qt::QueuedConnection); connect (pdfx, SIGNAL(finished()), this, SLOT(finishedPDF())); } setupPDFPD(); } void MainForm::onShowWindow() { // actionCheck_spelling->setCheckable(true); connect(selectLangsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(newLanguageSelected(int))); selectLangsBox->setCurrentIndex(selectLangsBox->findData(QVariant(settings->getLanguage()))); //spellChecker->setLanguage(language); //actionCheck_spelling->setEnabled(spellChecker->spellCheck()); } void MainForm::loadFromCommandLine() { QStringList sl = QApplication::arguments(); if (sl.count() > 1) { if (QFile::exists(sl.at(1))) loadFile(sl.at(1)); for (int i = 2; i < sl.count(); i++) { QApplication::processEvents(); if (QFile::exists(sl.at(i))) loadFile(sl.at(i)); } sideBar->select(sl.at(1)); } } void MainForm::loadFiles(const QStringList &files) { if (files.count() == 1) { if (QFile::exists(files.at(0))) { if (files.at(0).endsWith(".tiff", Qt::CaseInsensitive)||files.at(0).endsWith(".tif", Qt::CaseInsensitive)) loadTIFF(files.at(0)); else loadFile(files.at(0)); } return; } QProgressDialog pd(this); pd.setWindowTitle("YAGF"); pd.setLabelText(trUtf8("Loading files...")); pd.setRange(1, files.count()); pd.setValue(1); pd.show(); for (int i = 0; i < files.count(); i++) { if (QFile::exists(files.at(i))) { if (files.at(i).endsWith(".tiff", Qt::CaseInsensitive)||files.at(i).endsWith(".tif", Qt::CaseInsensitive)) loadTIFF(files.at(i)); else loadFile(files.at(i)); } pd.setValue(i+1); QApplication::processEvents(); if (pd.wasCanceled()) break; } } void MainForm::LangTextChanged(const QString &text) { if (selectLangsBox->findText(text, Qt::MatchStartsWith) < 0) selectLangsBox->lineEdit()->setText(""); } void MainForm::showConfigDlg() { ConfigDialog dialog(this); if (dialog.exec()) { //if (settings->getSelectedEngine() != ose) { QString oldLang = selectLangsBox->currentText(); selectLangsBox->clear(); if (settings->getSelectedEngine() == UseCuneiform) { engineLabel->setText(trUtf8("Using Cuneiform")); if (settings->selectedLanguagesAvailableTo("cuneiform").count() == 0) { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Cuneiform doesn't support any of selected recognition langualges.\nFalling back to tesseract. Please install tesseract.")); settings->setSelectedEngine(UseTesseract); engineLabel->setText(trUtf8("Using Tesseract")); } } if (settings->getSelectedEngine() == UseTesseract) { engineLabel->setText(trUtf8("Using Tesseract")); if (settings->selectedLanguagesAvailableTo("tesseract").count() == 0) { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Tesseract doesn't support any of selected recognition langualges.\nFalling back to cueniform. Please install cuneiform.")); settings->setSelectedEngine(UseCuneiform); engineLabel->setText(trUtf8("Using Cuneiform")); } } fillLangBox(); int newIndex = selectLangsBox->findText(oldLang); if (newIndex >= 0) { selectLangsBox->setCurrentIndex(newIndex); settings->setLanguage(selectLangsBox->itemData(newIndex).toString()); } else { settings->setLanguage("eng"); for (int i = 0; i < selectLangsBox->count(); i++) { QString s = selectLangsBox->itemData(i).toString(); if (s == "eng") { newLanguageSelected(i); selectLangsBox->setCurrentIndex(i); break; } } } //} else fillLangBox(); toolBar->setIconSize(settings->getIconSize()); if (selectLangsBox->count() > 1) { slAction->setVisible(true); langLabel->setText(""); } else { if(settings->getSelectedLanguages().count() == 1) { slAction->setVisible(false); langLabel->setText(trUtf8("Recognition Language") + ": " + settings->getFullLanguageName(settings->getLanguage())); } } } } void MainForm::importPDF() { if (!pdfx) { QMessageBox::critical(this, trUtf8("No PDF converter installed"), trUtf8("No compatible PDF converter software could be found. Please install either the pdftoppm utility or the GhostScript package (from this the gs command will be required).")); return; } PopplerDialog dialog(this); if (dialog.exec()) { pdfx->setSourcePDF(dialog.getPDFFile()); if (pdfx->getSourcePDF().isEmpty()) { QMessageBox::information(this, trUtf8("Error"), trUtf8("PDF file name may not be empty")); return; } pdfx->setStartPage(dialog.getStartPage()); pdfx->setStopPage(dialog.getStopPage()); pdfx->setOutputDir(); QApplication::processEvents(); pdfPD.setWindowFlags(Qt::Dialog|Qt::WindowStaysOnTopHint); pdfPD.show(); pdfPD.setMinimum(0); pdfPD.setMaximum(100); globalDeskew = settings->getAutoDeskew(); settings->setAutoDeskew(dialog.getDeskew()); QApplication::processEvents(); pdfx->exec(); } } void MainForm::addPDFPage(QString pageName) { pages->appendPage(pageName); int fr = pdfx->filesRemaining(pageName); if (fr > 0) { int ft = pdfx->filesTotal(); if (ft != 0) { int ratio = ((ft-fr)*100)/ft; //if (ratio > pdfPD.value()) pdfPD.setValue(ratio); } } else pdfPD.setValue(pdfPD.value()+1); } void MainForm::finishedPDF() { pdfPD.hide(); //setupPDFPD(); settings->setAutoDeskew(globalDeskew); } void MainForm::loadImage() { QFileDialog dialog(this, trUtf8("Open Image"), settings->getLastDir(), trUtf8("Image Files (*.png *.jpg *.jpeg *.bmp *.tiff *.tif *.gif *.pnm *.pgm *.pbm *.ppm)")); dialog.setFileMode(QFileDialog::ExistingFiles); if (dialog.exec()) { QStringList fileNames; fileNames = dialog.selectedFiles(); settings->setLastDir(dialog.directory().path()); if (fileNames.count() > 0) { loadFiles(fileNames); } } } void MainForm::singleColumnButtonClicked() { //singleColumn = singleColumnButton->isChecked(); } void MainForm::closeEvent(QCloseEvent *event) { if (!textEdit->textSaved()) { QPixmap icon; icon.load(":/info.png"); QMessageBox messageBox(QMessageBox::NoIcon, "YAGF", trUtf8("There is an unsaved text in the editor window. Do you want to save it?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, this); messageBox.setIconPixmap(icon); int result = messageBox.exec(); if (result == QMessageBox::Save) textEdit->saveText(); else if (result == QMessageBox::Cancel) { event->ignore(); return; } } if (scanner) { delete scanner; scanner = NULL; } settings->setSize(size()); settings->setPosition(pos()); settings->setFullScreen(isFullScreen()); settings->writeSettings(); delTmpFiles(); event->accept(); QXtUnixSignalCatcher::catcher()->disconnectUnixSugnals(); pages->clear(); } void MainForm::rotateCWButtonClicked() { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); pages->rotate90CW(); setCursor(oldCursor); } void MainForm::rotateCCWButtonClicked() { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); pages->rotate90CCW(); setCursor(oldCursor); } void MainForm::rotate180ButtonClicked() { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); pages->rotate180(); setCursor(oldCursor); } void MainForm::enlargeButtonClicked() { pages->makeLarger(); } void MainForm::decreaseButtonClicked() { pages->makeSmaller(); } void MainForm::initSettings() { settings = Settings::instance(); if (settings->getFullScreen()) showFullScreen(); else { move(settings->getPosition()); resize(settings->getSize()); } actionCheck_spelling->setChecked(settings->getCheckSpelling()); actionSelect_HTML_format->setChecked(settings->getOutputFormat() != "text"); QList li; li.append(1); li.append(1); splitter->setSizes(li); toolBar->setIconSize(settings->getIconSize()); } void MainForm::newLanguageSelected(int index) { if (index < 0) return; settings->setLanguage(selectLangsBox->itemData(index).toString()); actionCheck_spelling->setEnabled(textEdit->hasDict(settings->getLanguage())); if (settings->getCheckSpelling()) { settings->setCheckSpelling(textEdit->spellCheck(settings->getLanguage())); //actionCheck_spelling->setEnabled(checkSpelling); actionCheck_spelling->setChecked(settings->getCheckSpelling()); } } void MainForm::scanImage() { if (useXSane) { if (scanner) { delete scanner; } ScannerFactory * sf = new ScannerFactory(); scanner = sf->createScannerFE("xsane"); if (scanner == NULL) { QMessageBox::warning(this, trUtf8("Scanning is impossible"), trUtf8("No scanning front-end is found. Please install XSane in order to perform scanning.")); return; } scanner->setOutputFile(settings->workingDir() + scanOutputFile); delete sf; scanner->exec(); } } void MainForm::loadFile(const QString &fn, bool loadIntoView) { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); if (pages->appendPage(fn)) { if (loadIntoView) { pages->makePageCurrent(pages->count()-1); loadPage(); sideBar->item(sideBar->count()-1)->setSelected(true); } } else { QMessageBox::warning(this, trUtf8("Failed to Load Image"), fn); } setCursor(oldCursor); } void MainForm::loadTIFF(const QString &fn, bool loadIntoView) { TiffImporter ti(fn); ti.exec(); QStringList files = ti.extractedFiles(); if (!files.count()) { QMessageBox mb; mb.setWindowTitle("YAGF"); mb.setIconPixmap(QPixmap(":/critical.png")); mb.setText(trUtf8("Cannot open file %1. Make sure imagemagick and tifftopnm are installed.").arg(fn)); mb.addButton(QMessageBox::Close); mb.exec(); return; } loadFiles(files); } void MainForm::delTmpFiles() { QDir dir(settings->workingDir()); dir.setFilter(QDir::Files | QDir::NoSymLinks); for (uint i = 0; i < dir.count(); i++) { if (dir[i].endsWith("jpg") || dir[i].endsWith("bmp") || dir[i].endsWith("png") || dir[i].endsWith("txt") || dir[i].endsWith("ygf")) dir.remove(dir[i]); } if (pdfx) pdfx->setOutputDir(); delTmpDir(); } void MainForm::loadNextPage() { } void MainForm::loadPreviousPage() { } // TODO: think on blocks/page recognition bool MainForm::useTesseract(const QString &inputFile) { QProcess proc; proc.setWorkingDirectory(settings->workingDir()); QStringList sl; sl.append(inputFile); sl.append(outputBase); sl.append("-l"); sl.append(settings->getLanguage()); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("TESSDATA_PREFIX", settings->getTessdataPath()); QDir dir(settings->getTessdataPath()+"tessdata/"); QStringList sl1; sl1 << QString::fromUtf8("*%1.*").arg(settings->getLanguage()); if (dir.entryList(sl1, QDir::Files).count() == 0) { QMessageBox mb(this); mb.setIconPixmap(QPixmap(":/warning.png")); mb.setWindowTitle("tesseract"); mb.setText(trUtf8("You have selected recognising %1 language using tesseract OCR. Currently the data for this language is not installed in your system. Please install the tesseract data files for \"%2\" from your system repository.").arg(settings->getFullLanguageName(settings->getLanguage())).arg(settings->getLanguage())); mb.addButton(QMessageBox::Ok); mb.exec(); return false; } proc.setProcessEnvironment(env); proc.start("tesseract", sl); proc.waitForFinished(-1); if (proc.exitCode()) { QByteArray stdoutBytes = proc.readAllStandardOutput(); QByteArray stderrBytes = proc.readAllStandardError(); QString output = QString(stdoutBytes) + QString(stderrBytes); QMessageBox::critical(this, trUtf8("Starting tesseract failed"), trUtf8("The system said: ") + (output != "" ? output : trUtf8("program not found"))); return false; } return true; } bool MainForm::useCuneiform(const QString &inputFile, const QString &outputFile) { QProcess proc; proc.setWorkingDirectory(settings->workingDir()); QStringList sl; sl.append("-l"); sl.append(settings->getLanguage()); sl.append("-f"); if (settings->getOutputFormat() == "text") sl.append("text"); else sl.append("html"); sl.append("-o"); sl.append(settings->workingDir() + outputFile); sl.append(settings->workingDir() + inputFile); proc.start("cuneiform", sl); proc.waitForFinished(-1); if (proc.exitCode()) { QByteArray stdoutBytes = proc.readAllStandardOutput(); QByteArray stderrBytes = proc.readAllStandardError(); QString output = QString(stdoutBytes) + QString(stderrBytes); QMessageBox::critical(this, trUtf8("Starting cuneiform failed"), trUtf8("The system said: ") + (output != "" ? output : trUtf8("program not found"))); return false; } return true; } void MainForm::recognizeInternal() { if (settings->getSelectedEngine() == UseCuneiform) { if (!useCuneiform(inputFile, outputFile)) return; } if (settings->getSelectedEngine() == UseTesseract) { if (!useTesseract(inputFile)) return; } QFile textFile(settings->workingDir() + outputFile); textFile.open(QIODevice::ReadOnly); QByteArray text = textFile.readAll(); textFile.close(); QString textData; QTextCodec *codec = QTextCodec::codecForName("UTF-8"); textData = codec->toUnicode(text); //QString::fromUtf8(text.data()); if (settings->getOutputFormat() == "text") textData.prepend(""); textData.replace("", "\"--"); textData.replace(".bmp>", ""); // textData.replace("-

", ""); // textData.replace("-
", ""); textEdit->append(textData); textEdit->append(QString(" ")); if (settings->getCheckSpelling()) { actionCheck_spelling->setChecked(textEdit->spellCheck(settings->getLanguage())); } } void MainForm::recognize() { QFile::remove(settings->workingDir() + "input*.bmp"); if (!pages->pageValid()) { QMessageBox::critical(this, trUtf8("Error"), trUtf8("No image loaded")); return; } if (!findEngine()) return; if (pages->blockCount() > 0) { for (int i = 0; i < pages->blockCount(); i++) { prepareBlockForRecognition(i); recognizeInternal(); } } else { preparePageForRecognition(); recognizeInternal(); } } void MainForm::showAboutDlg() { QPixmap icon; icon.load(":/yagf.png"); QMessageBox aboutBox(QMessageBox::NoIcon, trUtf8("About YAGF"), trUtf8("

YAGF - Yet Another Graphical Front-end for cuneiform and tesseract OCR engines

Version %1

Ⓒ 2009-2014 Andrei Borovsky

This is a free software distributed under GPL v3. Visit http://symmetrica.net/cuneiform-linux/yagf-en.html for more details.").arg(version), QMessageBox::Ok); aboutBox.setIconPixmap(icon); QList labels = aboutBox.findChildren(); for (int i = 0; i < labels.count(); i++) { QLabel *lab = labels.at(i); lab->setTextInteractionFlags(Qt::TextBrowserInteraction); } aboutBox.setTextFormat(Qt::RichText); aboutBox.exec(); } void MainForm::showHelp() { QDesktopServices::openUrl(QUrl(trUtf8("http://symmetrica.net/cuneiform-linux/yagf-en.html"))); } void MainForm::readyRead(int sig) { QFile f(settings->workingDir() + scanOutputFile); QString newName = QString(settings->workingDir() + "scan-input-%1.png").arg(ifCounter); ifCounter++; QFileInfo fi(newName); if (fi.exists()) { QFile f2(newName); f2.remove(); } f.rename(newName); loadFile(newName); } void MainForm::delTmpDir() { QDir dir; dir.setPath(settings->workingDir() + "output_files"); dir.setFilter(QDir::Files | QDir::NoSymLinks); for (uint i = 0; i < dir.count(); i++) { if (dir[i].endsWith("jpg") || dir[i].endsWith("bmp") || dir[i].endsWith("ygf")) dir.remove(dir[i]); } dir.rmdir(settings->workingDir() + "output_files"); } void MainForm::clearTmpFiles() { QFile::remove(settings->workingDir() + "tmp*.bmp"); QFile::remove(settings->workingDir() + "tmp*.ygf"); QFile f(settings->workingDir()+inputFile); f.remove(); f.setFileName(settings->workingDir()+outputFile); f.remove(); } void MainForm::fillLangBox() { disconnect(selectLangsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(newLanguageSelected(int))); QStringList sl = Settings::instance()->getSelectedLanguages(); settings->startLangPair(); QString full; QString abbr; selectLangsBox->clear(); while(settings->getLangPair(full, abbr)) { if (sl.contains(full)||(sl.count()== 0)) selectLangsBox->addItem(full, QVariant(abbr)); } selectLangsBox->setCurrentIndex(-1); connect(selectLangsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(newLanguageSelected(int))); } void MainForm::preparePageForRecognition() { clearTmpFiles(); pages->savePageForRecognition(settings->workingDir() + inputFile); } void MainForm::prepareBlockForRecognition(const QRect &r) { clearTmpFiles(); pages->saveBlockForRecognition(r, settings->workingDir() + inputFile); } void MainForm::prepareBlockForRecognition(int index) { clearTmpFiles(); pages->saveBlockForRecognition(index, settings->workingDir() + inputFile); } void MainForm::setResizingCusor() { //scrollArea->widget()->setCursor(*resizeBlockCursor); } void MainForm::setUnresizingCusor() { //scrollArea->widget()->setCursor(QCursor(Qt::ArrowCursor)); } void MainForm::loadPage() { //graphicsInput->clearBlocks(); graphicsInput->loadImage(pages->pixmap()); QApplication::processEvents(); for (int i = 0; i < pages->blockCount(); i++) graphicsInput->addBlockColliding(pages->getBlock(i)); QFileInfo fi(pages->fileName()); setWindowTitle(QString("YAGF - %1").arg(fi.fileName()) ); } void MainForm::recognizeAll() { if (pages->count() == 0) return; QProgressDialog progress(trUtf8("Recognizing pages..."), trUtf8("Abort"), 0, pages->count(), this); progress.setWindowTitle("YAGF"); progress.show(); progress.setValue(0); pages->makePageCurrent(-1); for (int i = 0; i < pages->count(); i++) { progress.setValue(i); if (progress.wasCanceled()) break; pages->makeNextPageCurrent(); recognize(); } } void MainForm::unalignButtonClicked() { /*if (((QSelectionLabel *) scrollArea->widget())->pixmap()->isNull()) return; int rot = ((FileToolBar *) m_toolBar)->getRotation(); int rrot = ((rot + 45)/90); rrot *=90; rotateImage(rrot - rot); rotation = rrot;*/ } void MainForm::hideToolBar() { graphicsInput->setToolBarVisible(); } void MainForm::on_ActionClearAllBlocks_activated() { pages->clearBlocks(); loadPage(); } void MainForm::rightMouseClicked(int x, int y, bool inTheBlock) { m_menu->clear(); m_menu->addAction(ActionClearAllBlocks); if (inTheBlock) { m_menu->addAction(ActionDeleteBlock); m_menu->addAction(actionRecognize_block); m_menu->addAction(actionSave_block); m_menu->addAction(actionDeskew_by_Block); } else { m_menu->addAction(actionSelect_Text_Area); m_menu->addAction(actionSelect_multiple_blocks); } QPoint p = graphicsView->mapToGlobal(QPoint(x, y)); m_menu->move(p); m_menu->show(); } void MainForm::setupPDFPD() { pdfPD.setWindowTitle("YAGF"); pdfPD.setLabelText(trUtf8("Importing pages from the PDF document...")); pdfPD.setCancelButton(new QPushButton()); pdfPD.setCancelButtonText(trUtf8("Cancel")); pdfPD.setMinimum(-1); pdfPD.setMaximum(-1); pdfPD.setWindowIcon(QIcon(":/yagf.png")); if (pdfx) { connect(&pdfPD, SIGNAL(canceled()), pdfx, SLOT(cancel())); connect(&pdfPD, SIGNAL(canceled()), this, SLOT(cancelPDF())); } } void MainForm::on_ActionDeleteBlock_activated() { QRect r = graphicsInput->getCurrentBlock(); graphicsInput->deleteCurrentBlock(); pages->deleteBlock(r); } bool MainForm::findEngine() { if (settings->getSelectedEngine() == UseCuneiform) { if (!findProgram("cuneiform")) { if (findProgram("tesseract")) { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("cuneiform not found, switching to tesseract")); settings->setSelectedEngine(UseTesseract); } else { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("No recognition engine found.\nPlease install either cuneiform or tesseract")); return false; } } } if (settings->getSelectedEngine() == UseTesseract) { if (!findProgram("tesseract")) { if (findProgram("cuneiform")) { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("tesseract not found, switching to cuneiform")); settings->setSelectedEngine(UseCuneiform); } else { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("No recognition engine found.\nPlease install either cuneiform or tesseract")); return false; } } } return true; } void MainForm::on_actionRecognize_block_activated() { if (!findEngine()) return; if (graphicsInput->getCurrentBlock().isNull()) return; clearTmpFiles(); pages->saveRawBlockForRecognition(graphicsInput->getCurrentBlock(), settings->workingDir() + inputFile); recognizeInternal(); } /*void MainForm::on_actionRecognize_activated() { }*/ void MainForm::on_actionCheck_spelling_triggered() { settings->setCheckSpelling(actionCheck_spelling->isChecked()); } void MainForm::on_actionSave_current_image_activated() { QCursor oc = cursor(); setCursor(Qt::WaitCursor); QString format; QString fn = getFileNameToSaveImage(format); if (!fn.isEmpty()) { if (!(pages->savePageAsImage(fn, format))) QMessageBox::warning(this, QObject::trUtf8("Warning"), QObject::trUtf8("Failed to save the image")); } setCursor(oc); } QString MainForm::getFileNameToSaveImage(QString &format) { QString jpegFilter = QObject::trUtf8("JPEG Files (*.jpg)"); QString pngFilter = QObject::trUtf8("PNG Files (*.png)"); QStringList filters; format = "JPEG"; filters << jpegFilter << pngFilter; QFileDialog dialog(this, trUtf8("Save Image"), settings->getLastOutputDir()); dialog.setFilters(filters); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setDefaultSuffix("jpg"); if (dialog.exec()) { if (dialog.selectedNameFilter() == jpegFilter) { format = "JPEG"; dialog.setDefaultSuffix("jpg"); } else if (dialog.selectedNameFilter() == pngFilter) { format = "PNG"; dialog.setDefaultSuffix("png"); } QStringList fileNames; fileNames = dialog.selectedFiles(); settings->setLastOutputDir(dialog.directory().path()); return fileNames.at(0); } return ""; } MainForm::~MainForm() { delete resizeBlockCursor; delete resizeCursor; //delete fileChannel; delete graphicsInput; delete ba; delete pdfx; } void MainForm::on_actionSave_block_activated() { QString format; QString fn = getFileNameToSaveImage(format); if (!fn.isEmpty()) pages->saveBlockForRecognition(graphicsInput->getCurrentBlock(), fn, format); } void MainForm::on_actionCheck_spelling_activated() { settings->setCheckSpelling(actionCheck_spelling->isChecked()); if (settings->getCheckSpelling()) { actionCheck_spelling->setChecked(textEdit->spellCheck(settings->getLanguage())); } else textEdit->unSpellCheck(); } /*void MainForm::on_alignButton_clicked() { this->AnalizePage(); }*/ void MainForm::on_actionDeskew_activated() { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); pages->deskew(); setCursor(oldCursor); } void MainForm::on_actionSelect_HTML_format_activated() { if (actionSelect_HTML_format->isChecked()) settings->setOutputFormat("html"); else settings->setOutputFormat("text"); } void MainForm::pasteimage() { QClipboard *clipboard = QApplication::clipboard(); QPixmap pm = clipboard->pixmap(); if (pm.isNull()) return; QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); QString tmpFile = "input-01.png"; QFileInfo fi(settings->workingDir() + tmpFile); while (fi.exists()) { QString digits = extractDigits(tmpFile); bool result; int d = digits.toInt(&result); if (!result) return; d++; if (d < 0) d = 0; QString newDigits = QString::number(d); while (newDigits.size() < digits.size()) newDigits = '0' + newDigits; tmpFile = tmpFile.replace(digits, newDigits); fi.setFile(settings->workingDir(), tmpFile); } pm.save(fi.absoluteFilePath(), "PNG"); loadFile(fi.absoluteFilePath()); setCursor(oldCursor); } void MainForm::deskewByBlock() { /*QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); graphicsInput->update(); QApplication::processEvents(); if (!graphicsInput->getCurrentBlock().isNull()) { QImage img = graphicsInput->getCurrentBlock();*/ pages->deskew(); //} ///setCursor(oldCursor); } void MainForm::selectTextArea() { pages->blockAllText(); } void MainForm::addSnippet(int index) { sideBar->addItem(pages->snippet()); } void MainForm::preprocessPage() { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); if (!pages->splitPage(true)) QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Failed to detect text areas on this page.\nThe page possibly lacks contrast. Try to select blocks manually.")); setCursor(oldCursor); } void MainForm::saveProject() { if (settings->getProjectDir().isEmpty()) { QString dir = QFileDialog::getExistingDirectory(this, QObject::trUtf8("Select Project Directory"), ""); if (dir.isEmpty()) return; QCursor oldCursor = cursor(); QDir dinfo(dir); if (dinfo.entryList().count() > 2) { QMessageBox::warning(this, trUtf8("Warning"), trUtf8("The selected directoy is not empty. Please select or create another one.")); } else { ProjectSaver ps; if (!ps.save(dir)) QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Failed to save the project.")); else settings->setProjectDir(dir); } setCursor(oldCursor); } else { QCursor oldCursor = cursor(); ProjectSaver ps; if (!ps.save(settings->getProjectDir())) QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Failed to save the project.")); setCursor(oldCursor); } } void MainForm::loadProject() { pages->clear(); QString dir = QFileDialog::getExistingDirectory(this, QObject::trUtf8("Select Project Directory"), ""); if (dir.isEmpty()) return; QCursor oldCursor = cursor(); ProjectLoader pl; if (!pl.load(dir)) QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Failed to load project.")); else settings->setProjectDir(dir); setCursor(oldCursor); } void MainForm::selectBlocks() { QCursor oldCursor = cursor(); setCursor(Qt::WaitCursor); if (!pages->splitPage(false)) QMessageBox::warning(this, trUtf8("Warning"), trUtf8("Failed to detect text areas on this page.\nThe page possibly lacks contrast. Try to select blocks manually.")); setCursor(oldCursor); } void MainForm::selectHTMLformat() { if (actionSelect_HTML_format->isChecked()) settings->setOutputFormat("html"); else settings->setOutputFormat("text"); } void MainForm::SelectRecognitionLanguages() { LangSelectDialog lsd; if (lsd.exec() == QDialog::Accepted) fillLangBox(); } void MainForm::cancelPDF() { pdfx->removeRemaining(); //pdfPD.setLabelText(trUtf8("Opening already imported pages...")); // pdfPD.setCancelButton(0); } void MainForm::selectLanguages() { LangSelectDialog lsd(this); // ;) lsd.exec(); } yagf-0.9.3.2/src/make_style.sh0000664000175000017500000000023202263216100016600 0ustar parallelsparallels#!/bin/sh astyle --style=k\&r -l --convert-tabs --pad=oper --pad-header --indent-switches --unpad=paren --align-pointer=name --indent=spaces=4 *.cpp *.h yagf-0.9.3.2/src/pdf2ppt.cpp0000664000175000017500000000270102263216100016175 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "pdf2ppt.h" #include #include PDF2PPT::PDF2PPT() : PDFExtractor() { } void PDF2PPT::exec() { QString command = "pdftoppm"; QStringList args; args << "-jpeg"; if ((!getStopPage().isEmpty()) || (getStopPage().toInt() > 0)) { if (getStartPage().toInt() == 0) this->setStartPage("1"); args << "-f" << getStartPage() << "-l" << getStopPage(); } args << "-rx" << "600" << "-ry" << "600" << this->getSourcePDF(); setOutputPrefix(getOutputDir().append("page")); args << getOutputPrefix(); setOutputExtension("jpg"); execInternal(command, args); } yagf-0.9.3.2/src/mainform.ui0000664000175000017500000011374302263216100016272 0ustar parallelsparallels MainWindow 0 0 1920 1138 Qt::CustomContextMenu MainWindow :/yagf.png:/yagf.png 2 2 2 2 2 QFrame::StyledPanel QFrame::Raised 128 0 128 16777215 94 94 94 94 94 94 255 255 255 1 Qt::ScrollBarAlwaysOff true 100 128 Qt::ElideNone QListView::LeftToRight 4 QListView::IconMode 128 80 128 80 76 76 76 94 94 94 94 94 94 60 60 60 94 94 94 76 76 76 94 94 94 94 94 94 94 94 94 94 94 94 76 76 76 94 94 94 94 94 94 60 60 60 94 94 94 76 76 76 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 :/images/trashcan_full.png Qt::AlignCenter 1 0 Qt::Horizontal Qt::NoContextMenu Times New Roman 0 0 1920 25 &File &Help &Edit 0 0 0 0 toolBar 32 32 TopToolBarArea false toolBar_2 TopToolBarArea false toolBar_3 TopToolBarArea false :/images/scanner48.png:/images/scanner48.png S&can... Scanning images using XSane... Ctrl+N :/images/document_save_as.png:/images/document_save_as.png &Save text... Ctrl+S :/images/document_open.png:/images/document_open.png &Open Image... Open Image Ctrl+O :/images/filefind.png:/images/filefind.png &Recognize Recognizing text... Ctrl+R Choose &Language Quit Ctrl+Q :/images/back.png:/images/back.png Previous page Move to previous image :/images/forward.png:/images/forward.png Next page Move to next image Online Help About... Online Help :/images/editcopy.png:/images/editcopy.png Copy Text to Clipboard Copy recognized text to clipboard :/batch.png:/batch.png Recognize &All Pages Recognize All Pages Ctrl+A :/images/clearblocks.png:/images/clearblocks.png Clear all blocks Clear all blocks :/images/trashcan2-s.png:/images/trashcan2-s.png Delete the current block Delete the current block :/images/recblocks.png:/images/recblocks.png Recognize block Recognize this block Clear numbers Crear block numbering true :/images/check_spelling.png:/images/check_spelling.png Check spelling :/images/savpicas.png:/images/savpicas.png Save current image... Save currently opened image in its real size :/images/saveblock.png:/images/saveblock.png Save block Save the current block to the image file :/images/deskew2.png:/images/deskew2.png Deskew Correct the page skew true :/images/stock_new_html.png:/images/stock_new_html.png Select HTML format Select HTML format as recognition output :/images/larger.png:/images/larger.png Larger view Larger view :/images/smaller.png:/images/smaller.png Smaller view Smaller view :/images/rccw.png:/images/rccw.png Rotate 90 CCW Rotate 90 CCW :/images/revert.png:/images/revert.png Rotate 180 Rotate 180 :/images/rcw.png:/images/rcw.png Rotate 90 CW Rotate 90 CW << Hide/Show Toolbar :/images/application_pdf.png:/images/application_pdf.png Import from PDF... Import pages from PDF documents :/images/advanced.png:/images/advanced.png Settings :/images/edit_paste.png:/images/edit_paste.png Paste Image Paste image from clipboard :/images/selecttext.png:/images/selecttext.png Select Text Area Select Text Area Deskew Block Deskew the current block :/images/selectmulti.png:/images/selectmulti.png select multiple blocks Splits text into several blocks Toggle Large/Small Icons :/images/system_config_language.png:/images/system_config_language.png Select Recognition Language Select Recognition Language Save Project... Load Project... Select Recognition Languages... Select Recognition Languages Select All Text Select all the recognized text :/images/langs.png:/images/langs.png Select languages for recognition Select languages for recognition QXtGraphicsView QGraphicsView
qxtgraphicsview.h
SideBar QListWidget
sidebar.h
DropLabel QLabel
droplabel.h
TextEditor QTextEdit
texteditor.h
actionTBLV triggered() MainWindow enlargeButtonClicked() -1 -1 959 568 actionSmaller_view triggered() MainWindow decreaseButtonClicked() -1 -1 959 568 actionRotate_90_CCW triggered() MainWindow rotateCCWButtonClicked() -1 -1 959 568 actionRotate_180 triggered() MainWindow rotate180ButtonClicked() -1 -1 959 568 actionRotate_90_CW triggered() MainWindow rotateCWButtonClicked() -1 -1 959 568 actionHideShowTolbar triggered() MainWindow hideToolBar() -1 -1 959 568 actionImport_from_PDF triggered() MainWindow importPDF() -1 -1 959 568 actionOCR_Settings triggered() MainWindow showConfigDlg() -1 -1 959 568 actionPaste_Image triggered() MainWindow pasteimage() -1 -1 959 568 actionSelect_Text_Area triggered() MainWindow selectTextArea() -1 -1 959 568 actionDeskew_by_Block triggered() MainWindow deskewByBlock() -1 -1 959 568 actionSelect_multiple_blocks triggered() MainWindow selectBlocks() -1 -1 959 568 actionSave_Project triggered() MainWindow saveProject() -1 -1 959 568 actionLoad_Project triggered() MainWindow loadProject() -1 -1 959 568 actionSelect_Recognition_Languages triggered() MainWindow SelectRecognitionLanguages() -1 -1 959 568 rotateCWButtonClicked() rotateCCWButtonClicked() rotate180ButtonClicked() enlargeButtonClicked() decreaseButtonClicked() hideToolBar() importPDF() showConfigDlg() pasteimage() blockAllText() deskewByBlock() showAdvancedSettings() selectTextArea() selectBlocks() setSmallIcons() preprocessPage() saveProject() loadProject() SelectRecognitionLanguages()
yagf-0.9.3.2/src/pdf2ppt.h0000664000175000017500000000174002263216100015644 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PDF2PPT_H #define PDF2PPT_H #include "pdfextractor.h" class PDF2PPT : public PDFExtractor { public: PDF2PPT(); virtual void exec(); }; #endif // PDF2PPT_H yagf-0.9.3.2/src/pdfextractor.cpp0000664000175000017500000001172002263216100017324 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "pdfthread.h" #include "pdfextractor.h" #include "settings.h" #include #include #include #include #include #include #include #include #include PDFExtractor::PDFExtractor(QObject *parent) : QObject(parent) { } void PDFExtractor::setCommandStringPaged(const QString &cmdStr) { commandStringPaged = cmdStr; } void PDFExtractor::setCommandStringEntire(const QString &cmdStr) { commandStringEntire = cmdStr; } void PDFExtractor::setOutputDir() { QString pdfout = Settings::instance()->workingDir()+ QString("pdfout/"); outputDir = pdfout; QDir dir(pdfout); if (!dir.exists()) dir.mkdir(pdfout); else { dir.setFilter(QDir::Files); QStringList sl = dir.entryList(); foreach (QString s, sl) dir.remove(pdfout+s); } } QString PDFExtractor::getOutputDir() { return outputDir; } void PDFExtractor::setOutputExtension(const QString &value) { outputExtension = value; } QString PDFExtractor::getOutputExtension() { return outputExtension; } void PDFExtractor::setOutputPrefix(const QString &value) { outputPrefix = value; } QString PDFExtractor::getOutputPrefix() { return outputPrefix; } void PDFExtractor::setResolution(const QString &value) { resolution = value; } QString PDFExtractor::getResolution() { return resolution; } void PDFExtractor::setSourcePDF(const QString &value) { sourcePDF = value; } QString PDFExtractor::getSourcePDF() { return sourcePDF; } void PDFExtractor::setStartPage(const QString &value) { startPage = value; } QString PDFExtractor::getStartPage() { return startPage; } void PDFExtractor::setStopPage(const QString &value) { stopPage = value; } QString PDFExtractor::getStopPage() { return stopPage; } void PDFExtractor::cancel() { //emit killProcess(); //emit terminateProcess(); emit terminate(); } void PDFExtractor::execInternal(const QString &command, const QStringList &arguments) { filters.clear(); filters << QString("page*.%1").arg(getOutputExtension()); PDFThread thread(this); thread.setProcess(command, arguments); thread.start(); sleep(1); QFileInfoList oldFil; bool cont = true; while (cont) { //usleep(500000); QDir dir; prepareDir(dir); QFileInfoList fil; QApplication::processEvents(); fil = dir.entryInfoList(filters, QDir::Files, QDir::Name); cont = false; foreach (QFileInfo fi, fil) { if (!oldFil.contains(fi)) { usleep(250000); oldFil.append(fi); emit addPage(fi.absoluteFilePath()); QApplication::processEvents(); cont = true; } } } emit finished(); } void PDFExtractor::prepareDir(QDir &dir) { dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); dir.setSorting(QDir::Size | QDir::Reversed); dir.setSorting(QDir::Name); dir.setPath(outputDir); } int PDFExtractor::filesRemaining(const QString &fileName) { lastFile = fileName; QDir dir; prepareDir(dir); QStringList sl = dir.entryList(); sl.sort(); for (int i = 0; i < sl.count(); i++) { if (fileName.endsWith(sl.at(i))) return sl.count() - i - 1; } return -1; } void PDFExtractor::removeRemaining() { if (lastFile != "") { QDir dir; prepareDir(dir); QStringList sl = dir.entryList(); sl.sort(); bool doDelete = false; for (int i = 0; i < sl.count(); i++) { if (doDelete) { QFile f(outputDir+sl.at(i)); f.remove(); } if (lastFile.endsWith(sl.at(i))) doDelete = true; } } lastFile = ""; } int PDFExtractor::filesTotal() { QDir dir; prepareDir(dir); QStringList sl = dir.entryList(); return sl.count(); } yagf-0.9.3.2/src/pdfextractor.h0000664000175000017500000000464202263216100016776 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PDFEXTRACTOR_H #define PDFEXTRACTOR_H #include #include class QDir; class PDFExtractor : public QObject { Q_OBJECT public: explicit PDFExtractor(QObject *parent = 0); void setCommandStringPaged(const QString &cmdStr); void setCommandStringEntire(const QString &cmdStr); void setSourcePDF(const QString &value); QString getSourcePDF(); void setOutputDir(); QString getOutputDir(); void setStartPage(const QString &value); QString getStartPage(); void setStopPage(const QString &value); QString getStopPage(); void setResolution(const QString &value); QString getResolution(); void setOutputPrefix(const QString &value); QString getOutputPrefix(); void setOutputExtension(const QString &value); QString getOutputExtension(); void virtual exec() = 0; static bool findProgram(); int filesRemaining(const QString &fileName); void removeRemaining(); int filesTotal(); signals: void terminate(); void killProcess(); void terminateProcess(); void addPage(QString pageName); void finished(); public slots: void cancel(); protected: void execInternal(const QString &command, const QStringList &arguments); void prepareDir(QDir &dir); private: QString commandStringPaged; QString commandStringEntire; QString sourcePDF; QString outputDir; QString startPage; QString stopPage; QString resolution; QString outputPrefix; QString outputExtension; QStringList filters; QString lastFile; }; #endif // PDFEXTRACTOR_H yagf-0.9.3.2/src/pdfthread.cpp0000664000175000017500000000201102263216100016551 0ustar parallelsparallels#include "pdfthread.h" #include "pdfextractor.h" #include PDFThread::PDFThread(PDFExtractor *parent) : QThread() { mparent = parent; done = false; } void PDFThread::run() { done = false; QProcess process; // connect(this, SIGNAL(finished()), mparent, SIGNAL(finished()), Qt::QueuedConnection); // connect(this, SIGNAL(terminated()), mparent, SIGNAL(finished()), Qt::QueuedConnection); connect(mparent, SIGNAL(terminate()), this, SLOT(politeStop())); //connect (mparent, SIGNAL(killProcess()), &process, SLOT(kill()), Qt::QueuedConnection); //connect (mparent, SIGNAL(terminateProcess()), &process, SLOT(terminate()), Qt::QueuedConnection); process.start(command, arguments); while (!done) if (process.waitForFinished(100)) break; process.kill(); } void PDFThread::setProcess(const QString &cmd, const QStringList &args) { command = cmd; arguments.clear(); arguments.append(args); } void PDFThread::politeStop() { done = true; } yagf-0.9.3.2/src/popplerdialog.cpp0000664000175000017500000000344102263216100017461 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "popplerdialog.h" #include "ui_popplerdialog.h" #include PopplerDialog::PopplerDialog(QWidget *parent) : QDialog(parent), ui(new Ui::PopplerDialog) { ui->setupUi(this); } PopplerDialog::~PopplerDialog() { delete ui; } void PopplerDialog::on_checkBox_toggled(bool checked) { ui->spinBox->setEnabled(!checked); ui->spinBox_2->setEnabled(!checked); } void PopplerDialog::on_pushButton_clicked() { QString fileName = QFileDialog::getOpenFileName(this, trUtf8("Open PDF File"), "", trUtf8("PDF Files (*.pdf)")); if (!fileName.isEmpty()) ui->lineEdit->setText(fileName); } QString PopplerDialog::getPDFFile() { return ui->lineEdit->text(); } QString PopplerDialog::getStartPage() { return QString::number(ui->spinBox->value()); } QString PopplerDialog::getStopPage() { if (ui->checkBox->isChecked()) return "-1"; return QString::number(ui->spinBox_2->value()); } bool PopplerDialog::getDeskew() { return !(ui->checkBox_2->isChecked()); } yagf-0.9.3.2/src/pdfthread.h0000664000175000017500000000071102263216100016223 0ustar parallelsparallels#ifndef PDFTHREAD_H #define PDFTHREAD_H #include #include class PDFExtractor; class PDFThread : public QThread { Q_OBJECT public: explicit PDFThread(PDFExtractor *parent = 0); void run(); void setProcess(const QString &cmd, const QStringList &args); public slots: void politeStop(); private: bool done; QString command; QStringList arguments; PDFExtractor * mparent; }; #endif // PDFTHREAD_H yagf-0.9.3.2/src/popplerdialog.h0000664000175000017500000000241502263216100017126 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef POPPLERDIALOG_H #define POPPLERDIALOG_H #include namespace Ui { class PopplerDialog; } class PopplerDialog : public QDialog { Q_OBJECT public: explicit PopplerDialog(QWidget *parent = 0); ~PopplerDialog(); QString getPDFFile(); QString getStartPage(); QString getStopPage(); bool getDeskew(); private slots: void on_checkBox_toggled(bool checked); void on_pushButton_clicked(); private: Ui::PopplerDialog *ui; }; #endif // POPPLERDIALOG_H yagf-0.9.3.2/src/popplerdialog.ui0000664000175000017500000001633202263216100017317 0ustar parallelsparallels PopplerDialog 0 0 320 252 Import from PDF :/images/application_pdf.png:/images/application_pdf.png File Name 1 0 Select... Pages 2 From spinBox 1 1 1 999 12 2 4 2 To spinBox_2 0 0 1 999 Qt::Horizontal 40 20 0 1 Entire Document Don't Deskew Pages true Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox rejected() PopplerDialog reject() 316 260 286 274 buttonBox accepted() PopplerDialog accept() 248 254 157 274 yagf-0.9.3.2/src/preload.c0000664000175000017500000000403002263216100015701 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #define _GNU_SOURCE #define __USE_GNU #include //static int fd = 0; static FILE * f; /*int open(const char *pathname, int flags) { printf("called %s\n", pathname); }*/ FILE* fopen(const char* path, const char* mode) { printf("called %s\n", path); FILE* (*real_fopen)(const char*, const char*) = dlsym(RTLD_NEXT, "fopen"); if (strstr(path, "input.png")== NULL) return real_fopen(path, mode); if (strstr(mode, "r")) return 0; //if (fd <= 0) //fd = open("/var/tmp/yagf.fifo", O_WRONLY); f = real_fopen(path, mode); return f; } /*size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { FILE* (*real_fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream) = dlsym(RTLD_NEXT, "fwrite"); if (stream == f) write(fd, ptr, size*nmemb); return real_fwrite(ptr, size, nmemb, stream); }*/ int fclose(FILE *fp) { int (* real_fclose)(FILE *fp) = dlsym(RTLD_NEXT, "fclose"); if (f == fp) { // write(fd, endmark, 6); int ppid = getppid(); kill(ppid, SIGUSR2); } return real_fclose(fp); } yagf-0.9.3.2/src/projectmanager.h0000664000175000017500000000313202263216100017263 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include class QXmlStreamWriter; class QXmlStreamReader; class ProjectSaver : public QObject { Q_OBJECT public: explicit ProjectSaver(QObject *parent = 0); bool save(const QString &dir); signals: public slots: private: void writePages(); void writeBlocks(); void writeSettings(); QString copyFile(const QString &source); private: QXmlStreamWriter * stream; QString directory; }; class ProjectLoader : public QObject { Q_OBJECT public: explicit ProjectLoader(QObject *parent = 0); bool load(const QString &dir); signals: void languageChanged(); void engineChanged(); public slots: private: bool readPages(); bool readBlocks(); bool readSettings(); void loadPage(); bool readNextElement(); private: QXmlStreamReader * stream; QString directory; }; yagf-0.9.3.2/src/projectmanager.cpp0000664000175000017500000001675202263216100017632 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "projectmanager.h" #include "tpagecollection.h" #include "settings.h" #include #include #include #include #include #include const QString URI = "symmetrica.net/yagf"; const QString VERSION = "0.9.2"; inline QString boolToString(bool value) { return value ? "true" : "false"; } ProjectSaver::ProjectSaver(QObject *parent) : QObject(parent) { } bool ProjectSaver::save(const QString &dir) { directory = dir; if (!directory.endsWith("/")) directory = directory + "/"; QString fileName = directory+"yagf_project.xml"; QFile f(fileName); if (!f.open(QIODevice::WriteOnly|QIODevice::Truncate)) return false; stream = new QXmlStreamWriter(&f); stream->setAutoFormatting(true); stream->writeStartDocument(); stream->writeStartElement(URI, "yagf"); stream->writeAttribute(URI, "version", VERSION); writeSettings(); writePages(); stream->writeEndDocument(); f.flush(); delete stream; f.close(); return true; } void ProjectSaver::writePages() { PageCollection * pc = PageCollection::instance(); for (int i =0; i < pc->count(); i++) { stream->writeStartElement(URI, "page"); pc->makePageCurrent(i); stream->writeAttribute(URI, "image", copyFile(pc->fileName())); stream->writeAttribute(URI, "deskewed", boolToString(pc->isDeskewed())); stream->writeAttribute(URI, "rotation", QString::number(pc->getRotation())); stream->writeAttribute(URI, "preprocessed", boolToString(pc->isPreprocessed())); writeBlocks(); stream->writeEndElement(); } } void ProjectSaver::writeBlocks() { PageCollection * pc = PageCollection::instance(); for (int i = 0; i < pc->blockCount(); i++) { stream->writeStartElement(URI, "block"); Block b =pc->getBlock(i); stream->writeAttribute(URI, "left", QString::number(b.left())); stream->writeAttribute(URI, "top", QString::number(b.top())); stream->writeAttribute(URI, "width", QString::number(b.width())); stream->writeAttribute(URI, "height", QString::number(b.height())); // stream->writeAttribute(URI, "language", "eng"); stream->writeEndElement(); } } void ProjectSaver::writeSettings() { stream->writeStartElement(URI, "settings"); Settings * settings = Settings::instance(); QString engine; if (settings->getSelectedEngine() == UseCuneiform) engine = "cuneiform"; if (settings->getSelectedEngine() == UseTesseract) engine = "tesseract"; stream->writeAttribute(URI, "engine", engine); stream->writeAttribute(URI, "defaultlanguage", settings->getLanguage()); stream->writeEndElement(); } QString ProjectSaver::copyFile(const QString &source) { QFileInfo fi(source); QString dir = fi.absolutePath(); if (!dir.endsWith("/")) dir = dir + "/"; QString base = fi.baseName(); QString fileName = base+".ygf"; if (dir == directory) return fileName; QString newName = directory + fileName; if (source.endsWith(".ygf", Qt::CaseInsensitive)) { if (QFile::copy(source, newName)) return fileName; else return ""; } else { QImage image(source); if (image.save(newName)) return fileName; else return ""; } return ""; } ProjectLoader::ProjectLoader(QObject *parent): QObject(parent) { } bool ProjectLoader::load(const QString &dir) { directory = dir; if (!directory.endsWith("/")) directory = directory + "/"; QString fileName = directory+"yagf_project.xml"; QFile f(fileName); if (!f.open(QIODevice::ReadOnly)) return false; stream = new QXmlStreamReader(&f); stream->setNamespaceProcessing(true); if (!readSettings()) return false; if (!readPages()) return false; f.close(); return true; } bool ProjectLoader::readSettings() { Settings * settings = Settings::instance(); if (!readNextElement()) return false; QStringRef n; while ((n = stream->name()) != "settings") if (!readNextElement()) return false; QStringRef engine = stream->attributes().value(URI, "engine"); if (engine == "tesseract") settings->setSelectedEngine(UseTesseract); if (engine == "cuneiform") settings->setSelectedEngine(UseCuneiform); emit engineChanged(); QString language = stream->attributes().value(URI, "defaultlanguage").toString(); if (!language.isEmpty()) settings->setLanguage(language); emit languageChanged(); return true; } void ProjectLoader::loadPage() { QString image = stream->attributes().value(URI, "image").toString(); QString fn = directory + image; bool oldcl = Settings::instance()->getCropLoaded(); Settings::instance()->setCropLoaded(false); PageCollection * pc = PageCollection::instance(); Settings::instance()->setCropLoaded(oldcl); //pc->appendPage(fn); QString value = stream->attributes().value(URI, "rotation").toString(); bool deskewed = false; bool preprocessed =false; qreal rotation = 0; if (!value.isEmpty()) { rotation = (value.toDouble()); } value = stream->attributes().value(URI, "deskewed").toString(); if (!value.isEmpty()) { deskewed = value.endsWith("true", Qt::CaseInsensitive) ? true : false; } value = stream->attributes().value(URI, "preprocessed").toString(); if (!value.isEmpty()) { preprocessed = (value.endsWith("true", Qt::CaseInsensitive) ? true : false); } pc->newPage(fn,rotation,preprocessed, deskewed); } bool ProjectLoader::readPages() { if (!readNextElement()) return false; QString name; if ((name = stream->name().toString()) != "page") return false; while(stream->name() == "page") { loadPage(); if (!readBlocks()) break; } PageCollection::instance()->reloadPage(); return true; } bool ProjectLoader::readBlocks() { if (!readNextElement()) return false; while (stream->name() == "block") { int top = stream->attributes().value(URI, "top").toString().toInt(); int left = stream->attributes().value(URI, "left").toString().toInt(); int width = stream->attributes().value(URI, "width").toString().toInt(); int height = stream->attributes().value(URI, "height").toString().toInt(); PageCollection::instance()->addBlock(QRect(left, top, width, height)); if (!readNextElement()) return false; } return true; } bool ProjectLoader::readNextElement() { while (!stream->readNextStartElement()) if (stream->atEnd()) return false; return true; } yagf-0.9.3.2/src/qgraphicsinput.cpp0000664000175000017500000004151502263216100017665 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "qgraphicsinput.h" #include "qxtgraphicsview.h" #include "qxtgraphicsproxywidget.h" #include "analysis.h" #include "PageAnalysis.h" #include "math.h" #include "ycommon.h" #include "tblock.h" #include #include #include #include #include #include #include #include #include QGraphicsInput::QGraphicsInput(const QRectF &sceneRect, QGraphicsView *view) : QGraphicsScene(sceneRect) { setView(view); m_image = 0; selecting = NoSelect; hasImage = false; m_LastSelected = 0; buttonPressed = Qt::NoButton; near_res = 0; magnifierCursor = new QCursor(Qt::SizeAllCursor); toolbar = 0; redRect.setX(0); redRect.setY(0); redRect.setWidth(0); redRect.setHeight(0); xred = false; } QGraphicsInput::~QGraphicsInput() { delete magnifierCursor; } void QGraphicsInput::addToolBar() { toolbar = new QToolBar(m_view); toolbar->setMouseTracking(false); toolbar->setMovable(false); toolbar->setWindowOpacity(0.75); toolbar->move(0,0); toolbar->setIconSize(QSize(24,24)); toolbar->setMinimumHeight(32); //toolbar->setCursor(); actionList.at(0)->setText(QString::fromUtf8(">>")); setToolBarVisible(); //QXtGraphicsProxyWidget * pw = new QXtGraphicsProxyWidget(); //pw->setWidget(toolbar); //pw->setZValue(100); //this->addItem(pw); //pw->setView((QXtGraphicsView *) views().at(0)); //toolbar->setParent(0); toolbar->show(); foreach (QAction * action, actionList) { toolbar->addAction(action); } ((QXtGraphicsView *) views().at(0))->sendScrollSignal(); } bool QGraphicsInput::loadImage(const QPixmap &pixmap) { clear(); m_LastSelected = 0; m_CurrentBlockRect = 0; // clearBlocks(); m_image = addPixmap(pixmap); setSceneRect(pixmap.rect()); m_image->setZValue(-1); QApplication::processEvents(); //m_realImage->setData(1, "image"); //m_realImage->hide(); this->setFocus(); m_image->setFocus(); m_image->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MidButton); m_image->setAcceptHoverEvents(true); m_image->setData(1, "image"); addToolBar(); if (m_view) { m_view->centerOn(0, 0); m_view->show(); update(); hasImage = true; return true; } else return false; } void QGraphicsInput::setView(QGraphicsView *view) { m_view = view; if (m_view) m_view->setScene(this); } void QGraphicsInput::mousePressEvent(QGraphicsSceneMouseEvent *event) { QGraphicsScene::mousePressEvent(event); // QMessageBox::critical(0, "MOUS111", "MOUSE"); if (!hasImage) return; if (event->buttons() == Qt::LeftButton) { buttonPressed = Qt::LeftButton; if (selecting == NoSelect) { if ((near_res = nearActiveBorder(event->scenePos().x(), event->scenePos().y())) != 0) { m_CurrentBlockRect = m_LastSelected; selecting = Selecting; blockRect = m_CurrentBlockRect->rect(); } else { selecting = StartSelect; blockRect.setLeft(event->lastScenePos().x()); blockRect.setTop(event->lastScenePos().y()); blockRect.setWidth(10); blockRect.setHeight(10); } } else { //TODO!!! } } else buttonPressed = Qt::RightButton; } void QGraphicsInput::deleteBlockRect(QGraphicsRectItem *item) { if (item == 0) return; if (item == m_CurrentBlockRect) m_CurrentBlockRect = 0; if (item == m_LastSelected) m_LastSelected = 0; removeItem(item); } void QGraphicsInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) { QGraphicsScene::mouseReleaseEvent(mouseEvent); if (buttonPressed == Qt::LeftButton) { if (selecting == Selecting) { selecting = NoSelect; if ((blockRect.width() < 12) || (blockRect.height() < 12)) { if (m_CurrentBlockRect == m_LastSelected) m_LastSelected = 0; deleteBlockRect(m_CurrentBlockRect); //clik!!! leftMouseRelease(mouseEvent->scenePos().x(), mouseEvent->scenePos().y()); } else emit blockCreated(QRectF2Rect(m_CurrentBlockRect->rect())); if (xred) emit deleteBlock(redRect); xred = false; m_CurrentBlockRect = 0; } if (selecting == StartSelect) { selecting = NoSelect; m_CurrentBlockRect = 0; leftMouseRelease(mouseEvent->scenePos().x(), mouseEvent->scenePos().y()); } } if (buttonPressed == Qt::RightButton) { this->rightMouseRelease(mouseEvent->scenePos().x(), mouseEvent->scenePos().y()); } buttonPressed = Qt::NoButton; } QGraphicsRectItem *QGraphicsInput::newBlock(const QRectF &rect) { QPen p(Qt::SolidLine); QBrush b(Qt::SolidPattern); b.setColor(QColor(0, 0, 127, 127)); p.setWidth(2); p.setColor(QColor(0, 0, 255)); QGraphicsRectItem *res; res = this->addRect(rect, p, b); res->setAcceptHoverEvents(true); res->setZValue(1); res->setData(1, "block"); res->setData(2, "no"); return res; } bool QGraphicsInput::addBlock(const QRectF &rect, bool removeObstacles) { QGraphicsRectItem *block = newBlock(rect); if (!removeObstacles) { if (block->collidingItems().size() > 0) { deleteBlockRect(block); return false; } } else { for (int i = block->collidingItems().size() - 1; i >= 0; i--) { if (block->collidingItems().at(i)->data(1) == "block") deleteBlockRect((QGraphicsRectItem *) block->collidingItems().at(i)); } } m_CurrentBlockRect = block; return true; } void QGraphicsInput::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { QGraphicsScene::mouseMoveEvent(mouseEvent); if (selecting == StartSelect) { selecting = Selecting; m_CurrentBlockRect = newBlock(blockRect); } if ((mouseEvent->modifiers() & Qt::ControlModifier) == 0) if (mouseEvent->buttons() == Qt::NoButton) { near_res = nearActiveBorder(mouseEvent->scenePos().x(), mouseEvent->scenePos().y()); switch (near_res) { case 0: m_view->viewport()->setCursor(Qt::ArrowCursor); break; case 1: m_view->viewport()->setCursor(Qt::SplitHCursor); break; case 2: m_view->viewport()->setCursor(Qt::SplitVCursor); break; case 3: m_view->viewport()->setCursor(Qt::SplitHCursor); break; case 4: m_view->viewport()->setCursor(Qt::SplitVCursor); break; default: break; } } QRectF newRect; if (near_res && (mouseEvent->buttons()&Qt::LeftButton)) { if (!xred) redRect = QRectF2Rect(m_LastSelected->rect()); xred = true; QRectF newRect = m_LastSelected->mapRectToScene(m_LastSelected->rect()); switch (near_res) { case 1: newRect.setLeft(mouseEvent->lastScenePos().x()); break; case 2: newRect.setTop(mouseEvent->lastScenePos().y()); break; case 3: newRect.setRight(mouseEvent->lastScenePos().x()); break; case 4: newRect.setBottom(mouseEvent->lastScenePos().y()); break; default: break; } m_CurrentBlockRect = m_LastSelected; m_CurrentBlockRect->setRect(m_LastSelected->mapRectFromScene(newRect)); for (int i = 0; i < m_CurrentBlockRect->collidingItems().size(); ++i) if (m_CurrentBlockRect->collidingItems().at(i)->data(1) == "block") { m_CurrentBlockRect->setRect(m_LastSelected->mapRectFromScene(selBlockRect)); return; } selBlockRect = newRect; return; } if (selecting == Selecting) { newRect = blockRect; if (newRect.left() < mouseEvent->lastScenePos().x()) newRect.setRight(mouseEvent->lastScenePos().x()); else newRect.setLeft(mouseEvent->lastScenePos().x()); if (newRect.top() < mouseEvent->lastScenePos().y()) newRect.setBottom(mouseEvent->lastScenePos().y()); else newRect.setTop(mouseEvent->lastScenePos().y()); m_CurrentBlockRect->setRect(newRect); for (int i = 0; i < m_CurrentBlockRect->collidingItems().size(); ++i) if (m_CurrentBlockRect->collidingItems().at(i)->data(1) == "block") { m_CurrentBlockRect->setRect(blockRect); return; } blockRect = newRect; return; } if (!toolbar.isNull()) { // if (mouseEvent->pos().y() < toolbar->height()) // toolbar->setFocus(); } } void QGraphicsInput::leftMouseRelease(qreal x, qreal y) { QGraphicsItem *it = this->itemAt(x, y); if (it) { if (it->data(1).toString() == "block") { QGraphicsRectItem *r = (QGraphicsRectItem *) it; QPen p(Qt::SolidLine); QBrush b(Qt::SolidPattern); b.setColor(QColor(0, 0, 127, 127)); p.setColor(QColor(0, 0, 255)); p.setWidth(2); if (r->data(2).toString() == "no") { //select block!!!! if (m_LastSelected) { m_LastSelected->setPen(p); m_LastSelected->setBrush(b); m_LastSelected->setData(2, "no"); } b.setColor(QColor(127, 0, 0, 127)); p.setColor(QColor(255, 0, 0)); r->setData(2, "yes"); m_LastSelected = r; selBlockRect = m_LastSelected->rect(); redRect = QRectF2Rect(selBlockRect); // ATT // emit addBlock(QRectF2Rect(selBlockRect)); } else { m_LastSelected = 0; r->setData(2, "no"); } r->setPen(p); r->setBrush(b); // m_CurrentBlockRect = r; } } else m_CurrentBlockRect = 0; emit leftMouseClicked(m_view->mapFromScene(x, y).x(), m_view->mapFromScene(x, y).y(), m_CurrentBlockRect != 0); } void QGraphicsInput::rightMouseRelease(qreal x, qreal y) { QGraphicsItem *it = this->itemAt(x, y); if (it) { if (it->data(1).toString() == "block") { m_CurrentBlockRect = (QGraphicsRectItem *) it; } } else m_CurrentBlockRect = 0; emit rightMouseClicked(m_view->mapFromScene(x, y).x(), m_view->mapFromScene(x, y).y(), m_CurrentBlockRect != 0); } int QGraphicsInput::nearActiveBorder(qreal x, qreal y) { if (m_LastSelected == 0) return 0; x = m_LastSelected->mapFromScene(x, y).x(); y = m_LastSelected->mapFromScene(x, y).y(); qreal xcenter = m_LastSelected->rect().center().x(); qreal ycenter = m_LastSelected->rect().center().y(); qreal xcd = abs(m_LastSelected->rect().right() - xcenter) + 8; qreal ycd = abs(m_LastSelected->rect().bottom() - ycenter) + 8; if ((abs(x - m_LastSelected->rect().left()) <= 4)) { if (abs(y - ycenter) < ycd) return 1; else return 0; } if ((abs(m_LastSelected->rect().top() - y) <= 4)) { if (abs(x - xcenter) < xcd) return 2; else return 0; } if ((abs(x - m_LastSelected->rect().right()) <= 4)) { if (abs(y - ycenter) < ycd) return 3; else return 0; } if ((abs(m_LastSelected->rect().bottom() - y) <= 4)) { if (abs(x - xcenter) < xcd) return 4; else return 0; } return 0; } QRect QGraphicsInput::getActiveBlock() { return QRectF2Rect(m_LastSelected->rect()); } QRect QGraphicsInput::getCurrentBlock() { return QRectF2Rect(m_CurrentBlockRect->rect()); } void QGraphicsInput::deleteCurrentBlock() { if (m_CurrentBlockRect != 0) deleteBlockRect(m_CurrentBlockRect); } void QGraphicsInput::deleteBlock(int index) { int count = 0; for (int i = 0; i < items().count(); i++) { if (items().at(i)->data(1) == "block") { if (index == count) { deleteBlockRect((QGraphicsRectItem *)items().at(i)); return; } count++; } } } void QGraphicsInput::clearBlocks() // KEEP { for (int i = items().count() - 1; i >= 0; i--) { if (items().at(i)->data(1) == "block") { deleteBlockRect((QGraphicsRectItem *)items().at(i)); } } } void QGraphicsInput::setMagnifierCursor(QCursor *cursor) { delete magnifierCursor; magnifierCursor = new QCursor(cursor->pixmap()); } void QGraphicsInput::addToolBarAction(QAction *action) { actionList.append(action); } void QGraphicsInput::addToolBarSeparator() { QAction * action = new QAction(" | ", 0); action->setEnabled(false); actionList.append(action); } void QGraphicsInput::setToolBarVisible() { if (toolbar.isNull()) return; if (actionList.at(0)->text() == QString::fromUtf8("<<")) { for (int i = 1; i < actionList.count(); i++) actionList.at(i)->setVisible(false); toolbar->setMaximumWidth(32); toolbar->setMinimumWidth(32); actionList.at(0)->setText(QString::fromUtf8(">>")); } else { for (int i = 1; i < actionList.count(); i++) actionList.at(i)->setVisible(true); toolbar->setMaximumWidth(480); toolbar->setMinimumWidth(480); actionList.at(0)->setText(QString::fromUtf8("<<")); } } void QGraphicsInput::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) { if (wheelEvent->modifiers() == Qt::ControlModifier) { int delta = wheelEvent->delta(); qreal coeff = delta < 0 ? 1 / (1 - delta / (360.)) : 1 + delta / (240.); if (coeff >= 1) emit increaseMe(); else emit decreaseMe(); wheelEvent->accept(); m_view->viewport()->setCursor(*magnifierCursor); } else QGraphicsScene::wheelEvent(wheelEvent); } void QGraphicsInput::keyReleaseEvent(QKeyEvent *keyEvent) { if (keyEvent->key() == Qt::Key_Control) m_view->viewport()->setCursor(Qt::ArrowCursor); if (keyEvent->modifiers() & Qt::ControlModifier) { if ((keyEvent->key() == Qt::Key_Plus) || (keyEvent->key() == Qt::Key_Equal)) { emit increaseMe(); return; } if ((keyEvent->key() == Qt::Key_Minus) || (keyEvent->key() == Qt::Key_Underscore)) { emit decreaseMe(); return; } } if (keyEvent->key() > Qt::Key_F1) { emit keyPressed((int)keyEvent->key()); } } void QGraphicsInput::clearTransform() { if (m_view) { QTransform tr = m_view->transform(); tr.reset(); m_view->setTransform(tr); } } void QGraphicsInput::keyPressEvent(QKeyEvent *keyEvent) { if (keyEvent->key() == Qt::Key_Control) { m_view->viewport()->setCursor(*magnifierCursor); //QApplication:: } } void QGraphicsInput::drawLine(int x1, int y1, int x2, int y2) { QPen pen(QColor("red")); pen.setWidth(2); this->addLine(x1, y1, x2, y2, pen); } void QGraphicsInput::imageOrigin(QPoint &p) { p.setX(m_image->mapToScene(0,0).x()); p.setY(m_image->mapToScene(0,0).y()); } QPixmap QGraphicsInput::getCurrentImage() { return (m_image->pixmap()); } void QGraphicsInput::addBlockColliding(Block block) { QGraphicsRectItem *gi = newBlock(block); m_CurrentBlockRect = gi; QGraphicsTextItem * gte = new QGraphicsTextItem(QString::number(block.blockNumber()), gi); gte->setFont(QFont("Arial", 16)); gte->setDefaultTextColor(QColor("white")); gte->moveBy(block.x(), block.y()); } yagf-0.9.3.2/src/qgraphicsinput.h0000664000175000017500000000667502263216100017342 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef QGRAPHICSINPUT_H #define QGRAPHICSINPUT_H #include "sidebar.h" #include #include #include #include #include #include typedef QList ActionList; class Block; class QGraphicsPixmapItem; class QGraphicsRectItem; class QPixmap; class QCursor; class QRectF; class QCursor; class QToolBar; class QActions; enum SelectStates { NoSelect = 0, StartSelect, Selecting }; class QGraphicsInput : public QGraphicsScene { Q_OBJECT public: explicit QGraphicsInput(const QRectF &sceneRect, QGraphicsView *view = 0); ~QGraphicsInput(); void setView(QGraphicsView *view); QRect getActiveBlock(); QRect getCurrentBlock(); void deleteBlock(int index); void deleteCurrentBlock(); void clearBlocks(); bool addBlock(const QRectF &rect, bool removeObstacles = true); void addBlockColliding(Block block); void drawLine(int x1, int y1, int x2, int y2); void imageOrigin(QPoint &p); QPixmap getCurrentImage(); void cropWhiteFrame(); void setMagnifierCursor(QCursor *cursor); void addToolBarAction(QAction * action); void addToolBarSeparator(); void setToolBarVisible(); QGraphicsRectItem *newBlock(const QRectF &rect); bool loadImage(const QPixmap &pixmap); //setMagnifierCursor(QCursor * cursor = ); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent); virtual void wheelEvent(QGraphicsSceneWheelEvent *wheelEvent); virtual void keyReleaseEvent(QKeyEvent *keyEvent); virtual void keyPressEvent(QKeyEvent *keyEvent); signals: void leftMouseClicked(int x, int y, bool blockSelected); void rightMouseClicked(int x, int y, bool inTheBlock); void keyPressed(int key); void increaseMe(); void decreaseMe(); void blockCreated(QRect rect); void deleteBlock(QRect rect); private slots: private: void leftMouseRelease(qreal x, qreal y); void rightMouseRelease(qreal x, qreal y); int nearActiveBorder(qreal x, qreal y); void clearTransform(); void addToolBar(); void deleteBlockRect(QGraphicsRectItem *item); QGraphicsView *m_view; QGraphicsPixmapItem *m_image; QGraphicsRectItem *m_CurrentBlockRect; QGraphicsRectItem *m_LastSelected; SelectStates selecting; QRectF blockRect; QRectF selBlockRect; bool hasImage; Qt::MouseButton buttonPressed; QCursor *magnifierCursor; int near_res; QPointer toolbar; ActionList actionList; QRect redRect; bool xred; }; #endif // QGRAPHICSINPUT_H yagf-0.9.3.2/src/qsnippet.cpp0000664000175000017500000000270502263216100016465 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "qsnippet.h" QSnippet::QSnippet(QListWidget *parent) : QListWidgetItem(parent) { } bool QSnippet::setPage(int id, const QString &name, const QImage &image) { if (image.isNull()) { QImage img(name); if (img.isNull()) return false; setIcon(QPixmap::fromImage(img)); } else setIcon(QPixmap::fromImage(image)); this->name = name; setToolTip(name); pid = id; ih = image.height();iw = image.width(); return true; } QString QSnippet::getName() { return name; } int QSnippet::pageID() { return pid; } int QSnippet::imageHeight() { return ih; } int QSnippet::imageWidth() { return iw; } yagf-0.9.3.2/src/qxtgraphicsproxywidget.cpp0000664000175000017500000000330602263216100021463 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "qxtgraphicsproxywidget.h" #include "qxtgraphicsview.h" #include #include #include QXtGraphicsProxyWidget::QXtGraphicsProxyWidget(QGraphicsItem * parent, Qt::WindowFlags wFlags) : QGraphicsProxyWidget(parent, wFlags) { setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); } void QXtGraphicsProxyWidget::setView(QXtGraphicsView * view) { mview = view; QPointF p = mview->mapToScene(0,0); setPos(p.x(), p.y()); //setPos(0,0); connect(mview, SIGNAL(scrolled()), this, SLOT(viewScrolled())); } void QXtGraphicsProxyWidget::viewScrolled() { QPointF p = mview->mapToScene(0,0); setPos(p.x(), p.y()); } QVariant QXtGraphicsProxyWidget::itemChange(GraphicsItemChange change, const QVariant & value) { QVariant v; matrix().reset(); if (change == QGraphicsItem::ItemTransformChange) { v = QVariant(matrix()); return v; } return value; } yagf-0.9.3.2/src/qsnippet.h0000664000175000017500000000243002263216100016125 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef QSNIPPET_H #define QSNIPPET_H #include #include #include #include #include class QSnippet : public QListWidgetItem { public: explicit QSnippet(QListWidget *parent = 0); bool setPage(int id, const QString &name, const QImage &image = QImage()); QString getName(); int pageID(); int imageHeight(); int imageWidth(); signals: public slots: private: QString name; int pid; int ih; int iw ;}; #endif // QSNIPPET_H yagf-0.9.3.2/src/qxtgraphicsproxywidget.h0000664000175000017500000000244002263216100021126 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef QXTGRAPHICSPROXYWIDGET_H #define QXTGRAPHICSPROXYWIDGET_H #include class QXtGraphicsView; class QXtGraphicsProxyWidget : public QGraphicsProxyWidget { Q_OBJECT public: QXtGraphicsProxyWidget(QGraphicsItem * parent = 0, Qt::WindowFlags wFlags = 0); void setView(QXtGraphicsView * view); public slots: void viewScrolled(); protected: QVariant itemChange(GraphicsItemChange change, const QVariant & value); private: QXtGraphicsView * mview; }; #endif // QXTGRAPHICSPROXYWIDGET_H yagf-0.9.3.2/src/qxtgraphicsview.h0000664000175000017500000000261602263216100017520 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009-2011 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef QXTGRAPHICSVIEW_H #define QXTGRAPHICSVIEW_H #include #include class QXtGraphicsView : public QGraphicsView { Q_OBJECT public: QXtGraphicsView(QWidget * parent = 0):QGraphicsView(parent) { connect(this, SIGNAL(scrolledDeferred()), this, SIGNAL(scrolled()), Qt::QueuedConnection); } void sendScrollSignal() { emit scrolledDeferred(); } signals: void scrolled(); void scrolledDeferred(); protected: void scrollContentsBy (int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); emit scrolled(); } }; #endif // QXTGRAPHICSVIEW_H yagf-0.9.3.2/src/qxtunixscinternal.h0000664000175000017500000000163602263216100020074 0ustar parallelsparallels/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #ifndef QXTUNIXSCINTERNAL_H #define QXTUNIXSCINTERNAL_H #include class QXtUnixSignalCatcherInternal : public QObject { Q_OBJECT public: QXtUnixSignalCatcherInternal(QObject *parent = 0) : QObject(parent) {} void emitSignal(int sig_num) { emit unixSignalInternal(sig_num); } signals: void unixSignalInternal(int sig_num); }; #endif // QXTUNIXSCINTERNAL_H yagf-0.9.3.2/src/qxtunixsignalcatcher.cpp0000664000175000017500000000505102263216100021067 0ustar parallelsparallels/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "qxtunixsignalcatcher.h" #include "qxtunixscinternal.h" #include #include #include QXtUnixSignalCatcher * QXtUnixSignalCatcher::sc = NULL; QMutex * QXtUnixSignalCatcher::mutex = NULL; QXtUnixSignalCatcher::QXtUnixSignalCatcher(QObject *parent) : QObject(parent) { mutex = new QMutex(); sci = new QXtUnixSignalCatcherInternal(0); connect(sci, SIGNAL(unixSignalInternal(int)), this, SLOT(doEmit(int)), Qt::QueuedConnection); sigset_t newset; sigemptyset(&newset); sigaddset(&newset, SIGHUP); sigaddset(&newset, SIGTERM); sigaddset(&newset, SIGINT); sigaddset(&newset, SIGUSR1); sigaddset(&newset, SIGUSR2); sigprocmask(SIG_UNBLOCK, &newset, 0); } QXtUnixSignalCatcher::QXtUnixSignalCatcher() : QObject(0) { } QXtUnixSignalCatcher::QXtUnixSignalCatcher(QXtUnixSignalCatcher &) : QObject(0) { } QXtUnixSignalCatcher::~QXtUnixSignalCatcher() { } void QXtUnixSignalCatcher::signalHandler(int sig_num) { mutex->lock(); sc->emitSignal(sig_num); mutex->unlock(); } void QXtUnixSignalCatcher::emitSignal(int sig_num) { sci->emitSignal(sig_num); } bool QXtUnixSignalCatcher::connectUnixSignalInternal(int sig_num) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = signalHandler; return (sigaction(sig_num, &sa, 0) == 0); } bool QXtUnixSignalCatcher::connectUnixSignal(int sig_num) { catcher(); return sc->connectUnixSignalInternal(sig_num); } void QXtUnixSignalCatcher::disconnectUnixSugnals() { sigset_t newset; sigemptyset(&newset); sigaddset(&newset, SIGHUP); sigaddset(&newset, SIGTERM); sigaddset(&newset, SIGINT); sigaddset(&newset, SIGUSR1); sigaddset(&newset, SIGUSR2); sigprocmask(SIG_BLOCK, &newset, 0); delete sci; sci = NULL; delete mutex; mutex = NULL; } void QXtUnixSignalCatcher::doEmit(int sig_num) { emit unixSignal(sig_num); } QXtUnixSignalCatcher *QXtUnixSignalCatcher::catcher() { if (!sc) sc = new QXtUnixSignalCatcher(0); return sc; } yagf-0.9.3.2/src/scanimage.cpp0000664000175000017500000000013002263216100016537 0ustar parallelsparallels#include "scanimage.h" ScanImage::ScanImage(QObject *parent) : QObject(parent) { } yagf-0.9.3.2/src/qxtunixsignalcatcher.h0000664000175000017500000000253702263216100020542 0ustar parallelsparallels/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #ifndef QXTUNIXSIGNALCTACHER_H #define QXTUNIXSIGNALCTACHER_H #include class QMutex; class QXtUnixSignalCatcherInternal; class QXtUnixSignalCatcher : public QObject { Q_OBJECT public: static QXtUnixSignalCatcher * catcher(); static bool connectUnixSignal(int sig_num); void disconnectUnixSugnals(); signals: void unixSignal(int sig_num); private slots: void doEmit(int sig_num); private: QXtUnixSignalCatcher(QObject *parent = 0); QXtUnixSignalCatcher(); QXtUnixSignalCatcher(QXtUnixSignalCatcher&); ~QXtUnixSignalCatcher(); static void signalHandler(int sig_num); void emitSignal(int sig_num); bool connectUnixSignalInternal(int sig_num); private: static QXtUnixSignalCatcher * sc; static QMutex * mutex; QXtUnixSignalCatcherInternal * sci; }; #endif // QXTUNIXSIGNALCTACHER_H yagf-0.9.3.2/src/scanimage.h0000664000175000017500000000034202263216100016211 0ustar parallelsparallels#ifndef SCANIMAGE_H #define SCANIMAGE_H #include class ScanImage : public QObject { Q_OBJECT public: explicit ScanImage(QObject *parent = 0); signals: public slots: }; #endif // SCANIMAGE_H yagf-0.9.3.2/src/scanner.cpp0000664000175000017500000000555602263216100016262 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "scanner.h" #include "utils.h" #include class XSaneScannerFE : public ScannerBase { public: XSaneScannerFE(const QString &PLL, QObject *parent = 0) : ScannerBase(parent) { addParameter("-s"); addParameter("-n"); addParameter("-N"); setProgramName("XSane"); setPreloadLibrary(PLL); addEnvironmentVar("LD_PRELOAD=" + PLL); } void exec() { waitFor(); execInternal("xsane"); } }; ScannerBase::ScannerBase(QObject *parent) : QObject(parent), scanProcess(this) { environment.append(QProcess::systemEnvironment()); } ScannerBase::~ScannerBase() { waitFor(); } void ScannerBase::addParameter(const QString &s) { parameters.append(s); } void ScannerBase::addEnvironmentVar(const QString &s) { environment.append(s); } void ScannerBase::setOutputFile(const QString &s) { outputFile = s; } QString ScannerBase::programName() { return pName; } void ScannerBase::waitFor() { scanProcess.terminate(); scanProcess.waitForFinished(10000); } void ScannerBase::execInternal(const QString &s) { scanProcess.setEnvironment(environment); QStringList sl; sl.append(parameters); sl.append(outputFile); connect(&scanProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus))); scanProcess.start(s, sl); } void ScannerBase::setProgramName(const QString &s) { pName = s; } void ScannerBase::setPreloadLibrary(const QString &s) { preloadLibrary = s; } QString ScannerFactory::findPreloadLibrary() { QString path = QString(PRILIBRARY_PATH); return path + QString("libxspreload.so"); } void ScannerFactory::findFEs() { if (findProgram("xsane")) fes << "xsane"; } ScannerFactory::ScannerFactory() { preloadPath = findPreloadLibrary(); findFEs(); } ScannerBase *ScannerFactory::createScannerFE(const QString &name) { if (fes.contains(name)) return new XSaneScannerFE(preloadPath); return NULL; } void ScannerBase::finished(int, QProcess::ExitStatus) { emit processFinished(); } yagf-0.9.3.2/src/settings.cpp0000664000175000017500000004764702263216100016500 0ustar parallelsparallels/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "settings.h" #include "utils.h" #include #include #include #include Settings * Settings::m_instance = NULL; Settings::Settings() { //makeLanguageMaps(); } Settings::Settings(const Settings &) { } Settings::~Settings() { } Settings *Settings::instance() { if (!m_instance) m_instance = new Settings(); return m_instance; } void Settings::readSettings(const QString &path) { mPath = path; mPath = mPath.append("yagf.ini"); settings = new QSettings(mPath, QSettings::IniFormat); version = settings->value("program/version").toString(); if (version.isEmpty()) fr = true; else fr = false; lastDir = settings->value("mainwindow/lastDir").toString(); lastOutputDir = settings->value("mainwindow/lastOutputDir").toString(); QString defEngine; if (findProgram("tesseract")) defEngine = "tesseract"; else defEngine = "cuneiform"; QString engine = settings->value("ocr/engine", QVariant(defEngine)).toString(); if (engine == "cuneiform") selectedEngine = UseCuneiform; else selectedEngine = UseTesseract; language = settings->value("ocr/language", selectDefaultLanguageName()).toString(); //selectLangsBox->setCurrentIndex(selectLangsBox->findData(QVariant(language))); outputFormat = settings->value("ocr/outputFormat", QString("text")).toString(); if (outputFormat == "") outputFormat = "text"; checkSpelling = settings->value("mainWindow/checkSpelling", bool(true)).toBool(); bool ok; fontSize = settings->value("mainwindow/fontSize", int(12)).toInt(&ok); noLocale = settings->value("mainwindow/nolocale", QVariant(false)).toBool(); RussianLocale = settings->value("mainwindow/rulocale", QVariant(false)).toBool(); findTessDataPath(); tessdataPath = settings->value("ocr/tessData", QVariant(tessdataPath)).toString(); if (tessdataPath.isEmpty()) findTessDataPath(); languages = settings->value("ocr/selectedLanguages", QStringList(language)).toStringList(); cropLoaded = settings->value("processing/crop1", QVariant(true)).toBool(); autoDeskew = settings->value("processing/deskew", QVariant(true)).toBool(); preprocess = settings->value("processing/preprocess", QVariant(true)).toBool(); size = settings->value("mainwindow/size", QSize(800, 600)).toSize(); iconSize = settings->value("mainwindow/iconSize", QSize(24, 24)).toSize(); position = settings->value("mainwindow/pos", QPoint(0, 0)).toPoint(); fullScreen = settings->value("mainwindow/fullScreen", QVariant(false)).toBool(); darkBackgroundThreshold = settings->value("tweaks/darkBackgroundThreshold", QVariant(198)).toInt(); foregroundBrightenFactor = settings->value("tweaks/foregroundBrightenFactor", QVariant(32)).toInt(); globalBrightenFactor = settings->value("tweaks/globalBrightenFactor", QVariant(32)).toInt(); globalDarkenFactor = settings->value("tweaks/globalDarkenFactor", QVariant(32)).toInt(); globalDarkenThreshold = settings->value("tweaks/globalDarkenThreshold", QVariant(190)).toInt(); uSeed = settings->value("tweaks/seed", QVariant(1)).toInt(); tiffPS = settings->value("tweaks/tiffPageSize", QVariant(4000)).toString(); tiffD = settings->value("tweaks/tiffDensity", QVariant(300)).toString(); } bool Settings::firstRun() { return fr; } void Settings::writeSettings() { settings->setValue("program/version", QString::fromUtf8("0.9.3")); settings->setValue("mainwindow/size", size); settings->setValue("mainwindow/iconSize", iconSize); settings->setValue("mainwindow/pos", position); settings->setValue("mainwindow/fullScreen", fullScreen); settings->setValue("mainwindow/lastDir", lastDir); settings->setValue("mainWindow/checkSpelling", checkSpelling); settings->setValue("mainwindow/lastOutputDir", lastOutputDir); settings->setValue("mainwindow/fontSize", fontSize); settings->setValue("mainwindow/nolocale", noLocale); settings->setValue("mainwindow/rulocale", RussianLocale); settings->setValue("ocr/language", language); settings->setValue("ocr/selectedLanguages", languages); //settings->setValue("ocr/singleColumn", singleColumn); settings->setValue("ocr/outputFormat", outputFormat); QString engine = selectedEngine == UseCuneiform ? QString("cuneiform") : QString("tesseract"); settings->setValue("ocr/engine", engine); settings->setValue("ocr/tessData", tessdataPath); settings->setValue("processing/crop1", cropLoaded); settings->setValue("processing/deskew", autoDeskew); settings->setValue("processing/preprocess", preprocess); settings->setValue("tweaks/darkBackgroundThreshold", darkBackgroundThreshold); settings->setValue("tweaks/foregroundBrightenFactor", foregroundBrightenFactor); settings->setValue("tweaks/globalBrightenFactor", globalBrightenFactor); settings->setValue("tweaks/globalDarkenFactor", globalDarkenFactor); settings->setValue("tweaks/globalDarkenThreshold", globalDarkenThreshold); settings->setValue("tweaks/tiffPageSize", tiffPS); settings->setValue("tweaks/tiffDensity", tiffD); settings->setValue("tweaks/seed", uSeed); settings->sync(); } QString Settings::getLanguage() { return language; } QString Settings::getOutputFormat() { return outputFormat; } QString Settings::getLastDir() { return lastDir; } QString Settings::getLastOutputDir() { return lastOutputDir; } bool Settings::getCheckSpelling() { return checkSpelling; } QString Settings::getTessdataPath() { if (!tessdataPath.endsWith("/")) tessdataPath = tessdataPath.append("/"); return tessdataPath; } SelectedEngine Settings::getSelectedEngine() { return selectedEngine; } QSize Settings::getSize() { return size; } QPoint Settings::getPosition() { return position; } bool Settings::getFullScreen() { return fullScreen; } int Settings::getFontSize() { return fontSize; } QString Settings::getFullLanguageName(const QString &abbr) { QMap * map; if (selectedEngine == UseCuneiform) map = &cuMap; if (selectedEngine == UseTesseract) map = &tesMap; return map->key(abbr, ""); } QString Settings::getFullLanguageName(const QString &abbr, const QString &engine) { QMap * map; if (engine == "cuneiform") map = &cuMap; if (engine == "tesseract") map = &tesMap; return map->key(abbr, ""); } QString Settings::getShortLanguageName(const QString &lang) { QMap * map; if (selectedEngine == UseCuneiform) map = &cuMap; if (selectedEngine == UseTesseract) map = &tesMap; return map->value(lang, ""); } QString Settings::getShortLanguageName(const QString &lang, const QString &engine) { QMap * map; if (engine == "tesseract") map = &tesMap; if (engine == "cuneiform") map = &cuMap; return map->value(lang, ""); } bool Settings::getAutoDeskew() { return autoDeskew; } bool Settings::getCropLoaded() { return cropLoaded; } bool Settings::getPreprocessed() { return preprocess; } void Settings::setLanguage(const QString &value) { language = value; } void Settings::setOutputFormat(const QString &value) { outputFormat = value; } void Settings::setLastDir(const QString &value) { lastDir = value; } void Settings::setLastOutputDir(const QString &value) { lastOutputDir = value; } void Settings::setCheckSpelling(const bool value) { checkSpelling = value; } void Settings::setTessdataPath(const QString &value) { tessdataPath = value; } void Settings::setSelectedEngine(const SelectedEngine value) { selectedEngine = value; } void Settings::setSize(const QSize &value) { size = value; } void Settings::setPosition(const QPoint &value) { position = value; } void Settings::setFullScreen(const bool value) { fullScreen = value; } void Settings::setFontSize(const int &value) { fontSize = value; } void Settings::setCropLoaded(const bool value) { cropLoaded = value; } void Settings::setAutoDeskew(const bool value) { autoDeskew = value; } void Settings::setPreprocessed(const bool value) { preprocess = value; } QString Settings::uniqueSeed() { return QString::number(uSeed++); } QString Settings::tiffPageSize() { return tiffPS; } QString Settings::tiffDensity() { return tiffD; } int Settings::getDarkBackgroundThreshold() { return darkBackgroundThreshold; } int Settings::getForegroundBrightenFactor() { return foregroundBrightenFactor; } int Settings::getGlobalBrightenFactor() { return globalBrightenFactor; } int Settings::getGlobalDarkenFactor() { return globalDarkenFactor; } int Settings::getGlobalDarkenThreshold() { return globalDarkenThreshold; } QStringList Settings::fullLanguageNames() { QStringList res = tesMap.keys(); res << QObject::trUtf8("Russian-English"); return res; } QStringList Settings::getSelectedLanguages() { QStringList res; foreach (QString s, languages) { QString l = getFullLanguageName(s, "tesseract"); if (l!="") res.append(l); l = getFullLanguageName(s, "cuneiform"); if (l!="") res.append(l); } res.removeDuplicates(); return res; } QStringList Settings::selectedLanguagesAvailableTo(const QString &engine) { QStringList res; if (engine == "cuneiform") { foreach(QString s, languages) { if (cuMap.values().contains(s)) res.append(cuMap.key(s, "")); } } if (engine == "tesseract") { foreach(QString s, languages) { if (tesMap.values().contains(s)) res.append(tesMap.key(s, "")); } } return res; } QStringList Settings::languagesAvailableTo(const QString &engine) { if (engine == "cuneiform") return cuMap.keys(); if (engine == "tesseract") return tesMap.keys(); return QStringList(); } QStringList Settings::installedTesseractLanguages() { QString tessPath = tessdataPath + "/tessdata/"; QDir d(tessPath); QStringList res; QStringList sl = d.entryList(QStringList("*.traineddata")); foreach (QString s, tesMap.values()) { foreach(QString s1, sl) { if (s1.startsWith(s)) res.append(tesMap.key(s, "")); } } res.removeDuplicates(); return res; } void Settings::setSelectedLanguages(const QStringList &value) { languages.clear(); foreach (QString s, value) { QString l = getShortLanguageName(s, "tesseract"); if (l!="") languages.append(l); l =getShortLanguageName(s, "cuneiform"); if (l!="") languages.append(l); } languages.removeDuplicates(); } QString Settings::workingDir() { QString wDir = QDir::homePath(); if (!wDir.endsWith("/")) wDir += '/'; QDir d(wDir + ".config"); if (d.exists()) wDir += ".config/"; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); wDir = env.value("XDG_CONFIG_HOME", wDir); if (!wDir.endsWith("/")) wDir += '/'; wDir += "yagf/"; QDir dir(wDir); if (!dir.exists()) { dir.mkdir(wDir); fr = true; } else fr= false; return wDir; } void Settings::startLangPair() { lpi = 0; } bool Settings::getLangPair(QString &full, QString &abbr, bool forceTesseract) { QMap * map; if (selectedEngine == UseCuneiform) map = &cuMap; if (selectedEngine == UseTesseract) map = &tesMap; if (forceTesseract) map = &tesMap; if (lpi < map->count()) { full = map->keys().at(lpi); abbr = map->value(full); lpi++; return true; } return false; } void Settings::setProjectDir(const QString &dir) { projectDir = dir; } QString Settings::getProjectDir() { return projectDir; } void Settings::makeLanguageMaps() { cuMap.insert(QObject::trUtf8("Bulgarian"), "bul"); cuMap.insert(QObject::trUtf8("Czech"), "cze"); cuMap.insert(QObject::trUtf8("Danish"), "dan"); cuMap.insert(QObject::trUtf8("Dutch"), "dut"); cuMap.insert(QObject::trUtf8("English"), "eng"); cuMap.insert(QObject::trUtf8("French"), "fra"); cuMap.insert(QObject::trUtf8("German"), "ger"); cuMap.insert(QObject::trUtf8("Hungarian"), "hun"); cuMap.insert(QObject::trUtf8("Italian"), "ita"); cuMap.insert(QObject::trUtf8("Latvian"), "lav"); cuMap.insert(QObject::trUtf8("Lithuanian"), "lit"); cuMap.insert(QObject::trUtf8("Polish"), "pol"); cuMap.insert(QObject::trUtf8("Portuguese"), "por"); cuMap.insert(QObject::trUtf8("Romanian"), "rum"); cuMap.insert(QObject::trUtf8("Russian"), "rus"); cuMap.insert(QObject::trUtf8("Russian-English"), "ruseng"); cuMap.insert(QObject::trUtf8("Spanish"), "spa"); cuMap.insert(QObject::trUtf8("Serbian"), "srp"); cuMap.insert(QObject::trUtf8("Slovenian"), "slo"); cuMap.insert(QObject::trUtf8("Swedish"), "swe"); cuMap.insert(QObject::trUtf8("Ukrainian"), "ukr"); tesMap.insert(QObject::trUtf8("Albanian"), "sqi"); tesMap.insert(QObject::trUtf8("Ancient Greek"), "grc"); tesMap.insert(QObject::trUtf8("Azerbaijani"), "aze"); tesMap.insert(QObject::trUtf8("Bulgarian"), "bul"); tesMap.insert(QObject::trUtf8("Czech"), "ces"); tesMap.insert(QObject::trUtf8("Croatian"), "hrv"); tesMap.insert(QObject::trUtf8("Danish"), "dan"); tesMap.insert(QObject::trUtf8("Danish Gothic"), "dan-frak"); tesMap.insert(QObject::trUtf8("Dutch"), "nld"); tesMap.insert(QObject::trUtf8("English"), "eng"); tesMap.insert(QObject::trUtf8("Estonian"), "est"); tesMap.insert(QObject::trUtf8("Finnish"), "fin"); tesMap.insert(QObject::trUtf8("French"), "fra"); tesMap.insert(QObject::trUtf8("German"), "deu"); tesMap.insert(QObject::trUtf8("German Gothic"), "deu-frak"); tesMap.insert(QObject::trUtf8("Greek"), "ell"); tesMap.insert(QObject::trUtf8("Hebrew"), "heb"); tesMap.insert(QObject::trUtf8("Hungarian"), "hun"); tesMap.insert(QObject::trUtf8("Icelandic"), "isl"); tesMap.insert(QObject::trUtf8("Italian"), "ita"); tesMap.insert(QObject::trUtf8("Latvian"), "lav"); tesMap.insert(QObject::trUtf8("Lithuanian"), "lit"); tesMap.insert(QObject::trUtf8("Macedonian"), "mkd"); tesMap.insert(QObject::trUtf8("Middle English"), "enm"); tesMap.insert(QObject::trUtf8("Middle French"), "frm"); tesMap.insert(QObject::trUtf8("Norwegian"), "nor"); tesMap.insert(QObject::trUtf8("Polish"), "pol"); tesMap.insert(QObject::trUtf8("Portuguese"), "por"); tesMap.insert(QObject::trUtf8("Romanian"), "ron"); tesMap.insert(QObject::trUtf8("Russian"), "rus"); tesMap.insert(QObject::trUtf8("Serbian"), "srp"); tesMap.insert(QObject::trUtf8("Slovenian"), "slv"); tesMap.insert(QObject::trUtf8("Slovakian"), "slk"); tesMap.insert(QObject::trUtf8("Slovakian Gothic"), "slk-frak"); tesMap.insert(QObject::trUtf8("Spanish"), "spa"); tesMap.insert(QObject::trUtf8("Swedish"), "swe"); tesMap.insert(QObject::trUtf8("Swedish Gothic"), "swe-frak"); tesMap.insert(QObject::trUtf8("Turkish"), "tur"); tesMap.insert(QObject::trUtf8("Ukrainian"), "ukr"); } void Settings::findTessDataPath() { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); if (env.contains("TESSDATA_PREFIX")) { tessdataPath = env.value("TESSDATA_PREFIX"); return; } QDir dir; dir.setPath("/usr/share/tessdata"); if (dir.exists()) { tessdataPath = "/usr/share/"; return; } dir.setPath("/usr/local/share/tessdata"); if (dir.exists()) { tessdataPath = "/usr/local/share/"; return; } dir.setPath("/usr/local/share/tesseract-ocr/tessdata"); if (dir.exists()) { tessdataPath = "/usr/local/share/tesseract-ocr/"; return; } dir.setPath("/usr/share/tesseract-ocr/tessdata"); if (dir.exists()) { tessdataPath = "/usr/share/tesseract-ocr/"; return; } tessdataPath.clear(); return; } QString Settings::selectDefaultLanguageName() { QLocale loc = QLocale::system(); QString name = ""; QMap * map; if (selectedEngine == UseCuneiform) map = &cuMap; if (selectedEngine == UseTesseract) map = &tesMap; switch (loc.language()) { case QLocale::Bulgarian: name = map->value("Bulgarian"); break; case QLocale::Czech: name = map->value("Czech"); break; case QLocale::Danish: name = map->value("Danish"); break; case QLocale::German: name = map->value("German"); break; case QLocale::Dutch: name = map->value("Dutch"); break; case QLocale::Russian: name = map->value("Russian"); break; case QLocale::English: name = "eng"; break; case QLocale::Spanish: name = map->value("Spanish"); break; case QLocale::French: name = map->value("French"); break; case QLocale::Hungarian: name = map->value("Hungarian"); break; case QLocale::Italian: name = map->value("Italian"); break; case QLocale::Latvian: name = map->value("Latvian"); break; case QLocale::Lithuanian: name = map->value("Lithuanian"); break; case QLocale::Polish: name = map->value("Polish"); break; case QLocale::Portuguese: name = map->value("Portuguese"); break; case QLocale::Romanian: name = map->value("Romanian"); break; case QLocale::Swedish: name = map->value("Swedish"); break; case QLocale::Serbian: name = map->value("Serbian"); break; case QLocale::Slovenian: name = map->value("Slovenian"); break; case QLocale::Slovak: name = map->value("Slovak", "eng"); break; case QLocale::Ukrainian: name = map->value("Ukrainian"); break; case QLocale::Finnish: name = map->value("Finnish", "eng"); break; case QLocale::Greek: name = map->value("Greek", "eng"); break; case QLocale::Hebrew: name = map->value("Hebrew", "eng"); break; case QLocale::Norwegian: name = map->value("Norwegian", "eng"); break; case QLocale::Turkish: name = map->value("Turkish", "eng"); break; default: name = "eng"; break; } if (name == "") name = "eng"; return name; } QSize Settings::getIconSize() { return iconSize; } void Settings::setIconSize(const QSize &value) { iconSize = value; } bool Settings::useNoLocale() { return noLocale; } bool Settings::useRussianLocale() { return RussianLocale; } void Settings::setNoLocale(bool value) { noLocale = value; } void Settings::setRussianLocale(bool value) { RussianLocale = value; } yagf-0.9.3.2/src/sidebar.cpp0000664000175000017500000001206402263216100016232 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "sidebar.h" #include "qsnippet.h" #include #include #include #include #include #include #include #include SideBar::SideBar(QWidget *parent) : QListWidget(parent) { //setDragDropOverwriteMode(true); current = 0; setMaximumWidth(120); setMinimumWidth(120); connect(this, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(itemActive(QListWidgetItem*,QListWidgetItem*))); setToolTip(trUtf8("Drop files here")); setAcceptDrops(true); setDropIndicatorShown(true); lock = false; dragging = false; } void SideBar::addItem(QSnippet *item) { setAlternatingRowColors(false); QSize size= item->sizeHint(); size.setWidth(width()); if(item->imageWidth() == 0) return; int h = item->imageHeight()*100/item->imageWidth(); if (h > width()+2) h = width()+2; size.setHeight(item->imageHeight() < width() ? item->imageHeight() + 2 : h + 2); item->setSizeHint(size); QListWidget::addItem(item); setCurrentItem(item); } void SideBar::itemActive(QListWidgetItem *item, QListWidgetItem *item2) { if (lock) return; lock = true; if (item) { emit pageSelected(((QSnippet *)item)->pageID()); current = ((QSnippet *)item); } else current = 0; lock = false; } /*void SideBar::dragEnterEvent(QDragEnterEvent *event) { if (!event->mimeData()->hasUrls()) { setCursor(Qt::ForbiddenCursor); event->ignore(); // >mimeData()->setData("application/x-qabstractitemmodeldatalist", QByteArray()); } else { setCursor(Qt::DragCopyCursor); event->accept(); } QListWidget::dragEnterEvent(event); }*/ QStringList SideBar::mimeTypes() const { QStringList qstrList; qstrList.append("text/uri-list"); return qstrList; } Qt::DropActions SideBar::supportedDropActions() const { if (dragging) return 0; return Qt::CopyAction | Qt::MoveAction; } bool SideBar::dropMimeData(int index, const QMimeData *data, Qt::DropAction action) { // if (action == Qt::MoveAction) // return false; QList urlList; urlList = data->urls(); // retrieve list of urls QStringList files; foreach(QUrl url, urlList) // iterate over list { files.append(url.toLocalFile()); ++index; // increment index to preserve drop order } emit filesDropped(files); return true; } void SideBar::startDrag(Qt::DropActions supportedActions) { dragging = true; supportedActions |= Qt::MoveAction; QDrag * drag = new QDrag(this); QMimeData *mimeData = new QMimeData(); QList urlList; QStringList sl; foreach(QListWidgetItem * lwi, selectedItems()) { QString s = QString::number(((QSnippet *)lwi)->pageID()); sl << s; } mimeData->setUrls(urlList); drag->setMimeData(mimeData); if (drag->exec(supportedActions,Qt::CopyAction) == Qt::CopyAction) { foreach(QListWidgetItem * lwi, selectedItems()) { emit fileRemoved(((QSnippet *) lwi)->pageID()); model()->removeRow(row(lwi)); } } current = 0; dragging = false; } QSnippet * SideBar::getItemByName(const QString &name) { for (int i = 0; i < count(); i++) { if (((QSnippet *)item(i))->getName() == name) return ((QSnippet *)item(i)); } return NULL; } void SideBar::select(const QString &name) { current = getItemByName(name); if (current) current->setSelected(true); } void SideBar::selectFirstFile() { if (count() == 0) { current = NULL; return; } current = (QSnippet *) item(0); } /*void SideBar::dragLeaveEvent(QDragLeaveEvent *event) { setCursor(Qt::ArrowCursor); event->accept(); //QListWidget::dragLeaveEvent(event); } void SideBar::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls()) { QList ul = event->mimeData()->urls(); QList::Iterator i; for (i = ul.begin(); i != ul.end(); i++) { QUrl url = *i; this->addFile(url.toLocalFile()); } } setCursor(Qt::ArrowCursor); event->accept(); //QListWidget::dropEvent(event); }*/ yagf-0.9.3.2/src/scanner.h0000664000175000017500000000360502263216100015720 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SCANNER_H #define SCANNER_H #include #include #include class ScannerBase : public QObject { Q_OBJECT public: explicit ScannerBase(QObject *parent = 0); ~ScannerBase(); void addParameter(const QString &s); void addEnvironmentVar(const QString &s); void setOutputFile(const QString &s); QString programName(); virtual void exec() = 0; signals: void processFinished(); public slots: private slots: void finished( int, QProcess::ExitStatus ); protected: void waitFor(); void execInternal(const QString &s); void setProgramName(const QString &s); void setPreloadLibrary(const QString &s); private: QProcess scanProcess; QStringList parameters; QStringList environment; QString outputFile; QString pName; QString preloadLibrary; }; class ScannerFactory { public: ScannerFactory(); QStringList frontEnds(); ScannerBase * createScannerFE(const QString &name); private: QString findPreloadLibrary(); void findFEs(); private: QString preloadPath; QStringList fes; }; #endif // SCANNERBASE_H yagf-0.9.3.2/src/sidebar.h0000664000175000017500000000317402263216100015701 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SIDEBAR_H #define SIDEBAR_H #include class QSnippet; class SideBar : public QListWidget { Q_OBJECT public: explicit SideBar(QWidget *parent = 0); void addItem ( QSnippet * item ); void clearBlocks(); void select(const QString &name); void selectFirstFile(); signals: void pageSelected(int id); void filesDropped(QStringList); void fileRemoved(int id); private slots: void itemActive( QListWidgetItem * item, QListWidgetItem *item2 ); protected: QStringList mimeTypes () const; Qt::DropActions supportedDropActions () const; bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action); void startDrag(Qt::DropActions supportedActions); private: QSnippet * getItemByName(const QString &name); QSnippet * current; bool lock; bool dragging; }; #endif // SIDEBAR_H yagf-0.9.3.2/src/settings.h0000664000175000017500000000762002263216100016130 0ustar parallelsparallels/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #ifndef _SETTINGS_H_ #define _SETTINGS_H_ #include #include #include #include #include #include #include enum SelectedEngine { UseCuneiform, UseTesseract }; class Settings { public: static Settings * instance(); void readSettings(const QString &path); bool firstRun(); void writeSettings(); QString getLanguage(); bool useNoLocale(); bool useRussianLocale(); void setNoLocale(bool value); void setRussianLocale(bool value); QString getOutputFormat(); QString getLastDir(); QString getLastOutputDir(); bool getCheckSpelling(); QString getTessdataPath(); SelectedEngine getSelectedEngine(); QSize getSize(); QSize getIconSize(); QPoint getPosition(); bool getFullScreen(); int getFontSize(); QString getFullLanguageName(const QString &abbr); QString getFullLanguageName(const QString &abbr, const QString &engine); QString getShortLanguageName(const QString &lang); QString getShortLanguageName(const QString &lang, const QString &engine); bool getAutoDeskew(); bool getCropLoaded(); bool getPreprocessed(); void setLanguage(const QString &value); void setOutputFormat(const QString &value); void setLastDir(const QString &value); void setLastOutputDir(const QString &value); void setCheckSpelling(const bool value); void setTessdataPath(const QString &value); void setSelectedEngine(const SelectedEngine value); void setSize(const QSize &value); void setIconSize(const QSize &value); void setPosition(const QPoint &value); void setFullScreen(const bool value); void setFontSize(const int &value); void setCropLoaded(const bool value); void setAutoDeskew(const bool value); void setPreprocessed(const bool value); QString uniqueSeed(); QString tiffPageSize(); QString tiffDensity(); int getDarkBackgroundThreshold(); int getForegroundBrightenFactor(); int getGlobalBrightenFactor(); int getGlobalDarkenFactor(); int getGlobalDarkenThreshold(); QStringList fullLanguageNames(); QStringList getSelectedLanguages(); QStringList selectedLanguagesAvailableTo(const QString &engine); QStringList languagesAvailableTo(const QString &engine); QStringList installedTesseractLanguages(); void setSelectedLanguages(const QStringList &value); QString workingDir(); void startLangPair(); bool getLangPair(QString &full, QString &abbr, bool forceTesseract = false); void setProjectDir(const QString &dir); QString getProjectDir(); void makeLanguageMaps(); private: void findTessDataPath(); QString selectDefaultLanguageName(); Settings(); Settings(const Settings &); ~Settings(); private: QString language; QString outputFormat; QString lastDir; QString lastOutputDir; QString projectDir; QString version; bool checkSpelling; QString tessdataPath; SelectedEngine selectedEngine; QSize size; QSize iconSize; QPoint position; bool fullScreen; int fontSize; bool cropLoaded; bool preprocess; bool fr; QMap cuMap; QMap tesMap; int lpi; bool noLocale; bool RussianLocale; bool autoDeskew; int darkBackgroundThreshold; int globalBrightenFactor; int foregroundBrightenFactor; int globalDarkenFactor; int globalDarkenThreshold; int uSeed; QStringList languages; QString mPath; QSettings * settings; static Settings * m_instance; QString tiffPS; QString tiffD; }; #endif yagf-0.9.3.2/src/spellchecker.h0000664000175000017500000000363102263216100016732 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SPELLCHECKER_H #define SPELLCHECKER_H #include #include #include typedef QMap StringMap; class QTextEdit; class QRegExp; class QTextCursor; class QStringList; class SpellChecker { public: SpellChecker(QTextEdit *textEdit); ~SpellChecker(); void unSpellCheck(); void setLanguage(const QString &lang); bool spellCheck(); //Returns false only if the dictionary not found. Otherwise always true. void checkWord(); bool hasHyphen(QTextCursor *cursor); bool hasLongHyphen(QTextCursor *cursor); QString checkConcatenation(QTextCursor *cursor); void enumerateDicts(); bool hasDict(const QString &shname); QStringList suggestions(); private: void _checkWord(QTextCursor *cursor); bool checkWordSpelling(const QString &word); QTextEdit *m_textEdit; QRegExp *m_regExp; QString m_lang1; QString m_lang2; StringMap *m_map; AspellConfig *spell_config1; AspellConfig *spell_config2; AspellSpeller *spell_checker1; AspellSpeller *spell_checker2; QStringList *dictList; QString bad_language; }; #endif // SPELLCHECKER_H yagf-0.9.3.2/src/tblock.cpp0000664000175000017500000000300002263216100016065 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "tblock.h" Block::Block(int x, int y, int width, int height) : QRect(x, y, width, height) { number = -1; language = "default"; } Block::Block(const QRect &r) : QRect(r.x(), r.y(), r.width(), r.height()) { number = -1; } int Block::blockNumber() { return number; } void Block::setBlockNumber(const int value) { number = value; } void Block::setLanguage(const QString &lang) { language = lang; } QString Block::getLanguage() { return language; } bool rectLessThan(const QRect &r1, const QRect &r2) { if (r1.y() < r2.y()) return true; if (r1.x() < r2.x()) return true; return false; } void sortBlocks(TBlocks &blocks) { qSort(blocks.begin(), blocks.end(), rectLessThan); } yagf-0.9.3.2/src/tblock.h0000664000175000017500000000237102263216100015544 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TBLOCK_H #define TBLOCK_H #include #include #include class Block : public QRect { public: explicit Block( int x, int y, int width, int height ); Block(const QRect &r); int blockNumber(); void setBlockNumber(const int value); void setLanguage(const QString &lang); QString getLanguage(); private: int number; QString language; }; typedef QList TBlocks; void sortBlocks(TBlocks &blocks); #endif // TBLOCK_H yagf-0.9.3.2/src/texteditor.cpp0000664000175000017500000001755002263216100017021 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "texteditor.h" #include "settings.h" #include "ycommon.h" #include "utils.h" #include #include #include #include #include #include #include #include TextEditor::TextEditor(QWidget *parent) : QTextEdit(parent), spellChecker(this) { hasCopy = false; mTextSaved = true; setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(document(), SIGNAL(cursorPositionChanged(const QTextCursor &)), this, SLOT(updateSP())); connect(this, SIGNAL(copyAvailable(bool)), this, SLOT(copyAvailable(bool))); connect(this, SIGNAL(textChanged()), this, SLOT(textChanged())); } TextEditor::~TextEditor() { } bool TextEditor::textSaved() { return mTextSaved; } bool TextEditor::spellCheck(const QString &lang) { spellChecker.setLanguage(lang); return spellChecker.spellCheck(); } void TextEditor::unSpellCheck() { spellChecker.unSpellCheck(); } void TextEditor::enumerateDicts() { spellChecker.enumerateDicts(); } bool TextEditor::hasDict(const QString &shname) { return spellChecker.hasDict(shname); } void TextEditor::saveText() { Settings * settings = Settings::instance(); QString filter; if (settings->getOutputFormat() == "text") filter = trUtf8("Text Files (*.txt)"); else filter = trUtf8("HTML Files (*.html)"); QFileDialog dialog(this, trUtf8("Save Text"), settings->getLastOutputDir(), filter); if (settings->getOutputFormat() == "text") dialog.setDefaultSuffix("txt"); else dialog.setDefaultSuffix("html"); dialog.setAcceptMode(QFileDialog::AcceptSave); if (dialog.exec()) { QStringList fileNames; fileNames = dialog.selectedFiles(); settings->setLastOutputDir(dialog.directory().path()); QFile textFile(fileNames.at(0)); textFile.open(QIODevice::ReadWrite | QIODevice::Truncate); if (settings->getOutputFormat() == "text") textFile.write(toPlainText().toUtf8()); else saveHtml(&textFile); textFile.close(); mTextSaved = true; } } void TextEditor::keyPressEvent(QKeyEvent *e) { if (e->modifiers() & Qt::ControlModifier) { if ((e->key() == Qt::Key_Plus) || (e->key() == Qt::Key_Equal)) { enlargeFont(); e->accept(); return; } else if (e->key() == Qt::Key_Minus) { decreaseFont(); e->accept(); return; } } QTextEdit::keyPressEvent(e); } void TextEditor::wheelEvent(QWheelEvent *e) { if (e->modifiers() & Qt::ControlModifier) { if (e->delta() > 0) enlargeFont(); else decreaseFont(); e->accept(); return; } QTextEdit::wheelEvent(e); } void TextEditor::replaceWord() { QAction * action = (QAction *) sender(); QTextCursor cursor = textCursor(); cursor.select(QTextCursor::WordUnderCursor); cursor.removeSelectedText(); cursor.insertText(action->text()); } void TextEditor::copyAvailable(bool yes) { hasCopy = yes; } void TextEditor::textChanged() { mTextSaved = !(toPlainText().count()); Settings * settings = Settings::instance(); QFont f(font()); f.setPointSize(settings->getFontSize()); setFont(f); } void TextEditor::copyClipboard() { if (!hasCopy) { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(toPlainText(), QClipboard::Clipboard); } else copy(); } void TextEditor::saveHtml(QFile *file) { QString text = document()->toHtml().toUtf8(); QString newDir = extractFilePath(file->fileName()) + extractFileName(file->fileName()) + ".files"; text.replace("", ""); /*text.replace(workingDir + "output_files", newDir); text.replace("[img src=", ""); text.replace(".bmp]", ".bmp>"); QDir dir(workingDir+"output_files"); dir.rename(workingDir+"output_files", newDir);*/ file->write(text.toAscii()); } void TextEditor::contextMenuRequested(const QPoint &point) { QAction *action; QMenu * menu = new QMenu(this); QStringList sl = spellChecker.suggestions(); //if (sl.count() == 0) { action = new QAction(trUtf8("Undo\tCtrl+Z"), this); action->setShortcut(QKeySequence("Ctrl+Z")); connect(action, SIGNAL(triggered()), this, SLOT(undo())); menu->addAction(action); action = new QAction(trUtf8("Redo\tCtrl+Shift+Z"), this); action->setShortcut(QKeySequence("Ctrl+Shift+Z")); connect(action, SIGNAL(triggered()), this, SLOT(redo())); menu->addAction(action); action = new QAction("separator", this); action->setText(""); action->setSeparator(true); menu->addAction(action); action = new QAction(trUtf8("Select All\tCtrl+A"), this); action->setShortcut(QKeySequence("Ctrl+A")); connect(action, SIGNAL(triggered()), this, SLOT(selectAll())); menu->addAction(action); action = new QAction(trUtf8("Cut\tCtrl+X"), this); action->setShortcut(QKeySequence("Ctrl+X")); connect(action, SIGNAL(triggered()), this, SLOT(cut())); menu->addAction(action); action = new QAction(trUtf8("Copy\tCtrl+C"), this); action->setShortcut(QKeySequence("Ctrl+C")); connect(action, SIGNAL(triggered()), this, SLOT(copyClipboard())); menu->addAction(action); action = new QAction(trUtf8("Paste\tCtrl+V"), this); action->setShortcut(QKeySequence("Ctrl+V")); connect(action, SIGNAL(triggered()), this, SLOT(paste())); menu->addAction(action); action = new QAction("separator", this); action->setText(""); action->setSeparator(true); menu->addAction(action); action = new QAction(trUtf8("Larger Font\tCtrl++"), this); connect(action, SIGNAL(triggered()), this, SLOT(enlargeFont())); menu->addAction(action); action = new QAction(trUtf8("Smaller Font\tCtrl+-"), this); connect(action, SIGNAL(triggered()), this, SLOT(decreaseFont())); menu->addAction(action); //} if (sl.count() > 0) menu->addSeparator(); foreach(QString str, sl) { QAction * action = menu->addAction(str); connect(action, SIGNAL(triggered()), this, SLOT(replaceWord())); } menu->exec(mapToGlobal(point)); delete menu; } void TextEditor::enlargeFont() { Settings * settings = Settings::instance(); int fontSize = font().pointSize(); fontSize++; QFont f(font()); f.setPointSize(fontSize); setFont(f); settings->setFontSize(fontSize); } void TextEditor::decreaseFont() { Settings * settings = Settings::instance(); int fontSize = font().pointSize(); if (fontSize > 1) fontSize--; QFont f(font()); f.setPointSize(fontSize); setFont(f); settings->setFontSize(fontSize); } void TextEditor::updateSP() { Settings * settings = Settings::instance(); if (settings->getCheckSpelling()) spellChecker.checkWord(); } yagf-0.9.3.2/src/spellchecker.cpp0000664000175000017500000002770502263216100017275 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include "spellchecker.h" SpellChecker::SpellChecker(QTextEdit *textEdit): m_textEdit(textEdit) { m_regExp = new QRegExp("[^\\s]*"); //m_cursor= new QTextCursor(m_textEdit->document()); spell_config1 = new_aspell_config(); spell_config2 = new_aspell_config(); aspell_config_replace(spell_config1, "lang", m_lang1.toAscii()); aspell_config_replace(spell_config2, "lang", m_lang2.toAscii()); aspell_config_replace(spell_config1, "encoding", "utf-8"); aspell_config_replace(spell_config2, "encoding", "utf-8"); m_map = new StringMap(); m_map->insert("ruseng", "ru"); m_map->insert("rus", "ru"); m_map->insert("bul", "bg"); m_map->insert("cze", "cs"); m_map->insert("dan", "da"); m_map->insert("dut", "nl"); m_map->insert("nld", "nl"); m_map->insert("ell", "el"); m_map->insert("eng", "en"); m_map->insert("est", "et"); m_map->insert("fin", "fi"); m_map->insert("fra", "fr"); m_map->insert("ger", "de"); m_map->insert("deu", "de"); m_map->insert("deu-frak", "de-alt"); m_map->insert("heb", "he"); m_map->insert("hrv", "hr"); m_map->insert("hun", "hu"); m_map->insert("isl", "is"); m_map->insert("ita", "it"); m_map->insert("lav", "lv"); m_map->insert("lit", "lt"); m_map->insert("nor", "no"); m_map->insert("pol", "pl"); m_map->insert("por", "pt_PT"); m_map->insert("rum", "ro"); //m_map->insert("rus", "ru"); m_map->insert("ron", "ro"); m_map->insert("slo", "sl"); m_map->insert("slk", "sk"); m_map->insert("spa", "es"); m_map->insert("srp", "sr"); m_map->insert("swe", "sv"); m_map->insert("swef", "sv"); m_map->insert("tur", "tr"); m_map->insert("ukr", "uk"); spell_checker1 = 0; spell_checker2 = 0; setLanguage("ruseng"); dictList = new QStringList(); } void SpellChecker::enumerateDicts() { AspellConfig *config; AspellDictInfoList *dlist; AspellDictInfoEnumeration *dels; const AspellDictInfo *entry; config = new_aspell_config(); /* the returned pointer should _not_ need to be deleted */ dlist = get_aspell_dict_info_list(config); /* config is no longer needed */ delete_aspell_config(config); dels = aspell_dict_info_list_elements(dlist); while ((entry = aspell_dict_info_enumeration_next(dels)) != 0) { dictList->append(entry->code); } delete_aspell_dict_info_enumeration(dels); } bool SpellChecker::hasDict(const QString &shname) { return dictList->contains(m_map->value(shname)); } SpellChecker::~SpellChecker() { delete m_regExp; delete m_map; delete_aspell_speller(spell_checker1); delete_aspell_speller(spell_checker2); delete_aspell_config(spell_config1); delete_aspell_config(spell_config2); delete dictList; } void SpellChecker::setLanguage(const QString &lang) { if (lang.isEmpty()) return; delete_aspell_speller(spell_checker1); delete_aspell_speller(spell_checker2); bad_language.clear(); m_lang2 = "en"; m_lang1 = m_map->value(lang, QString("en")); if (lang == "rus_fra") { m_lang1 = "ru"; m_lang2 = "fr"; } else if (lang == "rus_ger") { m_lang1 = "ru"; m_lang2 = "de"; } else if (lang == "rus_spa") { m_lang1 = "ru"; m_lang2 = "es"; } if ((lang == "deu")||(lang == "ger")) { m_lang1 = "de_DE"; m_lang2 = "de_AT"; } if (lang == "ruseng") { m_lang1 = "ru"; m_lang2 = "en"; } aspell_config_replace(spell_config1, "lang", m_lang1.toAscii()); aspell_config_replace(spell_config2, "lang", m_lang2.toAscii()); AspellCanHaveError *possible_err = new_aspell_speller(spell_config1); spell_checker1 = 0; if (aspell_error_number(possible_err) == 0) spell_checker1 = to_aspell_speller(possible_err); else delete_aspell_can_have_error(possible_err); possible_err = new_aspell_speller(spell_config2); spell_checker2 = 0; if (aspell_error_number(possible_err) == 0) spell_checker2 = to_aspell_speller(possible_err); else delete_aspell_can_have_error(possible_err); // Check absent dictionary if (spell_checker1 == 0) bad_language = m_lang1; if (spell_checker2 == 0) bad_language = m_lang2; } bool SpellChecker::spellCheck() { if ((spell_checker1 == 0) && (spell_checker2 == 0)) { QPixmap icon; icon.load(":/warning.png"); QMessageBox messageBox(QMessageBox::NoIcon, "YAGF", QObject::trUtf8("Required spelling dictionary (%1) is not found.\nSpell-checking is disabled.\nTry to install an appropriate aspell dictionary.").arg(bad_language), QMessageBox::Ok, 0); messageBox.setIconPixmap(icon); messageBox.exec(); return false; } QTextCursor cursor(m_textEdit->document()); while (!cursor.isNull() && !cursor.atEnd()) { if (hasLongHyphen(&cursor)) { cursor.select(QTextCursor::WordUnderCursor); QString word1 = cursor.selectedText(); word1.truncate(word1.length()-1); cursor.movePosition(QTextCursor::NextWord); cursor.select(QTextCursor::WordUnderCursor); QString word2 = cursor.selectedText(); word1 = word1 +word2; if (checkWordSpelling(word1)) { cursor.movePosition(QTextCursor::PreviousWord); cursor.select(QTextCursor::WordUnderCursor); cursor.removeSelectedText(); cursor.select(QTextCursor::WordUnderCursor); cursor.removeSelectedText(); cursor.insertText(word1); } } cursor.select(QTextCursor::WordUnderCursor); QString word = cursor.selectedText(); if (word == QString::fromUtf8("—")) { cursor.removeSelectedText(); cursor.insertText(QString::fromUtf8("—")); } if (hasHyphen(&cursor)) { QString cc = checkConcatenation(&cursor); if (!cc.isEmpty()) { cursor.movePosition(QTextCursor::PreviousWord); cursor.movePosition(QTextCursor::PreviousWord); cursor.select(QTextCursor::WordUnderCursor); cursor.removeSelectedText(); cursor.select(QTextCursor::WordUnderCursor); cursor.removeSelectedText(); cursor.movePosition(QTextCursor::NextWord); cursor.select(QTextCursor::WordUnderCursor); cursor.removeSelectedText(); cursor.insertText(cc); } } _checkWord(&cursor); QTextCursor oldc = cursor; if (!cursor.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor)) break; //cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); //cursor = m_textEdit->document()->find(*m_regExp, cursor); int oldpos = oldc.position(); int newpos = cursor.position(); if (abs(newpos - oldpos) < 3) cursor.setPosition(newpos + 1); } if (!cursor.isNull()) _checkWord(&cursor); return true; } void SpellChecker::unSpellCheck() { QTextCursor cursor(m_textEdit->document()); QTextCharFormat fmt = cursor.charFormat(); fmt.setUnderlineStyle(QTextCharFormat::NoUnderline); cursor.select(QTextCursor::Document); cursor.setCharFormat(fmt); cursor.clearSelection(); } void SpellChecker::_checkWord(QTextCursor *cursor) { cursor->select(QTextCursor::WordUnderCursor); QString selText = cursor->selectedText(); static const QRegExp nonDigits("\\D"); if (!selText.contains(nonDigits)) return; selText = selText.remove(QString::fromUtf8("«")); selText = selText.remove(QString::fromUtf8("»")); //selText = selText.remove("\""); //selText = selText.remove("("); //selText = selText.remove(")"); if (!checkWordSpelling(selText)) { QTextCharFormat fmt = cursor->charFormat(); fmt.setUnderlineColor(QColor(Qt::red)); fmt.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); cursor->setCharFormat(fmt); } else { QTextCharFormat fmt = cursor->charFormat(); fmt.setUnderlineStyle(QTextCharFormat::NoUnderline); cursor->setCharFormat(fmt); } cursor->clearSelection(); } bool SpellChecker::checkWordSpelling(const QString &word) { QString tmp = word; tmp = tmp.remove(QString::fromUtf8("»")); tmp = tmp.remove(QString::fromUtf8("«")); QByteArray ba = tmp.toUtf8(); return (aspell_speller_check(spell_checker1, ba.data(), ba.size()) != 0) || (aspell_speller_check((spell_checker2 != NULL ? spell_checker2 : spell_checker1), ba.data(), ba.size()) != 0); } void SpellChecker::checkWord() { if ((spell_checker1 == 0) && (spell_checker2 == 0)) return; QTextCursor cursor = m_textEdit->textCursor(); _checkWord(&cursor); } bool SpellChecker::hasHyphen(QTextCursor * cursor) { if ((spell_checker1 == 0) || (spell_checker2 == 0)) return false; cursor->movePosition(QTextCursor::EndOfWord); //cursor->movePosition(QTextCursor::NextCharacter); cursor->select(QTextCursor::WordUnderCursor); QString selText = cursor->selectedText(); cursor->movePosition(QTextCursor::PreviousWord); if (selText.endsWith(QString::fromUtf8("-"))) return true; return false; } bool SpellChecker::hasLongHyphen(QTextCursor *cursor) { cursor->select(QTextCursor::WordUnderCursor); QString selText = cursor->selectedText(); return selText.endsWith(QString::fromUtf8("—")); } QString SpellChecker::checkConcatenation(QTextCursor *cursor) { cursor->movePosition(QTextCursor::PreviousWord); cursor->select(QTextCursor::WordUnderCursor); QString word1 = cursor->selectedText(); cursor->movePosition(QTextCursor::NextWord); //cursor->movePosition(QTextCursor::NextWord); cursor->select(QTextCursor::WordUnderCursor); QString word2 = cursor->selectedText(); cursor->movePosition(QTextCursor::PreviousWord); QString word = word1+word2; if (checkWordSpelling(word)) return word; return ""; } QStringList SpellChecker::suggestions() { QStringList sl; if ((spell_checker1 == 0) || (spell_checker2 == 0)) return sl; QTextCursor cursor = m_textEdit->textCursor(); cursor.select(QTextCursor::WordUnderCursor); QString word = cursor.selectedText(); QByteArray ba = word.toUtf8(); if ((aspell_speller_check(spell_checker2, ba.data(), ba.size()) != 0)||(aspell_speller_check(spell_checker1, ba.data(), ba.size()) != 0)) return sl; const struct AspellWordList * awl = aspell_speller_suggest(spell_checker1, ba.data(), ba.size()); if(aspell_word_list_size(awl) > 0) { struct AspellStringEnumeration * ase = aspell_word_list_elements(awl); int i = 0; while ((!aspell_string_enumeration_at_end(ase))&&(i < 10)) { const char * text = aspell_string_enumeration_next(ase); sl << QString::fromUtf8(text); i++; } delete_aspell_string_enumeration(ase); } return sl; } yagf-0.9.3.2/src/tiffimporter.h0000664000175000017500000000234402263216100017000 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TIFFIMPORTER_H #define TIFFIMPORTER_H #include #include #include class TiffImporter : public QObject { Q_OBJECT public: explicit TiffImporter(const QString &fileName, QObject *parent = 0); void exec(); QStringList extractedFiles(); signals: void error(); void finished(const QStringList &files); public slots: private: QStringList files; QString tiffName; }; #endif // TIFFIMPORTER_H yagf-0.9.3.2/src/tiffimporter.cpp0000664000175000017500000000535502263216100017340 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2014 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "tiffimporter.h" #include "settings.h" #include #include #include TiffImporter::TiffImporter(const QString &fileName, QObject *parent) : QObject(parent), tiffName(fileName) { } void TiffImporter::exec() { QString wd = Settings::instance()->workingDir(); QDir dir; dir.setPath(wd); QStringList sl, pf, nf; sl << "t*_out*.jpg"; pf = dir.entryList(sl); QProcess proc; proc.setEnvironment(QProcess::systemEnvironment()); proc.setWorkingDirectory(wd); QString size = Settings::instance()->tiffPageSize(); QString density = Settings::instance()->tiffDensity(); QString cmd = QString("convert %1 -units PixelsPerInch -resize %2x%3 -units PixelsPerInch -density %4 t%5_out.jpg").arg(tiffName).arg(size).arg(size).arg(density).arg(Settings::instance()->uniqueSeed()); proc.start(cmd); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit) { emit error(); return; } nf = dir.entryList(sl); foreach(QString s, nf) { if (!pf.contains(s)) files.append(wd+s); } if (files.count()) emit finished(files); else { QImageReader ir(tiffName); if (!ir.read().isNull()) files.append(tiffName); else { const QString ppmFile = QString::fromAscii("input.ppm"); if (dir.exists(ppmFile)) { dir.remove(ppmFile); QProcess proc; proc.setEnvironment(QProcess::systemEnvironment()); proc.setWorkingDirectory(wd); proc.start("tifftopnm > " + ppmFile); proc.waitForFinished(); QImageReader ir(wd+ppmFile); if (!ir.read().isNull()) files.append(wd + ppmFile); } } } if (files.count()) emit finished(files); else emit error(); } QStringList TiffImporter::extractedFiles() { return files; } yagf-0.9.3.2/src/texteditor.h0000664000175000017500000000324502263216100016462 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TEXTEDITOR_H #define TEXTEDITOR_H #include #include #include "spellchecker.h" class TextEditor : public QTextEdit { Q_OBJECT public: explicit TextEditor(QWidget *parent = 0); ~TextEditor(); bool textSaved(); bool spellCheck(const QString &lang); void unSpellCheck(); void enumerateDicts(); bool hasDict(const QString &shname); public slots: void saveText(); protected: void keyPressEvent ( QKeyEvent * e ); void wheelEvent ( QWheelEvent * e ); private slots: void contextMenuRequested(const QPoint& point); void enlargeFont(); void decreaseFont(); void updateSP(); void replaceWord(); void copyAvailable(bool yes); void textChanged(); void copyClipboard(); private: void saveHtml(QFile *file); private: SpellChecker spellChecker; bool hasCopy; bool mTextSaved; }; #endif // TEXTEDITOR_H yagf-0.9.3.2/src/tpage.cpp0000664000175000017500000003263702263216100015731 0ustar parallelsparallels /* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "tpage.h" #include "settings.h" #include "ccbuilder.h" #include "CCAnalysis.h" #include "PageAnalysis.h" #include "analysis.h" #include "core/imageprocessor.h" #include #include #include #include #include #include #include Page::Page(const int pid, QObject *parent) : QObject(parent), selectedBlock(0,0,0,0) { imageLoaded = false; loadedBefore = false; ccbuilder = NULL; rotation = 0; deskewed = false; preprocessed = false; cropped = false; mFileName.clear(); this->pid = pid; } Page::~Page() { delete ccbuilder; } bool Page::loadFile(QString fileName, int tiled, bool loadIntoView) { // TODO: перенести в TPages. /*if ((fileName != "") && (sideBar->getFileNames().contains(fileName))) { sideBar->select(fileName); sideBar->clearBlocks(); for (int i = 0; i < graphicsInput->blocksCount(); i++) sideBar->addBlock(graphicsInput->getBlockRectByIndex(i).toRect()); }*/ if (fileName == "") { if(mFileName.isEmpty()) return false; fileName = mFileName; } rotation = 0; crop1.setX(0); crop1.setY(0); crop1.setWidth(0); crop1.setHeight(0); scale = 0.5; if (!fileName.endsWith(".ygf", Qt::CaseInsensitive)) { QImageReader ir(fileName); if (!ir.canRead()) return false; if ((ir.size().width() > 7500)||(ir.size().height() > 7500)) { ir.setScaledSize(QSize(ir.size().width()/2, ir.size().height()/2)); } else { // if ((ir.size().width() > 3800)||(ir.size().height() > 3800)) // ir.setScaledSize(QSize(ir.size().width()/2, ir.size().height()/2)); } img = ir.read(); } else { ImageProcessor ipx; img = ipx.loadYGF(fileName); } imageLoaded = !img.isNull(); if (!imageLoaded) return false; if (img.format() != QImage::Format_ARGB32) img = img.convertToFormat(QImage::Format_ARGB32); if (ccbuilder){ delete ccbuilder; ccbuilder = 0; } ImageProcessor ip; ip.loadImage(img); settings = Settings::instance(); if (settings->getCropLoaded()) { if (!cropped) ip.crop(); } img = ip.gsImage(); //ip.start(img2); //ip.tiledBinarize(); rotateImageInternal(img, rotation); ip.loadImage(img); if (settings->getPreprocessed()&&(!preprocessed)) { ip.binarize(); preprocessed = true; } img = ip.gsImage(); mFileName = saveTmpPage("YGF"); loadedBefore = true; return true; } QPixmap Page::displayPixmap() { return QPixmap::fromImage(currentImage()); } QImage Page::thumbnail() { return img.scaled(img.width()*0.125, img.height()*0.125); } bool Page::makeLarger() { if (scale >= 1.0) return false; if (scale < 0.2) { scale = 0.2; return true; } if (scale < 0.25) { scale = 0.25; return true; } if (scale < 0.3) { scale = 0.3; return true; } if (scale < 0.5) { scale = 0.5; return true; } if (scale < 0.75) { scale = 0.75; return true; } if (scale < 1.0) scale = 1.0; return true; } bool Page::makeSmaller() { if (scale <= 0.125) { return false; } if (scale > 0.75) { scale = 0.75; return true; } if (scale > 0.5) { scale = 0.5; return true; } if (scale > 0.3) { scale = 0.3; return true; } if (scale > 0.25) { scale = 0.25; return true; } if (scale > 0.2) { scale = 0.2; return true; } if (scale > 0.125) scale = 0.125; return true; } void Page::rotate(qreal angle) { rotateImageInternal(img, angle); rotation += angle; clearBlocks(); } void Page::unload() { if (ccbuilder){ delete ccbuilder; ccbuilder = 0; } img = QImage(0,0,QImage::Format_ARGB32); imageLoaded = false; } inline bool qrects_equal(QRect &r1, QRect &r2) { if (abs(r1.x() - r2.x()) > 2) return false; if (abs(r1.y() - r2.y()) > 2) return false; if (abs(r1.width() - r2.width()) > 2) return false; if (abs(r1.height() - r2.height()) > 2) return false; return true; } void Page::addBlock(Block block) { QRect r = block; //normalizeRect(r); scaleRect(r); bool add = true; foreach (Block b, blocks) { QRect r1 = b; if (r == r1) { add = false; break; } } if (add) { block.setRect(r.x(), r.y(), r.width(), r.height()); blocks.append(block); } sortBlocksInternal(); renumberBlocks(); } void Page::deleteBlock(const Block &b) { blocks.removeOne(b); sortBlocksInternal(); renumberBlocks(); } void Page::deleteBlock(const QRect &r) { QRect rx = r; scaleRect(rx); //normalizeRect(rx); foreach (Block b, blocks) { QRect r1 = b; if (qrects_equal(rx, r1)) { blocks.removeAll(b); break; } } sortBlocksInternal(); renumberBlocks(); } Block Page::getBlock(const QRect &r) { QRect rn =r; scaleRectToScale(rn); //normalizeRect(rn); foreach (Block b, blocks) { QRect r1 = b; if (qrects_equal(rn,r1)) { scaleRect(b); return b; } } return Block(0,0,0,0); } Block Page::getBlock(int index) { Block b = blocks.at(index); scaleRectToScale(b); return b; } int Page::blockCount() { return blocks.count(); } void Page::clearBlocks() { blocks.clear(); } void Page::savePageForRecognition(const QString &fileName) { img.save(fileName, "BMP"); } bool Page::savePageAsImage(const QString &fileName, const QString &format) { return img.save(fileName, format.toAscii().data()); } void Page::saveRawBlockForRecognition(QRect r, const QString &fileName) { saveBlockForRecognition(r, fileName, "BMP"); } void Page::saveBlockForRecognition(QRect r, const QString &fileName, const QString &format) { //QRect rs = scaleRect(r); QImage image = img.copy(r); //applyTransforms(image, 1); image.save(fileName, format.toAscii().data()); } void Page::saveBlockForRecognition(int index, const QString &fileName) { saveBlockForRecognition(blocks.at(index), fileName, "BMP"); } void Page::selectBlock(const QRect &r) { QRect rn =r; //normalizeRect(rn); foreach (Block b, blocks) { QRect r1 = b; if (rn == r1) { selectedBlock = b; break; } } } Block Page::getSelectedBlock() { return selectedBlock; } bool Page::deskew(bool recreateCB) { if (deskewed) return false; if (imageLoaded) { prepareCCBuilder(); CCAnalysis * an = new CCAnalysis(ccbuilder); if (an->analize()) { QImage timg; //if ((img.height() > 3800)||(img.width() > 3800)) // return false; timg = tryRotate(img, -atan(an->getK())*360/6.283); CCBuilder * cb2 = new CCBuilder(timg); cb2->labelCCs(); CCAnalysis * an2 = new CCAnalysis(cb2); an2->analize(); qreal angle = -atan(an2->getK())*360/6.283; delete an2; delete cb2; if (abs(angle*10) >= abs(5)) angle += (-atan(an->getK())*360/6.283); else angle = -atan(an->getK())*360/6.283; if (abs(angle) < 0.001) { deskewed = true; return false; } rotate(angle); rotation = angle; ImageProcessor::cropAngles(img); QString fn = saveTmpPage("YGF"); loadFile(fn, 1); deskewed = true; delete ccbuilder; ccbuilder = 0; if (recreateCB) prepareCCBuilder(); } delete an; } return true; } void Page::rotate90CW() { deskewed = false; rotate(90); } void Page::rotate90CCW() { deskewed = false; rotate(-90); } void Page::rotate180() { deskewed = false; rotate(180); } void Page::blockAllText() { prepareCCBuilder(); clearBlocks(); BlockSplitter bs; bs.setImage(img, rotation, scale); QRect r = bs.getRootBlock(currentImage()); addBlock(r); } QList Page::splitInternal() { clearBlocks(); BlockSplitter bs; //rotation = 0; bs.setImage(img, 0, 1.0);// sideBar->getScale()); bs.splitBlocks(); return bs.getBlocks(); } void Page::prepareCCBuilder() { if (!ccbuilder) { ccbuilder = new CCBuilder(img); ccbuilder->labelCCs(); } } bool Page::splitPage(bool preprocess) { QList blocks; prepareCCBuilder(); if (preprocess) { QString fn = saveTmpPage("YGF"); loadedBefore = false; loadFile(fn, 1); blocks = splitInternal(); /*if (blocks.count() == 0) { deskew(); fn =Settings::instance()->workingDir() + QString::fromUtf8("tmp-%1.bmp").arg((quint64)img2.data_ptr()); saveTmpPage(fn, true, false); loadedBefore = false; loadFile(fn); blocks = splitInternal(); }*/ preprocessed = true; } else { deskew(); blocks = splitInternal(); } qreal sf = scale; foreach (Rect block, blocks) { QRect r; block.x1 *=sf; block.y1 *=sf; block.x2 *= sf; block.y2 *=sf; block.x1 -=4; block.y1 +=6; block.x2 -= 4; block.y2 +=6; r.setX(block.x1); r.setY(block.y1); r.setWidth(block.x2 - block.x1); r.setHeight(block.y2 - block.y1); addBlock(r); } return blocks.count() != 0; } bool Page::textHorizontal() { return ImageProcessor::isTextHorizontal(img); } QString Page::fileName() { return mFileName; } int Page::pageID() { return pid; } void Page::sortBlocksInternal() { sortBlocks(blocks); } bool Page::isDeskewed() { return deskewed; } bool Page::isCropped() { return cropped; } bool Page::isPreprocessed() { return preprocessed; } qreal Page::getRotation() { return rotation; } void Page::setDeskewed(bool value) { deskewed = value; } void Page::setPreprocessed(bool value) { preprocessed = value; } void Page::applyTransforms(QImage &image, qreal scale) { scale = scale*2; QRect crop; crop.setX(crop1.x()*scale); crop.setY(crop1.y()*scale); crop.setWidth(crop1.width()*scale); crop.setHeight(crop1.height()*scale); image = image.copy(crop); rotateImageInternal(image, rotation); } void Page::rotateImageInternal(QImage &image, qreal angle) { qreal x = image.width() / 2; qreal y = image.height() / 2; image = image.transformed(QTransform().translate(-x, -y).rotate(angle).translate(x, y), Qt::SmoothTransformation); } QRect Page::scaleRect(QRect &rect) { qreal iscale = 1./scale; qreal oldw = rect.width(); qreal oldh = rect.height(); rect.setX(rect.x()*iscale); rect.setY(rect.y()*iscale); rect.setWidth(oldw*iscale); rect.setHeight(oldh*iscale); return rect; } QRect Page::scaleRectToScale(QRect &rect) { qreal oldw = rect.width(); qreal oldh = rect.height(); rect.setX(rect.x()*scale); rect.setY(rect.y()*scale); rect.setWidth(oldw*scale); rect.setHeight(oldh*scale); return rect; } QImage Page::tryRotate(QImage image, qreal angle) { qreal x = image.width() / 2; qreal y = image.height() / 2; return image.transformed(QTransform().translate(-x, -y).rotate(angle).translate(x, y), Qt::SmoothTransformation); } QImage Page::currentImage() { if (!imageLoaded) { ImageProcessor ip; img = ip.loadYGF(mFileName); applyTransforms(img, 0.5); imageLoaded=true; } return img.scaled(img.width()*scale, img.height()*scale); } #include QString Page::saveTmpPage(const QString &format) { QString fileName =Settings::instance()->workingDir() + QString::fromUtf8("tmp-%1").arg(Settings::instance()->uniqueSeed()); // booster.flatten(&image); if (format == "BMP") { fileName = fileName +".bmp"; img.save(fileName, "BMP"); } else { fileName = fileName +".ygf"; ImageProcessor ip; ip.saveYGF(img, fileName); } return fileName; } void Page::reSaveTmpPage() { ImageProcessor ip; ip.saveYGF(img, mFileName); } void Page::cropYGF() { ImageProcessor ip; ip.loadYGF(fileName()); ip.crop(); saveTmpPage("YGF"); } void Page::renumberBlocks() { for (int i = 0; i < blocks.count(); i++) { blocks[i].setBlockNumber(i+1); } } yagf-0.9.3.2/src/tpagecollection.cpp0000664000175000017500000001776302263216100020010 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "tpagecollection.h" #include "core/imageprocessor.h" #include "qsnippet.h" #include "settings.h" #include #include #include PageCollection * PageCollection::m_instance = NULL; PageCollection::PageCollection(QObject *parent) : QObject(parent) { index = -1; pid = 0; } PageCollection::PageCollection(const PageCollection &) { } PageCollection::~PageCollection() { clear(); } bool PageCollection::appendPage(const QString &fileName) { unloadAll(); Page * p = new Page(++pid); connect(p,SIGNAL(refreshView()), this, SIGNAL(loadPage())); if (p->loadFile(fileName, 1, false)) { pages.append(p); index = pages.count() - 1; if (Settings::instance()->getAutoDeskew()) { deskew(); //p->cropYGF(); //p->reSaveTmpPage(); } emit addSnippet(index); connect(p, SIGNAL(textOut(QString)), SLOT(textOut(QString))); return true; } else { delete p; pid--; return false; } } void PageCollection::newPage(const QString &fileName, qreal rotation, bool preprocessed, bool deskewed) { if (cp()) cp()->unload(); Page * p = new Page(++pid); connect(p,SIGNAL(refreshView()), this, SIGNAL(loadPage())); p->setDeskewed(deskewed); p->setPreprocessed(preprocessed); if (p->loadFile(fileName, 1)) { pages.append(p); p->rotate(rotation); index = pages.count() - 1; emit addSnippet(index); makePageCurrent(index); } } int PageCollection::count() { return pages.count(); } bool PageCollection::makePageCurrent(int index) { if (cp()) cp()->unload(); this->index = index; return index < pages.count(); } bool PageCollection::makePageCurrentByID(int id) { return makePageCurrent(id2Index(id)) >= 0; } void PageCollection::setBeforeFirst() { index = -1; } bool PageCollection::makeNextPageCurrent() { if (cp()) cp()->unload(); index++; if (index < count()) return true; index = -1; return false; } QSnippet *PageCollection::snippet() { if (!cp()) return NULL; QSnippet * s = new QSnippet(); s->setPage(cp()->pageID(), cp()->fileName(), cp()->thumbnail()); return s; } QPixmap PageCollection::pixmap() { if (!cp()) return QPixmap(); return cp()->displayPixmap(); } void PageCollection::savePageForRecognition(const QString &fileName) { if (!cp()) return; cp()->savePageForRecognition(fileName); } void PageCollection::saveRawBlockForRecognition(QRect r, const QString &fileName) { if (!cp()) return; cp()->saveRawBlockForRecognition(r, fileName); } void PageCollection::saveBlockForRecognition(QRect r, const QString &fileName, const QString &format) { if (!cp()) return; cp()->saveBlockForRecognition(r, fileName, format); } void PageCollection::saveBlockForRecognition(int index, const QString &fileName) { if (!cp()) return; if (index == 0) cp()->sortBlocksInternal(); cp()->saveBlockForRecognition(index, fileName); } int PageCollection::blockCount() { if (!cp()) return 0; return cp()->blockCount(); } Block PageCollection::getBlock(const QRect &r) { Block block(0,0,0,0); if (!cp()) return block; return cp()->getBlock(r); } Block PageCollection::getBlock(int index) { Block block(0,0,0,0); if (!cp()) return block; return cp()->getBlock(index); } void PageCollection::selectBlock(const QRect &r) { if (!cp()) return; cp()->selectBlock(r); } Block PageCollection::getSelectedBlock() { Block block(0,0,0,0); if (!cp()) return block; return cp()->getSelectedBlock(); } bool PageCollection::pageValid() { return cp() != 0; } QString PageCollection::fileName() { if (!cp()) return ""; return cp()->fileName(); } bool PageCollection::savePageAsImage(const QString &fileName, const QString &format) { if (!cp()) return false; return cp()->savePageAsImage(fileName, format); } bool PageCollection::isDeskewed() { if (cp()) return cp()->isDeskewed(); return false; } bool PageCollection::isPreprocessed() { if (cp()) return cp()->isPreprocessed(); return false; } qreal PageCollection::getRotation() { if (cp()) return cp()->getRotation(); return 0; } void PageCollection::setRotation(const qreal value) { if (cp()) cp()->rotate(value); } void PageCollection::setDeskewed(const bool value) { if (cp()) cp()->setDeskewed(value); } void PageCollection::setPreprocessed(const bool value) { if (cp()) cp()->setPreprocessed(value); } void PageCollection::reloadPage() { emit loadPage(); } void PageCollection::unloadAll() { foreach(Page *p, pages) { p->unload(); } } void PageCollection::makeLarger() { if (!cp()) return; cp()->makeLarger(); emit loadPage(); } void PageCollection::makeSmaller() { if (!cp()) return; cp()->makeSmaller(); emit loadPage(); } void PageCollection::rotate90CW() { if (!cp()) return; cp()->rotate90CW(); emit loadPage(); } void PageCollection::rotate90CCW() { if (!cp()) return; cp()->rotate90CCW(); emit loadPage(); } void PageCollection::rotate180() { if (!cp()) return; cp()->rotate180(); emit loadPage(); } void PageCollection::deskew() { if (!cp()) return; if (cp()->textHorizontal()) cp()->deskew(); emit loadPage(); } void PageCollection::blockAllText() { if (!cp()) return; cp()->blockAllText(); emit loadPage(); } bool PageCollection::splitPage(bool preprocess) { if (!cp()) return false; bool res = cp()->splitPage(preprocess); emit loadPage(); return res; } void PageCollection::addBlock(const QRect &rect) { if (!cp()) return; Block block(rect.x(), rect.y(), rect.width(), rect.height()); cp()->addBlock(block); } void PageCollection::deleteBlock(const QRect &rect) { if (!cp()) return; cp()->deleteBlock(rect); } void PageCollection::clearBlocks() { if (!cp()) return; cp()->clearBlocks(); } void PageCollection::clear() { foreach (Page * p, pages) { delete p; } pages.clear(); emit cleared(); index = -1; pid = 0; } Page *PageCollection::cp() { if ((index < 0)|| (index >= count())) return (Page*) 0; return pages.at(index); } int PageCollection::id2Index(int id) { foreach (Page *p, pages) { if (p->pageID() == id) return pages.indexOf(p); } return -1; } void PageCollection::pageSelected(int id) { makePageCurrent(id2Index(id)); emit loadPage(); } void PageCollection::pageRemoved(int id) { int index = id2Index(id); if (index >= 0) { delete pages.at(index); pages.remove(index); } if (index >= pages.count()) index = pages.count() - 1; makePageCurrent(index); emit loadPage(); } void PageCollection::textOut(const QString &msg) { emit messagePosted(msg); } PageCollection *PageCollection::instance() { if (!m_instance) m_instance = new PageCollection(); return m_instance; } void PageCollection::clearCollection() { if (m_instance) m_instance->clear(); } yagf-0.9.3.2/src/tpage.h0000664000175000017500000000637202263216100015373 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TPAGE_H #define TPAGE_H #include "tblock.h" #include "analysis.h" #include #include #include #include class CCBuilder; class Settings; class Page : public QObject { Q_OBJECT public: explicit Page(const int pid, QObject *parent = 0); ~Page(); bool loadFile(QString fileName, int tiled, bool loadIntoView = true); QPixmap displayPixmap(); QImage thumbnail(); bool makeLarger(); bool makeSmaller(); void rotate(qreal angle); void unload(); void addBlock(Block block); void deleteBlock(const Block &b); void deleteBlock(const QRect &r); Block getBlock(const QRect &r); Block getBlock(int index); int blockCount(); void clearBlocks(); void savePageForRecognition(const QString &fileName); bool savePageAsImage(const QString &fileName, const QString &format); void saveRawBlockForRecognition(QRect r, const QString &fileName); void saveBlockForRecognition(QRect r, const QString &fileName, const QString &format); void saveBlockForRecognition(int index, const QString &fileName); void selectBlock(const QRect &r); Block getSelectedBlock(); bool deskew(bool recreateCB = true); void rotate90CW(); void rotate90CCW(); void rotate180(); void blockAllText(); bool splitPage(bool preprocess); bool textHorizontal(); QString fileName(); int pageID(); void sortBlocksInternal(); bool isDeskewed(); bool isCropped(); bool isPreprocessed(); qreal getRotation(); void setDeskewed(bool value); void setPreprocessed(bool value); void reSaveTmpPage(); void cropYGF(); signals: void refreshView(); void textOut(const QString &msg); public slots: private: void renumberBlocks(); void applyTransforms(QImage &image, qreal scale); void rotateImageInternal(QImage &image, qreal angle); QRect scaleRect(QRect &rect); QRect scaleRectToScale(QRect &rect); QImage tryRotate(QImage image, qreal angle); QImage currentImage(); QString saveTmpPage(const QString &format); QList splitInternal(); void prepareCCBuilder(); private: qreal scale; qreal rotation; QRect crop1; QRect crop2; bool deskewed; bool cropped; QImage img; TBlocks blocks; bool imageLoaded; bool loadedBefore; bool preprocessed; QString mFileName; CCBuilder * ccbuilder; Settings * settings; int blockPointer; int pid; Block selectedBlock; }; #endif // TPAGE_H yagf-0.9.3.2/src/utils.cpp0000664000175000017500000000336402263216100015764 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "utils.h" QString extractFileName(const QString &path) { QFileInfo fi(path); return fi.fileName(); } QString extractFilePath(const QString &path) { QFileInfo fi(path); QString s = fi.dir().path(); if (!s.endsWith("/")) s += '/'; return s; } QString extractDigits(const QString &fn) { bool extracting = FALSE; QString result = ""; for (int i = 0; i < fn.size(); i++) if ((fn.at(i) >= '0') && (fn.at(i) <= '9')) { extracting = TRUE; result += fn.at(i); } else { if (extracting) break; } return result; } bool findProgram(const QString &name) { QStringList sl = QString(getenv("PATH")).split(":"); QFileInfo fi; for (int i = 0; i < sl.count(); i++) { fi.setFile(sl.at(i), name); if (fi.exists()) return true; } return false; } yagf-0.9.3.2/src/tpagecollection.h0000664000175000017500000000615202263216100017443 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-end Copyright (C) 2009-2012 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TPAGECOLLECTION_H #define TPAGECOLLECTION_H #include "tpage.h" #include #include #include class QSnippet; class PageCollection : public QObject { Q_OBJECT public: static PageCollection * instance(); static void clearCollection(); bool appendPage(const QString &fileName); void newPage(const QString &fileName, qreal rotation, bool preprocessed, bool deskewed); int count(); bool makePageCurrent(int index); bool makePageCurrentByID(int id); void setBeforeFirst(); // the page pointer is set before the first page bool makeNextPageCurrent(); QSnippet * snippet(); QPixmap pixmap(); void savePageForRecognition(const QString &fileName); void saveRawBlockForRecognition(QRect r, const QString &fileName); void saveBlockForRecognition(QRect r, const QString &fileName, const QString &format = "BMP"); void saveBlockForRecognition(int index, const QString &fileName); int blockCount(); Block getBlock(const QRect &r); Block getBlock(int index); void selectBlock(const QRect &r); Block getSelectedBlock(); bool pageValid(); QString fileName(); bool savePageAsImage(const QString &fileName, const QString &format); bool isDeskewed(); bool isPreprocessed(); qreal getRotation(); void setRotation(const qreal value); void setDeskewed(const bool value); void setPreprocessed(const bool value); void reloadPage(); void unloadAll(); public slots: void makeLarger(); void makeSmaller(); void rotate90CW(); void rotate90CCW(); void rotate180(); void deskew(); void blockAllText(); bool splitPage(bool preprocess); void addBlock(const QRect & rect); void deleteBlock(const QRect & rect); void clearBlocks(); void clear(); signals: void loadPage(); // The page is already current void addSnippet(int index); void cleared(); void messagePosted(const QString &msg); private slots: void pageSelected(int id); void pageRemoved(int id); void textOut(const QString &msg); private: PageCollection(QObject *parent = 0); PageCollection(const PageCollection &); ~PageCollection(); Page * cp(); int id2Index(int id); private: QVector pages; int index; int pid; static PageCollection * m_instance; }; #endif // TPAGECOLLECTION_H yagf-0.9.3.2/src/utils.h0000664000175000017500000000214102263216100015421 0ustar parallelsparallels/* YAGF - cuneiform OCR graphical front-end Copyright (C) 2009 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef UTILS_H #define UTILS_H class QString; QString extractFileName(const QString &path); QString extractFilePath(const QString &path); QString extractDigits(const QString &fn); bool findProgram(const QString &name); inline bool _contains(qreal x1, qreal x2, qreal xc) { return ((xc >= x1) && (xc <= x2)) || ((xc <= x1) && (xc >= x2)); } #endif yagf-0.9.3.2/src/yagf.qrc0000664000175000017500000000512402263216100015551 0ustar parallelsparallels images/rcw.png images/rccw.png images/revert.png images/select.png images/smaller.png images/larger.png images/singlecolumn.png images/yagf.png images/back.png images/document_open.png images/document_save_as.png images/editcopy.png images/filefind.png images/fileopen.png images/filesaveas.png images/forward.png images/scanner.png images/warning.png images/critical.png images/info.png images/resize.png images/resize_block.png images/save_all.png images/editclear.png images/batch.png images/remove.png images/align.png images/undo.png images/scanner_s2.png images/recblocks.png images/trashcan1s.png images/trashcan2-s.png images/check_spelling.png images/savpicas.png images/saveblock.png images/deskew2.png images/stock_new_html.png images/application_pdf.png images/trashcan_full.png images/clearblocks.png images/selecttext.png images/edit_paste.png images/scanner48.png images/selectmulti.png images/tools_wizard.png images/tools_wizard_small.png images/appearence.png images/eISet.png images/recognition.png images/earth.png images/advanced.png images/cform.png images/tess.png images/installed.png images/box.png images/notinst.png images/langs.png yagf-0.9.3.2/src/ycommon.h0000664000175000017500000000213102263216100015741 0ustar parallelsparallels/* YAGF - cuneiform and tesseract OCR graphical front-ends Copyright (C) 2009-2010 Andrei Borovsky This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef YCOMMMON_H #define YCOMMMON_H #include #include #include #include typedef QList QPointList; inline QRect QRectF2Rect(const QRectF &rf) { QRect r; r.setX(rf.x()); r.setY(rf.y()); r.setWidth(rf.width()); r.setHeight(rf.height()); return r; } #endif yagf-0.9.3.2/CMakeLists.txt0000664000175000017500000001233202263216100016064 0ustar parallelsparallelsproject(yagf C CXX) cmake_minimum_required(VERSION 2.6.0) #set(QT_QMAKE_EXECUTABLE /usr/bin/qmake-qt4) if (NOT DEFINED ${CMAKE_INSTALL_PREFIX}) set(CPACK_INSTALL_PREFIX /usr/) set (CMAKE_INSTALL_PREFIX /usr/) endif() set (LIB_PATH_SUFFIX ) if (CMAKE_SIZEOF_VOID_P EQUAL 8) if (EXISTS "${CMAKE_INSTALL_PREFIX}lib64") set(LIB_PATH_SUFFIX 64) endif (EXISTS "${CMAKE_INSTALL_PREFIX}lib64") set_property (GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE) endif(CMAKE_SIZEOF_VOID_P EQUAL 8) add_definitions( -DPRILIBRARY_PATH="${CMAKE_INSTALL_PREFIX}lib${LIB_PATH_SUFFIX}/yagf/") # following 2 lines define where interface translations will be installed set(QML_DESTINATION share/yagf/translations/) set(QML_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) add_definitions( -DQML_INSTALL_PATH="${QML_INSTALL_PREFIX}${QML_DESTINATION}") set(SOURCES src/main.cpp src/mainform.cpp src/qgraphicsinput.cpp src/utils.cpp src/qxtunixsignalcatcher.cpp src/spellchecker.cpp src/BlockAnalysis.cpp src/PageAnalysis.cpp src/SkewAnalysis.cpp src/CCAnalysis.cpp src/qxtgraphicsproxywidget.cpp src/popplerdialog.cpp src/pdfextractor.cpp src/pdf2ppt.cpp src/ghostscr.cpp src/configdialog.cpp src/ccbuilder.cpp src/analysis.cpp src/qsnippet.cpp src/sidebar.cpp src/droplabel.cpp src/projectmanager.cpp src/settings.cpp src/texteditor.cpp src/tblock.cpp src/tpage.cpp src/tpagecollection.cpp src/scanner.cpp src/core/imageprocessor.cpp src/langselectdialog.cpp src/busyform.cpp src/tiffimporter.cpp src/pdfthread.cpp src/QBusyIndicator.cpp src/core/binarize.cpp src/core/util.cpp src/core/qipblackandwhiteimage.cpp src/core/qipgrayscaleimage.cpp) set(HEADERS src/settings.h) set(INT_SOURCES src/preload.c) set(UIS src/mainform.ui src/popplerdialog.ui src/configdialog.ui src/langselectdialog.ui src/busyform.ui) set(MOC_HEADERS src/mainform.h src/qxtunixsignalcatcher.h src/qxtunixscinternal.h src/qgraphicsinput.h src/qxtgraphicsproxywidget.h src/qxtgraphicsview.h src/popplerdialog.h src/pdfextractor.h src/pdf2ppt.h src/configdialog.h src/ccbuilder.h src/qsnippet.h src/sidebar.h src/droplabel.h src/projectmanager.h src/texteditor.h src/tblock.h src/tpage.h src/tpagecollection.h src/scanner.h src/core/imageprocessor.h src/langselectdialog.h src/busyform.h src/tiffimporter.h src/pdfthread.h src/QBusyIndicator.h src/core/binarize.h src/core/common.h src/core/util.h src/core/qipblackandwhiteimage.h src/core/qipgrayscaleimage.h) set(yagf_RCCS src/yagf.qrc) set(QM_FILES ${CMAKE_BINARY_DIR}/yagf_ru.qm ${CMAKE_BINARY_DIR}/yagf_fr.qm) add_definitions(-Wall -g) find_package(Qt4 4.6 REQUIRED) find_package(ASPELL REQUIRED) include_directories(${ASPELL_INCLUDE_DIR}) include(${QT_USE_FILE}) qt4_wrap_ui(UI_HEADERS ${UIS}) qt4_wrap_cpp(MOC_SRCS ${MOC_HEADERS}) qt4_add_resources(yagf_RCC_SRCS ${yagf_RCCS}) include_directories( ${CMAKE_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src) add_executable(yagf ${SOURCES} ${UI_HEADERS} ${MOC_SRCS} ${yagf_RCC_SRCS} ${QM_FILES}) add_library(xspreload SHARED ${INT_SOURCES}) target_link_libraries(xspreload ${CMAKE_DL_LIBS}) target_link_libraries(yagf ${QT_LIBRARIES} ${ASPELL_LIBRARIES}) add_custom_target(translations DEPENDS ${QM_FILES}) qt4_add_translation(${CMAKE_BINARY_DIR}/yagf_ru.qm ${PROJECT_SOURCE_DIR}/src/mainform.cpp ${PROJECT_SOURCE_DIR}/src/popplerdialog.cpp ${MOC_SRCS} ${UI_HEADERS} ${PROJECT_SOURCE_DIR}/src/translations/yagf_ru.ts) qt4_add_translation(${CMAKE_BINARY_DIR}/yagf_fr.qm ${PROJECT_SOURCE_DIR}/src/mainform.cpp ${PROJECT_SOURCE_DIR}/src/popplerdialog.cpp ${MOC_SRCS} ${UI_HEADERS} ${PROJECT_SOURCE_DIR}/src/translations/yagf_fr.ts) #QT4_CREATE_TRANSLATION(yagf_ru.qm src/mainform.cpp ${MOC_SRCS} ${UI_HEADERS} src/yagf_ru.ts) #set(CMAKE_INSTALL_PREFIX /usr/) install(TARGETS yagf xspreload RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_PATH_SUFFIX}/yagf) install(FILES ${QM_FILES} DESTINATION ${QML_DESTINATION}) #install(FILES COPYING DESCRIPTION README AUTHORS ChangeLog DESTINATION share/yagf/) install(FILES yagf.png DESTINATION share/pixmaps/) install(FILES yagf.png DESTINATION share/icons/hicolor/96x96/apps/) install(FILES YAGF.desktop DESTINATION share/applications/) #set(CPACK_PACKAGE_FILE_NAME yagf) set(CPACK_PACKAGE_VENDOR "Andrei Borovsky, anb@symmetrica.net") set(CPACK_PACKAGE_CONTACT ${CPACK_PACKAGE_VENDOR}) set(CPACK_SYSTEM_NAME "i586") set(CPACK_BINARY_DEB ON) set(CPACK_BINARY_RPM ON) set(CPACK_BINARY_STGZ ON) set(CPACK_BINARY_TGZ OFF) set(CPACK_BINARY_TZ OFF) set(CPACK_BINARY_TBZ2 OFF) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Graphical front-end for cuneiform and tesseract OCR tools") set(CPACK_PACKAGE_VERSION 0.9.1) set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MINOR "9") set(CPACK_PACKAGE_VERSION_PATCH "1") set(CPACK_STRIP_FILES bin/yagf bin/libyagfpreload) set(CPACK_SOURCE_TBZ2 "OFF") set(CPACK_SOURCE_TGZ "ON") set(CPACK_SOURCE_TZ "OFF") set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR}/yagf-${CPACK_PACKAGE_VERSION}/;/") set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/COPYING) set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/DESCRIPTION) set(CPACK_RESOURCE_FILE_README ${CMAKE_SOURCE_DIR}/README) set(DEBIAN_PACKAGE_SECTION "text processing") set(CPACK_RPM_PACKAGE_LICENSE "GPL v.3.0") set (CPACK_RPM_PACKAGE_REQUIRES "libqt > 4.5") include(CPack) yagf-0.9.3.2/DESCRIPTION0000664000175000017500000000162702263216100015037 0ustar parallelsparallelsYAGF is a graphical front-end for cuneiform and tesseract OCR tools. With YAGF you can open already scanned image files or obtain new images via XSane (scanning results are automatically passed to YAGF). Once you have a scanned image you can prepare it for recognition, select particular image areas for recognition, set the recognition language and so no. Recognized text is displayed in a editor window where it can be corrected, saved to disk or copied to clipboard. YAGF also provides some facilities for a multi-page recognition (see the online help for more details). Building YAGF requires: qt4 development tools version > 4.5 libaspell development package Running YAGF requires: Qt 4.5 or later, aspell Of course it is desirable to have xsane installed. PDFimport is done using either pdftoppm or gs utilities. Make sure at least one of these is installed if you are going to recognize text from PDFs. yagf-0.9.3.2/ChangeLog0000664000175000017500000001016202263216100015075 0ustar parallelsparallels* Fri Mar 21 2014 Andrei Borovsky - 0.9.3.1 - some GUI improvements - Multipage TIFF import added * Tue Feb 19 2014 Andrei Borovsky - 0.9.3 - New image enchansement functions added - New languages added - Minor bugs fixed, stability improved * Mon Sep 3 2012 Andrei Borovsky - 0.9.2 - Workspace may now be saved as a project - Prepare for recognition button - Small bug fixes and improvements * Fri Apr 20 2012 Andrei Borovsky - 0.9.1 - Some minor bug fixes and improvements * Sun Apr 15 2012 Andrei Borovsky - 0.9.1 - Toolbar icons size togling options is added - YAGF now stores its private data in according with freedesktop.org standard for configuration files * Mon Apr 09 2012 Andrei Borovsky - 0.9.1 - Text-splitting capability for splitting text into blocks automatically is added * Tue Feb 28 2012 Andrei Borovsky - 0.9 - Genral program restructure. Images are loaded and processed much faster. * Mon Feb 27 2012 Andrei Borovsky - 0.9 - Hebrew support added - corrected word suggestion added to the text editor context menu. * Sun Feb 26 2012 Andrei Borovsky - 0.9 - Automatic cropping of the loaded images added * Sun Dec 22 2011 Stefan Grthe - 0.9 - German Gothic and Swedish Gothic recognition is added (requires tesseract 3+) * Sun Dec 22 2011 Andrei Borovsky - 0.9 - Hebrew recognition support added (requires tesseract 3.0.1+) * Fri Dec 16 2011 Andrei Borovsky - 0.8.9 - Deskewing images improved - Pasting i,ages from clipboard added - Automatic text selection added (experimental) - Some minor bug fixes and improvements * Sun Aug 28 2011 Andrei Borovsky - 0.8.7 - Tesseract support added - PDF import added - Dictionary selection bug fixed * Sun Feb 20 2011 Andrei Borovsky - 0.8.6 - Some bugs fixed - The default recognition language is now set according to the user's locale - Spellchecker buton now indicates if the dictioanry for the selected language is available - Skew correction feature (beta stage) is added * Tue Jan 25 2011 Andrei Borovsky - 0.8.5 - Many bug fixes thanks to Skip - Spellchecker improved - Bulgarian language added (at last!;) - Blocks and entire image may be saved to a file * Sat Jan 08 2011 Andrei Borovsky - 0.8.4 - Serbian, Croatian, and Slovenian languages are added (at last!) - Ability to open several files at ones is added - Ability to drag and drop files on the sidebar is added - Sidebar now remembers blocks selected on all the pages * Sun Dec 26 2010 Andrei Borovsky - 0.8.2 - multiple blocks in image are now supported * Sun Aug 16 2009 Andrei Borovsky - 0.8.1 - batch recognition added * Wed Aug 5 2009 Andrei Borovsky - 0.8.0 - text selection blocks are now resizable - images management bar is added * Wed Aug 5 2009 Andrei Borovsky - 0.8.0 - text selection blocks are now resizable - images management bar is added * Sat Jul 25 2009 Andrei Borovsky - 0.7.1 - scaling and rotation is kept between images in the series - images and text may be scaled by Ctrl + mouse wheel or by Ctrl + [+]/[-] keys. * Sun Jul 19 2009 Andrei Borovsky - 0.7.0 - spell-checking is added - saving to html with images is added * Fri Jul 17 2009 Andrei Borovsky - 0.6.2 - merged the patches with the appropriate files - removed unnessesary ldconfig call * Wed Jul 15 2009 Kyrill Detinov - 0.6.1 - update to 0.6.1 - fixed build in x86-64 - corrected build requires * Sat Jun 20 2009 Kyrill Detinov - 0.5.0 - change compiling outside of the source tree * Mon Jun 15 2009 Kyrill Detinov - 0.5.0 - fix requires Qt version * Mon Jun 08 2009 Kyrill Detinov - 0.5.0 - correct build requires: libqt4-devel <= 4.4.3, cmake >= 2.6 * Fri Jun 05 2009 Kyrill Detinov - 0.5.0 - initial package created yagf-0.9.3.2/COPYING0000664000175000017500000010451302263216100014362 0ustar parallelsparallels GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . yagf-0.9.3.2/AUTHORS0000664000175000017500000000116402263216100014375 0ustar parallelsparallelsDevelopers: Andrei Borovsky Icon designers: YAGF theme: Andrei Borovsky Crystal Project theme: Everaldo Coelho Oxygen theme: Oxygen Team Human-O2 theme: Schollidesign Translators: German: Stefan Garthe Lithuanian: Donatas Glodenis Polish: Andrzej Januszkiewicz Russian: Andrei Borovsky Ukrainian: Olexa Stasevych yagf-0.9.3.2/INSTALL0000664000175000017500000000333202263216100014355 0ustar parallelsparallelsThese are generic installation instructions. YAGF requires Qt 4.6 or later and Aspell libraries. Note about Qt 5: YAGF cannot be built with Qt 5 yet. If your system uses Qt 5 by default you should install Qt 4 as well. You should then find where qmake for Qt 4 is and uncomment the following line in CMakeLists.txt: set(QT_QMAKE_EXECUTABLE /usr/bin/qmake-qt4) If qmake for QT 4 is called differently in you system you should edit the path portion of this line. You will need CMake to build YAGF from sources. There are two modes for building YAGF with CMake: in-source and out-source. The out-source mode is preferred. To build YAGF in this mode follow these instructions: make a directory for your build (for example yagf-build). Assuming your yagf-build directory is in the same directory as the yagf directory you can command: cd yagf-build cmake ../yagf make sudo make install You can now remove the yagf-build directory. In order to perform an in-source build you should issue this simple set of commands: cd yagf cmake ./ make sudo make install There are several important variables defined in CMakeLists.txt file. The QML_INSTALL_PREFIX and QML_DESTINATION valiarbles define where qml (traslation) files are going to be installed. The full path is cistructed as QML_INSTALL_PREFIX+QML_DESTINATION and it is the place where the program will look for the compiled translation files. While YAGF may be built to support multi-image TIFFs, I have found that it conflicts with JPEG input libraries. This may be reso;ved in the future. If you want to build the program with multi-image TIFF support, open CMakeLists.txt file and change the line set(MPTIFF_SUPPORT false) to set(MPTIFF_SUPPORT true) and then build the program as described above. yagf-0.9.3.2/YAGF.desktop0000664000175000017500000000153202263216100015445 0ustar parallelsparallels[Desktop Entry] Type=Application Categories=Qt;Office;OCR; Exec=yagf %F Icon=yagf Terminal=false StartupNotify=true MimeType=image/png;image/jpeg;image/bmp;image/tiff;image/gif;image/x-portable-pixmap;image/x-portable-graymap;image/x-portable-bitmap; Name=YAGF Name[ru]=YAGF Name[lt]=YAGF Name[pl]=YAGF Name[de]=YAGF GenericName=OCR text recognition GenericName[ru]=Распознавание текста GenericName[lt]=ENTER YOUR TRANSLATION HERE GenericName[pl]=ENTER YOUR TRANSLATION HERE GenericName[de]=OCR Texterkennung Comment=Graphical frontend for Cuneiform and Tesseract Comment[ru]=Графическая оболочка для Cuneiform и Tesseract Comment[lt]=Grafinė teksto atpažinimo programos Cuneiform sąsaja Comment[pl]=Grafinė teksto atpažinimo programos Cuneiform sąsaja Comment[de]=Graphische Benutzeroberfläche zu Cuneiform yagf-0.9.3.2/README0000664000175000017500000000046602263216100014211 0ustar parallelsparallelsFor more information and system requirements see DESCRIPTION file. This is a beta version so many things are not yet implemented as they are supposed to. The main things to do are - adding more image prepocessing features - adding packet recognition Contributors are welcome. Contact me via anb@symmetrica.net.yagf-0.9.3.2/TODO0000664000175000017500000000004102263216100014006 0ustar parallelsparallelsCheck recognition without polish yagf-0.9.3.2/yagf.png0000664000175000017500000002332402263216100014763 0ustar parallelsparallelsPNG  IHDR``w8sRGBbKGD pHYs ^tIME #<= IDATxyeWy[Þ|s߾oꖺ5R,!`zXI ]8S;6v'eaC0’h!jVÝs|Ξc!@Tw~UޮZZm2dȐ!C 2dȐ!C 2dȐ!C 2dȐ!C 2d}7_F~2îcsjE{">T}5RB~z.^f O?zf%| Uz:=V.n$8Z 足@u58}xU}FΟ-n8 = `bmIX[b38 (*,oe J(n&I/$?w|@ r4W07i,\΄qm#à6ZgWuGU=qkP.CV S|Dppj#D̍C^QIl5a} 4LMfcپc/︓ͯ}V97Q]3Pp ? ^ZN"vlCa &!J%C\ 7T&F.<j5mT|ػ S#p^Madfn\&T,+c~mR榱̰_^nR4JDqv~G_^mU\77ͯ&wi@h ۠®mHa#DA4WŒM85ZVP!ٷtrUPH^h7a0&&jUnp oӏ|{{K4]Csp^5&1#c$rʥyey4Hb6_@D)"d^ z0R@?83bnz?|k'js]!Lµ0_tXcē;!&H*\ ,`BIDZ PBQp aqEIi7ʙ#Gɕs8tf}8n+BF}p,9\D$$(xЕ`bPH, KbLD$4DL2zz)3gΓ_*Y̹G XNyAqdl;bĤ2>y鲁A p _%q<b\Z~ NJIF^Za^:?k>M8cw_ɕr^@Ai!ZE;eZJ!5N) HӔ8tE*hI q]8(d?@cgNzIfե [C` G$#.H7M\!g@ UjDG$Tq  Da/$A16N::2耑˜N*9.-/Dދ{oX[nt {0:-VG3 l)o 1TiH JL]X T&%"QP"Mb&jc Y]˹ݷpQo<&P*p>3c0:N&&G"/[HZ9?PTvɩ"](, lJ8MVbME  趩 Kx _Vop?_f3m}2] Zi0F]KQ$4 \LBl,Qo1֢%mE+6 pH;nDavN|Y>rœtȚõ ˮeT=vp$ۈrU! Ghh]1˸G>Tı6$KE$r#p?aƐ D`RK,>Rߴ?7̀_ 82x)FHKhRR-J ̳ú_q} RK( -A g8AF|zA PdK(!P v.dDC}\4_~׿ p0RT$&HS&`BQ_@(DpB ́8E]D*BT)I"t$J$:O%@ ".W#U XnC"%? leJ痻`tOA8Bi R\"YM}h@\\I̒[@[[+ lU'g&Yj\pHy^f}10D/c :Im XH̕S {ہ}=b1# 5qz LA2"]%݉`FPA.- Kn[|8A)"$ 쾊8Wd@)N"I@zB 4QW(>|ku"Ґ-eX{t@"!p }XC' '? qna<@( 9g4fLUYL[8  ~&,wP16hZ ؊uf@O'Ybmz k1ޠ@+ Q;G(QyGzxR;Q("* +>IဴNbLd) W`]:UHc$cc:P9mtĒ"PQ%0<ғ)fIAH)ұ->+P.=]$!&^H?=ePF6;H%ԪXÌOt(s|pQh''3#HMnQRF{ x& 4UaXnR *Spq@!j/>F]O&쁗$B @JHah: NH )}5>|w"&(Q2w#\3@ZU$HWt B>KQl `ZOYF2@i{7 7QDCL GČ*?J2\J~X!u؉I'K I"/]b95 GK0*>ˈ0Aw+HrjQ6A p%SPƍأG>|Fc8xG"W2W,'ꐌ-] 0ңWtIEL\3NurDŽu@jyP, !RyDF'V"*mS80SV_@w! N Pw"n!!QqLɇ SO`s !z1#-s AjNA qJm2 (n]"q (,o[p8㨧E&t`p$6|5D KO㬞Al#$s}6Wf]Ľ_=3P]K͇7`]F vG0857rݱY^ `CԽ{/D7;8}0? ډp0BQ5ĭ{1q%`{Ƿ ov.^M קVV|b#r/޽$($<,ZQ YMzn U#_w6.]DuTKGBOezv!޶~ǰ{$Q(zQ(y[mrpxJF [8ZqtvmIZTy;i%ɋjCͥ>?w32NOB`|Q(bp[>-\ xQO%yA[G||spy3;sݻ6]T5BXWƹR*xpzY7zE,1A\׼tj x<32@vo E4w Iᕪ"ǁr{ZG~_zF6j%b q LH*jtzי Q݆v%LMIG!8v4Ћ@ nv+y p>_KxD>h7J.$pE$R#tFrS/P]u툷=xKOBO@I,cAȬ%4('s*%n;JU 0`xF&J2A ֕.,ѬEPE*(%IY7NSĹM;ߍ|~S/F8 iew::|saL>E.BxRbaɣ1A긃Տ1-"&eY8H}-i4J\d^hKKdSDs$\Z`Z M@̃ӧ"TlG_Ʀt} M|o Gie5} qF n5fg|?SdhuGoAQ)'ւ\ۆTOù݋0^`5_a3հ DF:0 g#ID3ȠRX1; ) dV 6D2A~zxM B'4Ž-@R?œ9{>?EduL};be@Y I|-\%J4DYscN˛$:v,3aĴJ0^xkD]Ԉf`P,^C:EznϖY݇V$X+AQ@9RL$Y?1q_ydߖcWAu;HT@Iw$bL_#ؠ@Vc ۬.mR9_,^sM+5e& "Mb/ț%=O[#U})~+5Ribrp"v^ا仐s5b0/!ZE?s"3k0Y~떷28|v#DGbz]PY<b$ -BPB̿:- 'ь"DkJĩ83ai 67`Fso_x"I>JcqL3аևV`umDvq!(kq^sG0RSyEM4̺."_WĆ-r[E@2 4YKNc)+hs&_ 5 \܂|.ZP Ľ:G}I曂W>π80YR$,X-D#xu_$[!^?wh`q|B]9IX\F*aqx  Tk\s#"m i!sh10i+8B$+Up=~I+ 2bk^'Q鷨ʩd.*& +Y7Lo"+oq2}- L!fMf֒( /J+wJ '. ZA·HfX[$X#o(cQ"!1=C @ܯЖ?N/?N1 CtCG=JRSR%%m_[ktރ/B}8tyIz]"r|4(t+]x<$12Zr4M{8iH"ٹv'p/IcQ4$M l`+eӈKˈ."֪U~[[G!~MLh~yWO=^_Z3 18i$n5`P 3qHIqjnBNIBEǖ\RNzvbk {b}oIx*>@[㤉[o e_dyaqgP6J4&P h >q*Yى)ab̀1ٍIRk -p4c(Σo{K7R1a§HYh[ H ڤZD}*O'T"S %‹&!L80&qr|P / ^}wexaay0+\N}o{ _'+P-d-ɂ-v\<."JP@X ,PK#";$fHUl8I?_ZVo*bi=/H…ok{0g,JTRlwJޅZ(*1"E#3Idkb \1pˤA=L\X@l @93L[Y-:|  `lD1T΢u)0%VNc4p%{ Q\#hFFc'`7PN: /'gPߌn_oG%~dRAyK9[xZJHĕh/d@*$G%ˆ:HIDAT#r&N{ |v-x,aD򧓳{6N.6Ys#fmdmFTP!F=7@ Eb$&FU "O^U)]TP&M"r8kKǐM8'gd9|ǿW}}\ꆸ*!6cr5Qm=IDlcGx~?$C}Sp| N-edee˟F}ͽ{ }{XVGK*8l[\GֳTBG"<>E8i*•8qz/po<D>};WT`v7o/erZ.|Y>0E edR@K ë#)JE*+*NIح%Q-r6 l4ZNАry'x/z)>4H3|Qc#`ԙ`|4"lצ .:wnepiQ,oh'? kYw)4ri>\AM*_nXLWvp}!T"۶tцc:YV6`#[|~el-ѽ^gdV'R Q+hm( R{ O^5sHB"+Gq.ZZp#0VF#$EEc8ʂZ \3͹53sp`{4LN`GY9A lJ`q+_LƋ,T|2 WoƹfƋGhNFffƲS+ vLabwm&b"ڝ'NZ'3~6eg& t<ɟS?97orR˰@,L 6BaE6Togg 03BWe^257m-\3qi 3~ݬ-gxg|) deܬq#Ho{(C~w88>O7hcNVA5Q ف*: lo$bĤɡ ޾Omflރ0zI֕*(jzթ )o6Y?xӜ׫ n}/PfmT*o}d!ѕρhZ=X;qm%+dkwpw._@jqn6EF~ĆCs?>y$kWGaBq,}ޜ'pNAZxm^_*׵ /}5Q\ؠ ᫡"ūBf/$*j_Iu,}a ?=r 7Q0|?#0dȐ!C 2dȐ!C 2dȐ!C 2dȐ!C 2dȐ6c[IENDB`yagf-0.9.3.2/src/core/0000775000175000017500000000000012317210207015045 5ustar parallelsparallelsyagf-0.9.3.2/src/images/0000775000175000017500000000000012317210207015362 5ustar parallelsparallelsyagf-0.9.3.2/src/translations/0000775000175000017500000000000012317210207016636 5ustar parallelsparallelsyagf-0.9.3.2/src/0000775000175000017500000000000012317210207014115 5ustar parallelsparallelsyagf-0.9.3.2/0000775000175000017500000000000012317210675013337 5ustar parallelsparallels