/// <summary> /// Draw this text block within specified bounds /// </summary> /// <param name="g">Graphic context</param> /// <param name="bounds">Bounds</param> public void Draw(GdiGraphics g, Rectangle bounds) { if (parts == null || proposedWidth != bounds.Width) { proposedWidth = bounds.Width; RecreateCache(g); } int i = 0; for (; i < parts.Length; i++) { Part currentPart = parts[i]; // TODO: Rework this... if (currentPart.Flags == PartFlags.Alignment) { if (currentPart.Alignment.LineWidth > 0) { bounds.X += (bounds.Width - currentPart.Alignment.LineWidth); } continue; } if (currentPart.Text.Location.Y + currentPart.Text.Height > bounds.Height) { break; } Point p = currentPart.Text.Location; p.X += bounds.X; p.Y += bounds.Y; if (currentPart.Text.Font == null) { if (imageResourceCallback != null) { Image image = imageResourceCallback(currentPart.Text.Value); if (image != null) { g.ManagedGraphics.DrawImage(image, p); } } } else { g.DrawString(currentPart.Text.Value, currentPart.Text.Font, currentPart.Text.Color, p); } } }
/// <summary> /// Measure height of the text block /// </summary> /// <param name="g">Graphic context</param> /// <returns>Height</returns> public int MeasureHeight(GdiGraphics g) { if (parts == null) { RecreateCache(g); } if (parts.Length == 0) { return(0); } Part lastPart = parts[parts.Length - 1]; return(lastPart.Text.Location.Y + lastPart.Text.Height); }
/// <summary> /// Measure block content and recreate cache /// </summary> /// <param name="g">Graphic context</param> private void RecreateCache(GdiGraphics g) { if (text == null) { return; } List <Part> processedParts = new List <Part>(); string unprocessedText = text; Color currentColor = defaultColor; Font currentFont = font; Point currentLocation = Point.Empty; int firstPartOfLine = 0; int lineAlignIndex = -1; while (unprocessedText.Length > 0) { Part part; int formatIndex = unprocessedText.IndexOf("\f[", StringComparison.Ordinal); while (formatIndex == 0) { int formatIndexEnd = unprocessedText.IndexOf(']', formatIndex + 2); if (formatIndexEnd == -1) { throw new InvalidDataException("Missing closing format bracket"); } string formatString = unprocessedText.Substring(formatIndex + 2, formatIndexEnd - (formatIndex + 2)); switch (formatString) { case "Rc": // Reset Color currentColor = defaultColor; break; case "Rs": // Reset Style currentFont = font; break; case "B": // Bold currentFont = new Font(font, currentFont.Style ^ FontStyle.Bold); break; case "I": // Italic currentFont = new Font(font, currentFont.Style ^ FontStyle.Italic); break; case "U": // Underline currentFont = new Font(font, currentFont.Style ^ FontStyle.Underline); break; case "S": // Strikeout currentFont = new Font(font, currentFont.Style ^ FontStyle.Strikeout); break; case "-": // Right Align // TODO: Rework this... if (lineAlignIndex == -1) { lineAlignIndex = processedParts.Count; part = default(Part); part.Flags = PartFlags.Alignment; processedParts.Add(part); } break; default: // Image Resource if (formatString.StartsWith("image:", StringComparison.Ordinal)) { string resourceName = formatString.Substring(6); if (imageResourceCallback == null) { throw new NotSupportedException("ImageResourceCallback is not specified"); } Image image = imageResourceCallback(resourceName); if (image == null) { throw new InvalidDataException("Resource \"" + resourceName + "\" not found"); } if (currentLocation.X + image.Width > proposedWidth) { PerformVerticalAlignment(processedParts, firstPartOfLine); goto End; } part = default(Part); part.Text.Value = resourceName; part.Text.Location = currentLocation; part.Text.Height = image.Height; processedParts.Add(part); currentLocation.X += image.Width; } else // Custom Color { NumberStyles styles; if (formatString.StartsWith("0x", StringComparison.Ordinal)) { formatString = formatString.Substring(2); styles = NumberStyles.HexNumber; } else { styles = NumberStyles.Integer; } uint color; if (!uint.TryParse(formatString, styles, NumberFormatInfo.CurrentInfo, out color)) { throw new InvalidDataException("Unknown format descriptor \"" + formatString + "\""); } currentColor = Color.FromArgb((int)color); } break; } unprocessedText = unprocessedText.Substring(formatIndexEnd + 1); formatIndex = unprocessedText.IndexOf("\f[", StringComparison.Ordinal); } if (unprocessedText.Length == 0) { if (lineAlignIndex != -1) { part = processedParts[lineAlignIndex]; part.Alignment.LineWidth = currentLocation.X; processedParts[lineAlignIndex] = part; } PerformVerticalAlignment(processedParts, firstPartOfLine); break; } int charFit, charFitWidth; string measuredString = (formatIndex > 0 ? unprocessedText.Substring(0, formatIndex) : unprocessedText); Size size = g.MeasureString(measuredString, currentFont, proposedWidth - currentLocation.X, out charFit, out charFitWidth); if (formatIndex != -1 && formatIndex <= charFit) { int endOfLine = unprocessedText.IndexOf('\n', 0, formatIndex); if (endOfLine != -1) { // '\n' is nearest part = default(Part); part.Text.Value = unprocessedText.Substring(0, endOfLine); part.Text.Location = currentLocation; part.Text.Color = currentColor; part.Text.Font = currentFont; part.Text.Height = size.Height; processedParts.Add(part); if (lineAlignIndex != -1) { part = processedParts[lineAlignIndex]; part.Alignment.LineWidth = currentLocation.X + charFitWidth; processedParts[lineAlignIndex] = part; } PerformVerticalAlignment(processedParts, firstPartOfLine); break; } else { // '\f' is nearest part = default(Part); part.Text.Value = unprocessedText.Substring(0, formatIndex); part.Text.Location = currentLocation; part.Text.Color = currentColor; part.Text.Font = currentFont; part.Text.Height = size.Height; processedParts.Add(part); currentLocation.X += charFitWidth; unprocessedText = unprocessedText.Substring(formatIndex); } } else if (charFit < unprocessedText.Length) { if (charFit > 2) { part = default(Part); part.Text.Value = unprocessedText.Substring(0, charFit - 2) + "..."; part.Text.Location = currentLocation; part.Text.Color = currentColor; part.Text.Font = currentFont; part.Text.Height = size.Height; processedParts.Add(part); } PerformVerticalAlignment(processedParts, firstPartOfLine); break; } else { int endOfLine = unprocessedText.IndexOf('\n'); if (endOfLine == -1) { // Value is short enough part = default(Part); part.Text.Value = unprocessedText; part.Text.Location = currentLocation; part.Text.Color = currentColor; part.Text.Font = currentFont; part.Text.Height = size.Height; processedParts.Add(part); if (lineAlignIndex != -1) { part = processedParts[lineAlignIndex]; part.Alignment.LineWidth = (currentLocation.X + charFitWidth); processedParts[lineAlignIndex] = part; lineAlignIndex = -1; } PerformVerticalAlignment(processedParts, firstPartOfLine); unprocessedText = string.Empty; } else { // '\n' found part = default(Part); part.Text.Value = unprocessedText.Substring(0, endOfLine); part.Text.Location = currentLocation; part.Text.Color = currentColor; part.Text.Font = currentFont; part.Text.Height = size.Height; processedParts.Add(part); if (lineAlignIndex != -1) { part = processedParts[lineAlignIndex]; part.Alignment.LineWidth = (currentLocation.X + charFitWidth); processedParts[lineAlignIndex] = part; } PerformVerticalAlignment(processedParts, firstPartOfLine); break; } } } End: if (processedParts.Count > 0) { int last = processedParts.Count - 1; if (processedParts[last].Flags == PartFlags.Alignment) { processedParts.RemoveAt(last); } } parts = processedParts.ToArray(); }