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; } } } }