using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
namespace Inet.Viewer.Data
{
///
/// Represents a drawn text block. Provides methods for bounding box functionality.
///
public class TextBlock
{
private System.Drawing.Drawing2D.Matrix matrix;
private int docX, docY;
private RectangleF[] characterBounds;
private Size size;
private Font font;
private string text;
private RectangleF bbox;
///
/// Creates a new text block.
///
/// the string
/// the transformation matrix
/// the font
/// the size of the text block, in pixels
/// the X location of the text block in document space (twips)
/// the Y location of the text block in document space (twips)
public TextBlock(string text, System.Drawing.Drawing2D.Matrix matrix, Font font, Size size, int docX, int docY)
{
this.text = text;
this.matrix = matrix;
this.font = font;
this.size = size;
this.docX = docX;
this.docY = docY;
// compute the bounding box from size and transform
using (var image = new Bitmap(1, 1))
{
using (var g = Graphics.FromImage(image))
{
Region region = new Region(new RectangleF(0, 0, size.Width, size.Height));
region.Transform(matrix);
bbox = region.GetBounds(g);
}
}
}
///
/// Creates a new text block with individual character bounds. This constructor is
/// used for non-standard text layouts, for example rotated glyphs.
///
/// the string
/// the transformation matrix
/// the font
/// array of character bounding rectangles, in pixels
/// the X location of the text block in document space (twips)
/// the Y location of the text block in document space (twips)
public TextBlock(string text, Matrix matrix, System.Drawing.Font font, RectangleF[] charBounds, int docX, int docY)
{
this.text = text;
this.matrix = matrix;
this.font = font;
this.characterBounds = charBounds;
this.docX = docX;
this.docY = docY;
// compute the bounding box of the full block
bbox = characterBounds[0];
for (int i = 1; i < characterBounds.Length; i++)
{
bbox = RectangleF.Union(bbox, characterBounds[i]);
}
}
///
/// Checks if the specified rectangle intersects with any characters of this text block. If there
/// are such characters a range instance will be returned.
///
/// the rectangle in pixels
/// an instance describing the intersecting characters, or null if this
/// text block does not intersect with the given rectangle
public TextBlockRange ComputeRangeForArea(Rectangle rectangle)
{
if (!bbox.IntersectsWith(rectangle))
{
return null;
}
RectangleF[] charBounds = CharacterBounds;
RectangleF rectangleF = (RectangleF)rectangle;
int first = -1;
for (int i = 0; i < Text.Length; i++)
{
if (rectangleF.IntersectsWith(charBounds[i]))
{
if (first == -1)
{
first = i;
}
}
else if (first != -1)
{
return new TextBlockRange(this, first, i);
}
}
if (first == -1)
{
return null;
}
return new TextBlockRange(this, first, Text.Length);
}
///
/// The transformation matrix of this text block. The matrix includes the location.
///
public Matrix Transform
{
get
{
return matrix;
}
}
///
/// Checks of the specified location matches with the location of this text block.
///
/// the X coordinate, in twips
/// the Y coordinate, in twips
///
public bool HasDocumentLocation(int x, int y)
{
return Math.Abs(docX - x) <= 1 && Math.Abs(docY - y) <= 1;
}
///
/// The string of this text block
///
public string Text { get { return text; } }
///
/// The font of the text in this block.
///
public Font Font { get { return font; } }
///
/// Array with bounding boxes of the individual characters in this text block.
///
public RectangleF[] CharacterBounds
{
get
{
if (characterBounds == null)
{
characterBounds = new RectangleF[Text.Length];
PointF[] p = new PointF[2];
StringFormat format = (StringFormat)StringFormat.GenericTypographic.Clone();
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoWrap | StringFormatFlags.FitBlackBox;
using (var image = new Bitmap(1, 1))
{
using (var g = Graphics.FromImage(image))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
for (int k = 0; k < Text.Length; k += 32)
{
int n = Math.Min(32, Text.Length - k);
CharacterRange[] ranges = new CharacterRange[n];
for (int i = 0; i < n; i++)
{
ranges[i] = new CharacterRange(k + i, 1);
}
format.SetMeasurableCharacterRanges(ranges);
Region[] rranges = g.MeasureCharacterRanges(Text, Font, new RectangleF(new PointF(0, 0), size), format);
for (int i = 0; i < n; i++)
{
RectangleF rect = rranges[i].GetBounds(g);
p[0] = new PointF(rect.Left, rect.Top);
p[1] = new PointF(rect.Right, rect.Bottom);
matrix.TransformPoints(p);
characterBounds[k + i] = new RectangleF(p[0], new SizeF(p[1].X - p[0].X, p[1].Y - p[0].Y));
}
}
}
}
}
return characterBounds;
}
}
}
}