/// <summary> /// Draw an outlined text on a graphic. /// Draw a text using an outline in it, from custom CardGraphicComponent values. These are used to get position of text and destination picture/graphic. /// </summary> /// <param name="g">Graphic to draw the text</param> /// <param name="text">Text to draw</param> /// <param name="canvas">Values for position, font, etc. of the text</param> /// <param name="parent">If canvas has a parent, use it for relative position of the text</param> /// <param name="centered">If text must be drawn centered or not</param> public static void drawOutlineText(Graphics g, string text, CardGraphicComponent canvas, CardGraphicComponent parent, bool centered=false) { // Check parent to get proper offsets var offsetTop = 0; var offsetLeft = 0; if (parent != null) { offsetLeft = parent.Left; offsetTop = parent.Top; } // set atialiasing for drawing g.SmoothingMode = SmoothingMode.HighQuality; // AntiAlias g.InterpolationMode = InterpolationMode.HighQualityBicubic; // High g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; g.PixelOffsetMode = PixelOffsetMode.HighQuality; // Create GraphicsPath to draw text using (GraphicsPath path = new GraphicsPath()) { // Calculate position in case text must be draw centered or not float txtWidth = 0; if (centered) txtWidth = getStringWidth(g, text, new Font(canvas.font.FontFamily, canvas.font.Size, canvas.font.Style, GraphicsUnit.Point, ((byte)(0)))) / 2; // Add string to path path.AddString(text, canvas.font.FontFamily, (int)canvas.font.Style, canvas.font.Size, new Point(canvas.Left + offsetLeft - (int)txtWidth, canvas.Top + offsetTop), StringFormat.GenericTypographic); // Draw text using this pen. This does the trick (with the path) to draw the outline using (Pen pen = new Pen(canvas.borderColor, canvas.Outline)) { pen.LineJoin = LineJoin.Round; g.DrawPath(pen, path); g.FillPath(canvas.textColor, path); } } }
/// <summary> /// Render string text in graphics over a path using given font. /// Path will be a cuve (the one defined in main class). /// </summary> /// <param name="g">Graphic to draw to.</param> /// <param name="text">Text to draw.</param> /// <param name="component">CardGraphicComponent to get style parameters to draw text (font, size...)</param> /// <param name="squish">Used to adjust spaces between chars due to MeasureString</param> /// <param name="debug">If true, it draws a red curve as reference. Default: do not draw.</param> public void renderText(System.Drawing.Graphics g, string text, CardGraphicComponent component, bool squish = false, bool debug = false) { // Define vars Font fontR = Graphic.findBestFitFont(g, text, component.font, getPathLength(pathSegments), squish); var width = Graphic.getStringWidth(g, text, fontR, squish); // If text too big and it was adjusted, we need adjust the vertical offset too var adjust = component.font.Size - fontR.Size; var middleX = (last.X + first.X) / 2; var middleY = getY(middleX); var startX = middleX - (width / 2); var startY = getY(startX); var len = text.Length; var space = g.MeasureString(" ", fontR, Point.Empty, StringFormat.GenericDefault).Width; var factor = (squish) ? Config.align : 0f; var flaw = 2 * fontR.Size / component.font.Size; // First char needs an offset, idk why :S float w; // set atialiasing g.SmoothingMode = SmoothingMode.HighQuality; // AntiAlias g.InterpolationMode = InterpolationMode.HighQualityBicubic; // High g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; g.PixelOffsetMode = PixelOffsetMode.HighQuality; // Go along string and render chars for (int characterIndex = 0; characterIndex < len; characterIndex++) { char @char = text[characterIndex]; w = (@char == ' ') ? space : g.MeasureString(@char.ToString(), fontR, Point.Empty, StringFormat.GenericTypographic).Width - factor; // put char in (startX, startY) using (GraphicsPath characterPath = new GraphicsPath()) { characterPath.AddString(@char.ToString(), fontR.FontFamily, (int)fontR.Style, fontR.Size, Point.Empty, StringFormat.GenericTypographic); // Transformation matrix to move the character to the correct location. // Note that all actions on the Matrix class are prepended, so we apply them in reverse. var transform = new Matrix(); // Translate to the final position transform.Translate(startX + flaw, startY + adjust); // Rotate the character var cathetus = getY(startX + w) - startY; var hypothenuse = Math.Sqrt(Math.Pow(w - flaw, 2) + Math.Pow(cathetus, 2)); var angle = (factor == 0) ? 0f : Math.Asin(cathetus / hypothenuse) * 180f / (float)Math.PI; transform.Rotate((float)angle); // Apply transformations characterPath.Transform(transform); // Draw the character using (Pen pen = new Pen(component.borderColor, component.Outline)) { pen.LineJoin = LineJoin.Round; g.DrawPath(pen, characterPath); g.FillPath(component.textColor, characterPath); } } // Skip offset for the other chars flaw = 0; // Update new position startX += w; startY = getY(startX); } // Show path line if debugging if (debug) { // Get path of N points PointF[] points = getPath(pathSegments); using (GraphicsPath path = new GraphicsPath()) { // Create curve path.AddCurve(points); // And draw it to the screen. using (Pen pen = new Pen(Color.Red, 2)) { g.DrawPath(pen, path); } } } }