Skip to content
Merged
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Paint.NET-Plugins
Plugins for Paint .NET

##Spaced text
## Spaced text
Renders grid-fit anti-aliased text with variable character and line spacing. Works with or without a selection area.

Features:
Expand Down
10 changes: 1 addition & 9 deletions SpacedText/Constants.cs → SpacedText/Data/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpacedTextPlugin
namespace SpacedTextPlugin.Data
{
using System.Diagnostics.PerformanceData;

public class Constants
{
public enum Properties
Expand Down
34 changes: 34 additions & 0 deletions SpacedText/Data/Extent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace SpacedTextPlugin.Data
{
using System.Drawing;

public struct Extent
{
public int VerticalPosition { get; private set; }
public int Left { get; private set; }
public int Right { get; private set; }
public int Width { get; private set; }

public Point Start => new Point(Left, VerticalPosition);

public Point End => new Point(Right, VerticalPosition);

public Extent(int left, int right, int y)
{
Left = left;
Right = right;
Width = right - left;
VerticalPosition = y;
}

public Extent Multiply(int factor)
{
Left *= factor;
Right *= factor;
Width = Right - Left;
VerticalPosition *= factor;

return this;
}
}
}
11 changes: 11 additions & 0 deletions SpacedText/Data/LineData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace SpacedTextPlugin.Data
{
using System.Drawing;

public class LineData
{
public string Text { get; set; }
public Rectangle LineBounds { get; set; }
public Size TextSize { get; set; }
}
}
26 changes: 26 additions & 0 deletions SpacedText/Data/Settings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace SpacedTextPlugin.Data
{
using System.Drawing;

class Settings
{
public string Text { get; set; }
public FontFamily FontFamily { get; set; }
public int FontSize { get; set; }
public double LetterSpacing { get; set; }
public double LineSpacing { get; set; }
public int AntiAliasLevel { get; set; }
public FontStyle FontStyle { get; set; }
public Constants.TextAlignmentOptions TextAlign { get; set; }

public Font GetFont()
{
return new Font(FontFamily, FontSize, FontStyle, GraphicsUnit.Pixel);
}

public Font GetAntiAliasSizeFont()
{
return new Font(FontFamily, FontSize * AntiAliasLevel, FontStyle, GraphicsUnit.Pixel);
}
}
}
23 changes: 23 additions & 0 deletions SpacedText/ExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace SpacedTextPlugin
{
using System.Drawing;

public static class ExtensionMethods
{
public static Rectangle Multiply(this Rectangle r, int factor)
{
return new Rectangle(
r.X * factor,
r.Y * factor,
r.Width * factor,
r.Height * factor);
}

public static Size Multiply(this Size s, int factor)
{
return new Size(
s.Width * factor,
s.Height * factor);
}
}
}
11 changes: 3 additions & 8 deletions SpacedText/Interop.cs → SpacedText/Interop/Interop.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpacedTextPlugin
namespace SpacedTextPlugin.Interop
{
using System;
using System.Drawing;
using System.Runtime.InteropServices;

internal class Interop
internal static class Interop
{
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
public static extern int SetTextCharacterExtra(IntPtr hdc, int nCharExtra);
Expand Down
10 changes: 4 additions & 6 deletions SpacedText/PInvoked.cs → SpacedText/Interop/PInvoked.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
namespace SpacedTextPlugin
namespace SpacedTextPlugin.Interop
{
using System;
using System.Drawing;
using System.Drawing.Drawing2D;

public class PInvoked
internal static class PInvoked
{
public static void TextOut(Graphics g, string text, int x, int y, Font font, double letterSpacing)
{
g.PixelOffsetMode = PixelOffsetMode.Half;
IntPtr hdc = default(IntPtr);
IntPtr fontPtr = default(IntPtr);
try
{
//Grab the Graphic object's handle
hdc = g.GetHdc();
IntPtr hdc = g.GetHdc();
//Set the current GDI font
fontPtr = Interop.SelectObject(hdc, font.ToHfont());
//Set the drawing surface background color
Expand All @@ -36,13 +35,12 @@ public static void TextOut(Graphics g, string text, int x, int y, Font font, dou

public static Size MeasureString(Graphics g, string text, Font font, double letterSpacing)
{
IntPtr hdc = default(IntPtr);
IntPtr fontPtr = default(IntPtr);
Size size = new Size();
try
{
//Grab the Graphic object's handle
hdc = g.GetHdc();
IntPtr hdc = g.GetHdc();
//Set the current GDI font
fontPtr = Interop.SelectObject(hdc, font.ToHfont());
//Set the kerning
Expand Down
138 changes: 138 additions & 0 deletions SpacedText/Renderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
namespace SpacedTextPlugin
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using PaintDotNet;
using SpacedTextPlugin.Data;
using SpacedTextPlugin.Interop;

internal class Renderer : IDisposable
{
private readonly Rectangle selectionBounds;
private readonly Font font;
private readonly Bitmap image;
private readonly Graphics graphics;
private readonly ImageAttributes imageAttributes;

private readonly Settings settings;

public Renderer(Settings settings, PdnRegion selectionRegion)
{
//convert to AA space
selectionBounds = selectionRegion.GetBoundsInt();
var scaledBounds = selectionBounds.Multiply(settings.AntiAliasLevel);
font = settings.GetAntiAliasSizeFont();
image = new Bitmap(scaledBounds.Width, scaledBounds.Height);
graphics = Graphics.FromImage(image);
graphics.Clear(Color.Black);
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

this.settings = settings;

//map color black to transparent
ColorMap[] colorMap = {
new ColorMap
{
OldColor = Color.Black,
NewColor = Color.Transparent
}
};
imageAttributes = new ImageAttributes();
imageAttributes.SetRemapTable(colorMap);
}

public Bitmap Draw(IEnumerable<LineData> lines)
{
foreach (var line in lines)
{
//create bitmap for line
using (var lineImage = new Bitmap(line.TextSize.Width, line.TextSize.Height))
{
var lineGraphics = Graphics.FromImage(lineImage);

if (settings.TextAlign != Constants.TextAlignmentOptions.Justify)
{
PInvoked.TextOut(lineGraphics, line.Text, 0, 0, font, settings.LetterSpacing);
}
else
{
Justify(line, lineGraphics);
}

//draw line bitmap to image
graphics.DrawImage(lineImage,
new Rectangle(
line.LineBounds.Location,
lineImage.Size
), /* destination rect */
0, 0, /* source coordinates */
lineImage.Width,
lineImage.Height,
GraphicsUnit.Pixel,
imageAttributes
);

#if DEBUG
//draw rectangles
graphics.DrawRectangle(Pens.White, line.LineBounds);
graphics.DrawLine(Pens.Gray,
line.LineBounds.X,
line.LineBounds.Y + line.TextSize.Height / 2,
line.LineBounds.X + line.TextSize.Width,
line.LineBounds.Y + line.TextSize.Height / 2
);
#endif
lineGraphics.Dispose();
}
}

//create selection-sized bitmap
var resultImage = new Bitmap(selectionBounds.Width, selectionBounds.Height);
var resultGraphics = Graphics.FromImage(resultImage);
resultGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
resultGraphics.DrawImage(image, 0, 0, selectionBounds.Width, selectionBounds.Height);

return resultImage;
}

private void Justify(LineData line, Graphics lineGraphics)
{
var lineTextWithoutSpaces = line.Text.Replace(Constants.Space, string.Empty);
var lineSizeWithoutSpaces = PInvoked.MeasureString(lineGraphics, lineTextWithoutSpaces, font,
settings.LetterSpacing);
var spaceWidth = (line.TextSize.Width - lineSizeWithoutSpaces.Width) /
Math.Max((line.Text.Length - lineTextWithoutSpaces.Length), 1);
if (spaceWidth > font.Size * 3)
{
PInvoked.TextOut(lineGraphics, line.Text, 0, 0, font, settings.LetterSpacing);
}
else
{
var x = 0;

foreach (string word in line.Text.Split(Constants.SpaceChar))
{
var wordSize = PInvoked.MeasureString(lineGraphics, word, font, settings.LetterSpacing);
PInvoked.TextOut(lineGraphics, word, x, 0, font, settings.LetterSpacing);
x += wordSize.Width + spaceWidth;
}
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool isDisposing)
{
graphics.Dispose();
image.Dispose();
}
}
}
Loading