VeLC Posted May 13, 2022 Share Posted May 13, 2022 (edited) Resizes and draws line/s of text to a specified width Found in Effects > Text Formations Versions: 1.0(20220513) - Initial Release 1.1(20220519) - Add 'Convert to all caps' and 'Set spacing from baseline' options. Prevent crash when selected font does not contain glyph/s of typed letters. C# Code Snippet (Modified from CodeLab generated code) License: LPGL-3.0 Spoiler protected override void OnRender(Rectangle[] rois, int startIndex, int length) { if (length == 0 || IsCancelRequested) return; Render(DstArgs.Surface, SrcArgs.Surface, EnvironmentParameters.SelectionBounds); } #region UICode private MultiLineTextboxControl text = ""; // [1000] private CheckboxControl allCaps = false; // Convert to all caps private FontFamily fontFamily = new("Arial"); // private CheckboxControl bold = false; // Bold private CheckboxControl italic = false; // Italic private IntSliderControl targetWidth = 150; // [1,1000] Width private IntSliderControl spacing = 15; // [0,100] Spacing private CheckboxControl baseline = false; // Set spacing from baseline private PanSliderControl center = Pair.Create(0.000, 0.000); // Center #endregion private void Render(Surface dst, Surface src, Rectangle rect) { dst.CopySurface(src, rect.Location, rect); if (text == string.Empty || EnvironmentParameters.PrimaryColor.A == 0) return; using Graphics g = new RenderArgs(dst).Graphics; using Region gClipRegion = new(rect); g.Clip = gClipRegion; g.SmoothingMode = SmoothingMode.AntiAlias; g.TextRenderingHint = TextRenderingHint.AntiAlias; float centerX = (float)(rect.Left + (center.First + 1) / 2 * rect.Width); float centerY = (float)(rect.Top + (center.Second + 1) / 2 * rect.Height); g.TranslateTransform(centerX, centerY); StringSplitOptions options = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries; string[] lines = text.Split(new string[] { "\r\n", "\r", "\n" }, options); Pair<Bitmap, Rectangle>[] pairs = new Pair<Bitmap, Rectangle>[lines.Length]; int totalHeight = 0; try { for (int i = 0; i < lines.Length; ++i) { string line = allCaps ? lines[i].ToUpperInvariant() : lines[i]; pairs[i] = MakeBitmap(g, line); if (IsCancelRequested) return; totalHeight += pairs[i].Second.Height; } totalHeight += (lines.Length - 1) * spacing; g.TranslateTransform(targetWidth / -2f, totalHeight / -2f); foreach (Pair<Bitmap, Rectangle> pair in pairs) { if (IsCancelRequested) return; g.DrawImageUnscaled(pair.First, -pair.Second.Left, -pair.Second.Top); g.TranslateTransform(0, pair.Second.Height + spacing); } } finally { foreach (Pair<Bitmap, Rectangle> pair in pairs) { pair.First?.Dispose(); } } } private Pair<Bitmap, Rectangle> MakeBitmap(Graphics g, string line) { FontStyle fontStyle = FontStyle.Regular; if (bold) fontStyle |= FontStyle.Bold; if (italic) fontStyle |= FontStyle.Italic; using Brush brush = new SolidBrush(EnvironmentParameters.PrimaryColor); StringFormat format = new(StringFormat.GenericDefault); format.FormatFlags |= StringFormatFlags.NoFontFallback; Font refFont = new(fontFamily, 10, fontStyle, GraphicsUnit.Pixel); float refWidth = g.MeasureString(line, refFont, short.MaxValue, format).Width; while (!IsCancelRequested) { Font newFont = new(fontFamily, refFont.Size * targetWidth / refWidth, fontStyle, GraphicsUnit.Pixel); SizeF newSize = g.MeasureString(line, newFont, short.MaxValue, format); int width = (int)Math.Ceiling(newSize.Width); int height = (int)Math.Ceiling(newSize.Height); Bitmap bmp = new(width, height, PixelFormat.Format32bppArgb); using Graphics bmpG = Graphics.FromImage(bmp); bmpG.SmoothingMode = SmoothingMode.AntiAlias; bmpG.TextRenderingHint = TextRenderingHint.AntiAlias; bmpG.Clear(Color.Transparent); bmpG.DrawString(line, newFont, brush, 0, 0, format); Rectangle boundingBox = GetBoundingBox(bmp); if (boundingBox.Width == targetWidth) { if (baseline) { int bottom = GetBaseline(g, newFont, brush, format); boundingBox = Rectangle.FromLTRB(boundingBox.Left, boundingBox.Top, boundingBox.Right, bottom); } newFont.Dispose(); refFont.Dispose(); return new(bmp, boundingBox); } else { bmp.Dispose(); refFont.Dispose(); refFont = newFont; refWidth = boundingBox.Width; } } refFont.Dispose(); return default; } private static int GetBaseline(Graphics g, Font font, Brush brush, StringFormat format) { SizeF size = g.MeasureString("Z", font, short.MaxValue, format); int width = (int)Math.Ceiling(size.Width); int height = (int)Math.Ceiling(size.Height); using Bitmap bmp = new(width, height, PixelFormat.Format32bppArgb); using Graphics bmpG = Graphics.FromImage(bmp); bmpG.SmoothingMode = SmoothingMode.AntiAlias; bmpG.TextRenderingHint = TextRenderingHint.AntiAlias; bmpG.Clear(Color.Transparent); bmpG.DrawString("Z", font, brush, 0, 0, format); return GetBoundingBox(bmp).Bottom; } private static Rectangle GetBoundingBox(Bitmap bmp) { Rectangle rect = new(0, 0, bmp.Width, bmp.Height); BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); byte[] bytes = new byte[bmpData.Stride * bmpData.Height]; Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length); int bottom = GetBottom(bytes, bmpData); int left = GetLeft(bytes, bmpData); int right = GetRight(bytes, bmpData); int top = GetTop(bytes, bmpData); bmp.UnlockBits(bmpData); return Rectangle.FromLTRB(left, top, right, bottom); } private static int GetBottom(byte[] bytes, BitmapData data) { for (int row = data.Height - 1; row >= 0; --row) { for (int col = 0; col < data.Width; ++col) { int alpha = bytes[row * data.Stride + col * 4 + 3]; if (alpha > 0) return row; } } return data.Height - 1; } private static int GetLeft(byte[] bytes, BitmapData data) { for (int col = 0; col < data.Width; ++col) { for (int row = 0; row < data.Height; ++row) { int alpha = bytes[row * data.Stride + col * 4 + 3]; if (alpha > 0) return col; } } return 0; } private static int GetRight(byte[] bytes, BitmapData data) { for (int col = data.Width - 1; col >= 0; --col) { for (int row = 0; row < data.Height; ++row) { int alpha = bytes[row * data.Stride + col * 4 + 3]; if (alpha > 0) return col; } } return data.Width - 1; } private static int GetTop(byte[] bytes, BitmapData data) { for (int row = 0; row < data.Height; ++row) { for (int col = 0; col < data.Width; ++col) { int alpha = bytes[row * data.Stride + col * 4 + 3]; if (alpha > 0) return row; } } return 0; } SlabText.zip Edited May 19, 2022 by VeLC Version 1.1 6 2 Quote Link to comment Share on other sites More sharing options...
VeLC Posted May 13, 2022 Author Share Posted May 13, 2022 (edited) Feel free to suggest a better name for the plugin. There seems to be no technical term for this effect / format as per this old StackExchange. Current "Slab" name is a reference to Erik Loyer's more advanced Slabtype Algorithm that achieves something similar. Edited May 19, 2022 by VeLC typo Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted May 14, 2022 Share Posted May 14, 2022 I like this Block Text or Text Block? Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
VeLC Posted May 19, 2022 Author Share Posted May 19, 2022 (edited) Version 1.1 - Add 'Convert to all caps' and 'Set spacing from baseline' options. Prevent crash when selected font does not contain glyph/s of typed letter/s. Edited May 19, 2022 by VeLC typo 2 1 Quote Link to comment Share on other sites More sharing options...
BDP Posted May 19, 2022 Share Posted May 19, 2022 I really like this effect - thank you Brian Quote Link to comment Share on other sites More sharing options...
Welsh Yellow Cheddar Posted May 20, 2022 Share Posted May 20, 2022 I think I will be making posters for my classroom walls with this plugin. Thanks a lot. 1 Quote Link to comment Share on other sites More sharing options...
NinthDesertDude Posted May 31, 2022 Share Posted May 31, 2022 Looks like a useful plugin, nice. Slab text is a nice name since it can't be confused with anything else. I would call it something like stacked text. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.