private Multistring _GetSingleLine(Bitmap bitmap, Rectangle bounds, int font_color)
        {
            Rectangle  dest_bounds = BitmapAnalyzer.BoundingRectangle(bitmap, bounds, font_color);
            FastBitmap dest_bitmap = new FastBitmap(bitmap);

            int         h;
            Multistring result = RecognizeSingleLine(dest_bitmap, dest_bounds, font_color, out h);

            dest_bitmap.UnlockBitmap();

            PrintPossibleFonts();

            return(result);
        }
        public Multistring GetSingleLineCached(Bitmap bitmap, Rectangle bounds, int font_color, string cache_key)
        {
            if (!font_cache.ContainsKey(cache_key))
            {
                SetPossibleFonts(fonts);
            }
            else
            {
                SetPossibleFonts(font_cache[cache_key]);
            }

            Multistring result = _GetSingleLine(bitmap, bounds, font_color);

            // Update cache
            List <FontCharCollection> new_fonts = new List <FontCharCollection>();

            foreach (FontCharCollection font in possible_fonts)
            {
                new_fonts.Add(font);
            }
            font_cache[cache_key] = new_fonts;

            return(result);
        }
        private Multistring RecognizeSingleLine(FastBitmap dest_bitmap, Rectangle dest_bounds, int font_color, out int max_height)
        {
            match_offsets.Clear();


            max_height = 0;

            int[]  y_offsets = new int[dest_bounds.Width];
            bool[] empty     = new bool[dest_bounds.Width];

            for (int x = 0; x < dest_bounds.Width; x++)
            {
                y_offsets[x] = -1;

                for (int y = 0; y < dest_bounds.Height; y++)
                {
                    if (dest_bitmap[dest_bounds.X + x, dest_bounds.Y + y] == font_color)
                    {
                        y_offsets[x] = y;
                        break;
                    }
                }

                if (y_offsets[x] < 0)
                {
                    empty[x] = true;
                }
            }

            int space_width = TextRenderer.MeasureText("   ", possible_fonts[0].Font).Width - TextRenderer.MeasureText("  ", possible_fonts[0].Font).Width;

            if (space_width < 1)
            {
                space_width = 1;
            }

            //Console.WriteLine(space_width);

            int current_x  = 0;
            int empty_cols = 0;

            int wanted_match_offset = int.MinValue;

            Multistring line_str = new Multistring();

            while (current_x < dest_bounds.Width)
            {
                if (y_offsets[current_x] >= 0)
                {
                    empty_cols = 0;

                    Rectangle bounds = dest_bounds;
                    bounds.X     = dest_bounds.X + current_x;
                    bounds.Width = dest_bounds.Width - current_x;

                    List <FontChar> matches = GetMatches(dest_bitmap, bounds, font_color, wanted_match_offset, y_offsets, current_x);

                    if (matches.Count > 0)
                    {
                        // Remove the recognized character from the destination bitmap
                        dest_bitmap.BlitSingleColor(matches[0].Bitmap,
                                                    new Point(bounds.X, bounds.Y + match_offsets[matches[0].FontChars] + matches[0].CropRect.Y), matches[0].FontColor, (int)0x00FFFFFF);

                        // Update y-offsets after removing the recognized character
                        for (int x = 0; x < matches[0].Bitmap.Width; x++)
                        {
                            y_offsets[current_x + x] = -1;

                            for (int y = 0; y < dest_bounds.Height; y++)
                            {
                                if (dest_bitmap[bounds.X + x, dest_bounds.Y + y] == font_color)
                                {
                                    y_offsets[current_x + x] = y;
                                    break;
                                }
                            }
                        }

                        if (possible_fonts.Count > 1)
                        {
                            possible_fonts_lookup.Clear();
                            possible_fonts.Clear();

                            foreach (FontChar fc in matches)
                            {
                                possible_fonts_lookup[fc.FontChars] = true;
                            }

                            foreach (FontCharCollection font in possible_fonts_lookup.Keys)
                            {
                                possible_fonts.Add(font);
                            }

                            space_width = TextRenderer.MeasureText("   ", possible_fonts[0].Font).Width - TextRenderer.MeasureText("  ", possible_fonts[0].Font).Width;
                            if (space_width < 1)
                            {
                                space_width = 1;
                            }
                        }

                        List <char> possible_chars = new List <char>();

                        foreach (FontChar fc in matches)
                        {
                            if (match_offsets[fc.FontChars] + matches[0].RealHeight > max_height)
                            {
                                max_height = match_offsets[fc.FontChars] + matches[0].RealHeight;
                            }

                            if (!possible_chars.Contains(fc.Character))
                            {
                                possible_chars.Add(fc.Character);
                            }
                        }

                        line_str.Append(possible_chars);
                    }
                }
                else if (empty[current_x])
                {
                    //Console.WriteLine("{0} empty", current_x);

                    empty_cols++;

                    if (empty_cols >= space_width)
                    {
                        line_str.Append(' ');
                        //Console.WriteLine("SPACE");
                        empty_cols = 0;
                    }
                }

                current_x++;
            }

            return(line_str);
        }
        private List <Multistring> RecognizeMultiLine(FastBitmap dest_bitmap, Rectangle dest_bounds, int font_color)
        {
            int max_height = 0;

            foreach (FontCharCollection font in possible_fonts)
            {
                if (font.MaxHeight > max_height)
                {
                    max_height = font.MaxHeight;
                }
            }

            int start_y = 0;

            List <Multistring> lines = new List <Multistring>();

            while (start_y < dest_bounds.Height - 1)
            {
                bool empty = true;

                for (int x = 0; x < dest_bounds.Width; x++)
                {
                    if (dest_bitmap[dest_bounds.X + x, dest_bounds.Y + start_y] == font_color)
                    {
                        empty = false;
                        break;
                    }
                }

                if (!empty)
                {
                    int end_y = start_y + max_height + 1;
                    if (end_y > dest_bounds.Height - 1)
                    {
                        end_y = dest_bounds.Height - 1;
                    }

                    Rectangle bounds = dest_bounds;
                    bounds.Y     += start_y;
                    bounds.Height = end_y - start_y + 1;

                    //Console.WriteLine("fop " + bounds);

                    int h;

                    Multistring line = RecognizeSingleLine(dest_bitmap, bounds, font_color, out h);

                    if (h > 0)
                    {
                        // Console.WriteLine("start_y += " + h);
                        lines.Add(line);
                        start_y += h;
                    }
                    else
                    {
                        start_y++;
                    }
                }
                else
                {
                    start_y++;
                }
            }

            return(lines);
        }
        private Multistring RecognizeSingleLine(FastBitmap dest_bitmap, Rectangle dest_bounds, int font_color, out int max_height)
        {
            match_offsets.Clear();

            max_height = 0;

            int[] y_offsets = new int[dest_bounds.Width];
            bool[] empty = new bool[dest_bounds.Width];

            for (int x = 0; x < dest_bounds.Width; x++)
            {
                y_offsets[x] = -1;

                for (int y = 0; y < dest_bounds.Height; y++)
                {
                    if (dest_bitmap[dest_bounds.X + x, dest_bounds.Y + y] == font_color)
                    {
                        y_offsets[x] = y;
                        break;
                    }
                }

                if (y_offsets[x] < 0) empty[x] = true;
            }

            int space_width = TextRenderer.MeasureText("   ", possible_fonts[0].Font).Width - TextRenderer.MeasureText("  ", possible_fonts[0].Font).Width;
            if (space_width < 1) space_width = 1;

               //Console.WriteLine(space_width);

            int current_x = 0;
            int empty_cols = 0;

            int wanted_match_offset = int.MinValue;

            Multistring line_str = new Multistring();

            while (current_x < dest_bounds.Width)
            {
                if (y_offsets[current_x] >= 0)
                {
                    empty_cols = 0;

                    Rectangle bounds = dest_bounds;
                    bounds.X = dest_bounds.X + current_x;
                    bounds.Width = dest_bounds.Width - current_x;

                    List<FontChar> matches = GetMatches(dest_bitmap, bounds, font_color, wanted_match_offset, y_offsets, current_x);

                    if (matches.Count >0)
                    {

                        // Remove the recognized character from the destination bitmap
                        dest_bitmap.BlitSingleColor(matches[0].Bitmap,
                            new Point(bounds.X, bounds.Y + match_offsets[matches[0].FontChars] + matches[0].CropRect.Y), matches[0].FontColor, (int)0x00FFFFFF);

                        // Update y-offsets after removing the recognized character
                        for (int x = 0; x < matches[0].Bitmap.Width; x++)
                        {
                            y_offsets[current_x + x] = -1;

                            for (int y = 0; y < dest_bounds.Height; y++)
                            {
                                if (dest_bitmap[bounds.X + x, dest_bounds.Y + y] == font_color)
                                {
                                    y_offsets[current_x + x] = y;
                                    break;
                                }
                            }
                        }

                        if (possible_fonts.Count > 1)
                        {
                            possible_fonts_lookup.Clear();
                            possible_fonts.Clear();

                            foreach (FontChar fc in matches)
                            {
                                possible_fonts_lookup[fc.FontChars] = true;
                            }

                            foreach (FontCharCollection font in possible_fonts_lookup.Keys)
                            {
                                possible_fonts.Add(font);
                            }

                            space_width = TextRenderer.MeasureText("   ", possible_fonts[0].Font).Width - TextRenderer.MeasureText("  ", possible_fonts[0].Font).Width;
                            if (space_width < 1) space_width = 1;
                        }

                        List<char> possible_chars = new List<char>();

                        foreach (FontChar fc in matches)
                        {
                            if (match_offsets[fc.FontChars] + matches[0].RealHeight > max_height) max_height = match_offsets[fc.FontChars] + matches[0].RealHeight;

                            if (!possible_chars.Contains(fc.Character)) possible_chars.Add(fc.Character);
                        }

                        line_str.Append(possible_chars);
                    }
                }
                else if (empty[current_x])
                {
                    //Console.WriteLine("{0} empty", current_x);

                    empty_cols++;

                    if (empty_cols >= space_width)
                    {
                        line_str.Append(' ');
                        //Console.WriteLine("SPACE");
                        empty_cols = 0;
                    }
                }

                current_x++;
            }

            return line_str;
        }