// Gets the bounds of each character in a line of text.
        // The line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges).
        IEnumerable <RectangleF> GetCharExtents(string text, int height, int line_start, int line_length,
                                                RectangleF layoutRect, IntPtr native_graphics, IntPtr native_font, IntPtr native_string_format)
        {
            RectangleF rect     = new RectangleF();
            int        line_end = line_start + line_length;

            while (line_start < line_end)
            {
                //if (text[line_start] == '\n' || text[line_start] == '\r')
                //{
                //    line_start++;
                //    continue;
                //}

                int num_characters = (line_end - line_start) > GdiPlus.MaxMeasurableCharacterRanges ?
                                     GdiPlus.MaxMeasurableCharacterRanges :
                                     line_end - line_start;
                int status = 0;

                for (int i = 0; i < num_characters; i++)
                {
                    characterRanges[i] = new CharacterRange(line_start + i, 1);

                    IntPtr region;
                    status     = GdiPlus.CreateRegion(out region);
                    regions[i] = region;
                    Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));
                }

                status = GdiPlus.SetStringFormatMeasurableCharacterRanges(native_string_format, num_characters, characterRanges);
                Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));

                status = GdiPlus.MeasureCharacterRanges(native_graphics, text, text.Length,
                                                        native_font, ref layoutRect, native_string_format, num_characters, regions);
                Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));

                for (int i = 0; i < num_characters; i++)
                {
                    GdiPlus.GetRegionBounds(regions[i], native_graphics, ref rect);
                    Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));
                    GdiPlus.DeleteRegion(regions[i]);
                    Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));

                    rect.Y += height;
                    yield return(rect);
                }

                line_start += num_characters;
            }
        }
        // Gets the bounds of each character in a line of text.
        // Each line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges).
        IEnumerable <RectangleF> MeasureGlyphExtents(
            ref TextBlock block, string text,
            IntPtr native_graphics, IntPtr native_font, IntPtr native_string_format,
            ref RectangleF layoutRect, out float max_width, out float max_height)
        {
            measured_glyphs.Clear();
            max_width  = layoutRect.Left;
            max_height = layoutRect.Top;
            float last_line_width = 0, last_line_height = 0;

            int current = 0;

            while (current < text.Length)
            {
                int num_characters = (text.Length - current) > GdiPlus.MaxMeasurableCharacterRanges ?
                                     GdiPlus.MaxMeasurableCharacterRanges :
                                     text.Length - current;
                int status = 0;

                // Prepare the character ranges and region structs for the measurement.
                for (int i = 0; i < num_characters; i++)
                {
                    if (text[current + i] == '\n' || text[current + i] == '\r')
                    {
                        throw new NotSupportedException();
                    }

                    characterRanges[i] = new CharacterRange(current + i, 1);

                    IntPtr region;
                    status     = GdiPlus.CreateRegion(out region);
                    regions[i] = region;
                    Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));
                }

                status = GdiPlus.SetStringFormatMeasurableCharacterRanges(native_string_format, num_characters, characterRanges);
                Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));

                status = GdiPlus.MeasureCharacterRanges(native_graphics, text, text.Length,
                                                        native_font, ref layoutRect, native_string_format, num_characters, regions);
                Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));

                // Read back the results of the measurement.
                for (int i = 0; i < num_characters; i++)
                {
                    RectangleF rect = new RectangleF();

                    GdiPlus.GetRegionBounds(regions[i], native_graphics, ref rect);
                    Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));
                    GdiPlus.DeleteRegion(regions[i]);
                    Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status));

                    if (rect.Bottom > max_height)
                    {
                        max_height = rect.Bottom;
                    }
                    if (rect.Right > max_width)
                    {
                        max_width = rect.Right;
                    }

                    if (rect.X > last_line_width)
                    {
                        last_line_width = rect.X;
                    }
                    if (rect.Y > last_line_height)
                    {
                        last_line_height = rect.Y;
                    }

                    measured_glyphs.Add(rect);
                }

                current += num_characters;
            }

            // Make sure the current height is updated, if the the current line has wrapped due to word-wraping.
            // Otherwise, the next line will overlap with the current one.
            if (measured_glyphs.Count > 1)
            {
                if ((block.Direction & TextDirection.Vertical) == 0)
                {
                    if (layoutRect.Y < last_line_height)
                    {
                        layoutRect.Y = last_line_height;
                    }
                }
                else
                {
                    if (layoutRect.X < last_line_width)
                    {
                        layoutRect.X = last_line_width;
                    }
                }
            }

            // Mono's GDI+ implementation suffers from an issue where the specified layoutRect is not taken into
            // account. We will try to improve the situation by moving text to the correct location on this
            // error condition. This will not help word wrapping, but it is better than nothing.
            // TODO: Mono 2.8 is supposed to ship with a Pango-based GDI+ text renderer, which should not
            // suffer from this bug. Verify that this is the case and remove the hack.
            if (Configuration.RunningOnMono && (layoutRect.X != 0 || layoutRect.Y != 0) && measured_glyphs.Count > 0)
            {
                for (int i = 0; i < measured_glyphs.Count; i++)
                {
                    RectangleF rect = measured_glyphs[i];
                    rect.X            += layoutRect.X;
                    rect.Y            += layoutRect.Y;
                    measured_glyphs[i] = rect;
                }
            }

            return(measured_glyphs);
        }