Skip to content

Commit 5f941f3

Browse files
authored
Merge pull request #7 from simmetric/dev
Merge justified alignment option
2 parents 28dd171 + b408fe9 commit 5f941f3

File tree

4 files changed

+93
-62
lines changed

4 files changed

+93
-62
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Renders grid-fit anti-aliased text with variable character and line spacing. Wor
66

77
Features:
88
- Configurable letter spacing and line spacing
9-
- Left, center or right alignment
9+
- Left, center, right or justified alignment
1010
- Text is automatically wrapped to fit the selection
1111
- Supports all TTF fonts
1212
- Up to 8x AA (configurable)

SpacedText/Constants.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public enum TextAlignmentOptions
2929
{
3030
Left,
3131
Center,
32-
Right
32+
Right,
33+
Justify
3334
}
3435

3536
public const string Space = " ";

SpacedText/SpacedText.cs

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ internal class SpacedText : IDisposable
1414
{
1515
//configuration options
1616
public string Text { get; set; }
17-
public string FontFamily { get; set; }
17+
public FontFamily FontFamily { get; set; }
1818
public int FontSize { get; set; }
1919
public double LetterSpacing { get; set; }
2020
public double LineSpacing { get; set; }
2121
public int AntiAliasLevel { get; set; }
2222
public FontStyle FontStyle { get; set; }
23-
public Constants.TextAlignmentOptions TextAlign { get; set; }
23+
public C.TextAlignmentOptions TextAlign { get; set; }
2424

2525
//flow control
2626
public bool IsCancelRequested { get; set; }
@@ -34,7 +34,7 @@ internal class SpacedText : IDisposable
3434

3535
public SpacedText()
3636
{
37-
AntiAliasLevel = Constants.DefaultAntiAliasingLevel;
37+
AntiAliasLevel = C.DefaultAntiAliasingLevel;
3838

3939
ColorMap[] colorMap = {
4040
new ColorMap
@@ -83,12 +83,7 @@ public void RenderText(Rectangle bounds)
8383
resultGr.InterpolationMode = InterpolationMode.HighQualityBicubic;
8484
resultGr.DrawImage(bm, 0f, 0f, Bounds.Width, Bounds.Height);
8585
BufferSurface = Surface.CopyFromBitmap(resultBm);
86-
87-
#if DEBUG
88-
bm.Save("C:\\dev\\buffer.png", ImageFormat.Png);
89-
resultBm.Save("C:\\dev\\result.png", ImageFormat.Png);
90-
#endif
91-
86+
9287
//cleanup
9388
gr.Dispose();
9489
bm.Dispose();
@@ -108,47 +103,80 @@ public void RenderText(Rectangle bounds)
108103
private void DrawLines(List<string> lines, Graphics gr, Font font, double letterSpacing, Bitmap bm)
109104
{
110105
int lineStart = 0;
111-
int lineNum = 0;
112106
foreach (string line in lines)
113107
{
114108
if (IsCancelRequested || lineStart > Bounds.Bottom * AntiAliasLevel)
115109
{
116110
break;
117111
}
118-
119-
lineNum++;
120-
121-
if (!line.Equals(string.Empty))
112+
113+
if (!string.IsNullOrWhiteSpace(line))
122114
{
123115
int left = FontSize / 2;
124116

125-
//measure text
126-
Size textBounds = PInvoked.MeasureString(gr, line, font, letterSpacing);
127-
if (TextAlign != Constants.TextAlignmentOptions.Left)
117+
if (TextAlign != C.TextAlignmentOptions.Justify)
128118
{
129-
if (TextAlign == Constants.TextAlignmentOptions.Center)
119+
//measure text
120+
Size textBounds = PInvoked.MeasureString(gr, line, font, letterSpacing);
121+
if (TextAlign == C.TextAlignmentOptions.Center)
130122
{
131123
left = bm.Width / 2 - textBounds.Width / 2;
132124
}
133-
else if (TextAlign == Constants.TextAlignmentOptions.Right)
125+
else if (TextAlign == C.TextAlignmentOptions.Right)
134126
{
135127
left = bm.Width - (textBounds.Width + FontSize);
136128
}
137-
}
138129

139-
if (textBounds.Width > 0 && textBounds.Height > 0 &&
140-
textBounds.Width * AntiAliasLevel < Constants.MaxBitmapSize && textBounds.Height * AntiAliasLevel < Constants.MaxBitmapSize)
130+
if (textBounds.Width > 0 && textBounds.Height > 0 &&
131+
textBounds.Width * AntiAliasLevel < C.MaxBitmapSize &&
132+
textBounds.Height * AntiAliasLevel < C.MaxBitmapSize)
133+
{
134+
//create new bitmap for line
135+
Bitmap lineBm = new Bitmap(textBounds.Width * AntiAliasLevel,
136+
textBounds.Height * AntiAliasLevel);
137+
Graphics lineGr = Graphics.FromImage(lineBm);
138+
//draw text
139+
PInvoked.TextOut(lineGr, line, 0, 0, font, letterSpacing);
140+
141+
//draw lineBm to bm leaving out black
142+
gr.DrawImage(lineBm, new Rectangle(new Point(left, lineStart), lineBm.Size), 0, 0,
143+
lineBm.Width,
144+
lineBm.Height, GraphicsUnit.Pixel, imgAttr);
145+
lineGr.Dispose();
146+
lineBm.Dispose();
147+
}
148+
}
149+
else
141150
{
151+
//measure text without spaces
152+
string lineWithoutSpaces = line.Replace(" ", string.Empty);
153+
Size textBounds = PInvoked.MeasureString(gr, lineWithoutSpaces, font, letterSpacing);
154+
155+
//calculate width of spaces
156+
int spaceWidth = FontSize;
157+
if (textBounds.Width > bm.Width / 2)
158+
{
159+
spaceWidth = (bm.Width - textBounds.Width - FontSize) /
160+
Math.Max(line.Length - lineWithoutSpaces.Length, 1);
161+
}
162+
142163
//create new bitmap for line
143-
Bitmap lineBm = new Bitmap(textBounds.Width * AntiAliasLevel, textBounds.Height * AntiAliasLevel);
164+
Bitmap lineBm = new Bitmap(bm.Width, bm.Height);
144165
Graphics lineGr = Graphics.FromImage(lineBm);
145-
//draw text
146-
PInvoked.TextOut(lineGr, line, 0, 0, font, letterSpacing);
147-
#if DEBUG
148-
lineBm.Save($"C:\\dev\\line{lineNum}.png", ImageFormat.Png);
149-
#endif
166+
167+
//draw word by word with correct space in between.7
168+
foreach (string word in line.Split(' '))
169+
{
170+
//draw text
171+
PInvoked.TextOut(lineGr, word, left, 0, font, letterSpacing);
172+
173+
Size wordBounds = PInvoked.MeasureString(lineGr, word, font, letterSpacing);
174+
left += wordBounds.Width + spaceWidth;
175+
}
176+
150177
//draw lineBm to bm leaving out black
151-
gr.DrawImage(lineBm, new Rectangle(new Point(left, lineStart), lineBm.Size), 0, 0, lineBm.Width,
178+
gr.DrawImage(lineBm, new Rectangle(new Point(0, lineStart), lineBm.Size), 0, 0,
179+
lineBm.Width,
152180
lineBm.Height, GraphicsUnit.Pixel, imgAttr);
153181
lineGr.Dispose();
154182
lineBm.Dispose();

SpacedText/SpacedTextEffectsPlugin.cs

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
using PaintDotNet.Drawing;
88
using PaintDotNet.Effects;
99
using PaintDotNet.PropertySystem;
10-
using PaintDotNet.SystemLayer;
1110
using System.Collections.Generic;
11+
using System.Drawing.Text;
1212
using System.Linq;
1313
using PaintDotNet.IndirectUI;
1414
using PaintDotNet.Rendering;
@@ -19,15 +19,12 @@
1919
public class SpacedTextEffectsPlugin : PropertyBasedEffect
2020
{
2121
private readonly SpacedText helper;
22-
private readonly List<string> fontFamilies;
22+
private readonly List<FontFamily> fontFamilies;
2323

2424
public SpacedTextEffectsPlugin() : base("Spaced text", null, "Text Formations", EffectFlags.Configurable)
2525
{
2626
fontFamilies =
27-
UIUtil.GetGdiFontNames()
28-
.Where(f => f.Item2 == UIUtil.GdiFontType.TrueType)
29-
.Select(f => f.Item1).OrderBy(f => f)
30-
.ToList();
27+
new InstalledFontCollection().Families.ToList();
3128
helper = new SpacedText();
3229
}
3330

@@ -40,7 +37,7 @@ protected override PropertyCollection OnCreatePropertyCollection()
4037
new DoubleProperty(C.Properties.LetterSpacing.ToString(), C.DefaultLetterSpacing, C.MinLetterSpacing, C.MaxLetterSpacing),
4138
new DoubleProperty(C.Properties.LineSpacing.ToString(), C.DefaultLineSpacing, C.MinLineSpacing, C.MaxLineSpacing),
4239
new Int32Property(C.Properties.AntiAliasLevel.ToString(), C.DefaultAntiAliasingLevel, C.MinAntiAliasingLevel, C.MaxAntiAliasingLevel),
43-
new StaticListChoiceProperty(C.Properties.FontFamily.ToString(), fontFamilies.ToArray<object>(), fontFamilies.FirstIndexWhere(f => f == "Arial" || f == "Helvetica")),
40+
new StaticListChoiceProperty(C.Properties.FontFamily.ToString(), fontFamilies.ToArray<object>(), fontFamilies.FirstIndexWhere(f => f.Name == "Arial" || f.Name == "Helvetica")),
4441
new StaticListChoiceProperty(C.Properties.TextAlignment, Enum.GetNames(typeof(C.TextAlignmentOptions)).ToArray<object>(), 0),
4542
new BooleanProperty(C.Properties.Bold.ToString(), false),
4643
new BooleanProperty(C.Properties.Italic.ToString(), false),
@@ -53,60 +50,65 @@ protected override ControlInfo OnCreateConfigUI(PropertyCollection props)
5350
{
5451
ControlInfo configUI = CreateDefaultConfigUI(props);
5552

56-
configUI.SetPropertyControlValue(Constants.Properties.Text.ToString(), ControlInfoPropertyNames.Multiline, true);
57-
configUI.SetPropertyControlValue(Constants.Properties.Bold.ToString(), ControlInfoPropertyNames.DisplayName, "Formatting");
58-
configUI.SetPropertyControlValue(Constants.Properties.Bold.ToString(), ControlInfoPropertyNames.Description, Constants.Properties.Bold.ToString());
59-
configUI.SetPropertyControlValue(Constants.Properties.Italic.ToString(), ControlInfoPropertyNames.DisplayName, string.Empty);
60-
configUI.SetPropertyControlValue(Constants.Properties.Italic.ToString(), ControlInfoPropertyNames.Description, Constants.Properties.Italic.ToString());
61-
configUI.SetPropertyControlValue(Constants.Properties.Underline.ToString(), ControlInfoPropertyNames.DisplayName, string.Empty);
62-
configUI.SetPropertyControlValue(Constants.Properties.Underline.ToString(), ControlInfoPropertyNames.Description, Constants.Properties.Underline.ToString());
63-
configUI.SetPropertyControlValue(Constants.Properties.Strikeout.ToString(), ControlInfoPropertyNames.DisplayName, string.Empty);
64-
configUI.SetPropertyControlValue(Constants.Properties.Strikeout.ToString(), ControlInfoPropertyNames.Description, Constants.Properties.Strikeout.ToString());
53+
configUI.SetPropertyControlValue(C.Properties.Text.ToString(), ControlInfoPropertyNames.Multiline, true);
54+
configUI.SetPropertyControlValue(C.Properties.Bold.ToString(), ControlInfoPropertyNames.DisplayName, "Formatting");
55+
configUI.SetPropertyControlValue(C.Properties.Bold.ToString(), ControlInfoPropertyNames.Description, C.Properties.Bold.ToString());
56+
configUI.SetPropertyControlValue(C.Properties.Italic.ToString(), ControlInfoPropertyNames.DisplayName, string.Empty);
57+
configUI.SetPropertyControlValue(C.Properties.Italic.ToString(), ControlInfoPropertyNames.Description, C.Properties.Italic.ToString());
58+
configUI.SetPropertyControlValue(C.Properties.Underline.ToString(), ControlInfoPropertyNames.DisplayName, string.Empty);
59+
configUI.SetPropertyControlValue(C.Properties.Underline.ToString(), ControlInfoPropertyNames.Description, C.Properties.Underline.ToString());
60+
configUI.SetPropertyControlValue(C.Properties.Strikeout.ToString(), ControlInfoPropertyNames.DisplayName, string.Empty);
61+
configUI.SetPropertyControlValue(C.Properties.Strikeout.ToString(), ControlInfoPropertyNames.Description, C.Properties.Strikeout.ToString());
6562

66-
configUI.SetPropertyControlValue(Constants.Properties.LetterSpacing.ToString(),
63+
configUI.SetPropertyControlValue(C.Properties.LetterSpacing.ToString(),
6764
ControlInfoPropertyNames.SliderLargeChange, 0.25);
68-
configUI.SetPropertyControlValue(Constants.Properties.LetterSpacing.ToString(),
65+
configUI.SetPropertyControlValue(C.Properties.LetterSpacing.ToString(),
6966
ControlInfoPropertyNames.SliderSmallChange, 0.01);
70-
configUI.SetPropertyControlValue(Constants.Properties.LetterSpacing.ToString(),
67+
configUI.SetPropertyControlValue(C.Properties.LetterSpacing.ToString(),
7168
ControlInfoPropertyNames.UpDownIncrement, 0.01);
7269

73-
configUI.SetPropertyControlValue(Constants.Properties.LineSpacing.ToString(),
70+
configUI.SetPropertyControlValue(C.Properties.LineSpacing.ToString(),
7471
ControlInfoPropertyNames.SliderLargeChange, 0.25);
75-
configUI.SetPropertyControlValue(Constants.Properties.LineSpacing.ToString(),
72+
configUI.SetPropertyControlValue(C.Properties.LineSpacing.ToString(),
7673
ControlInfoPropertyNames.SliderSmallChange, 0.01);
77-
configUI.SetPropertyControlValue(Constants.Properties.LineSpacing.ToString(),
74+
configUI.SetPropertyControlValue(C.Properties.LineSpacing.ToString(),
7875
ControlInfoPropertyNames.UpDownIncrement, 0.01);
7976

77+
PropertyControlInfo fontControl = configUI.FindControlForPropertyName(C.Properties.FontFamily);
78+
foreach (FontFamily fontFamily in fontFamilies)
79+
{
80+
fontControl.SetValueDisplayName(fontFamily, fontFamily.Name);
81+
}
82+
8083
return configUI;
8184
}
8285

8386
protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
8487
{
8588
helper.Text = newToken.GetProperty<StringProperty>(C.Properties.Text.ToString()).Value;
86-
helper.FontFamily = newToken.GetProperty<StaticListChoiceProperty>(C.Properties.FontFamily.ToString()).Value.ToString();
89+
helper.FontFamily = (FontFamily)newToken.GetProperty<StaticListChoiceProperty>(C.Properties.FontFamily.ToString()).Value;
8790
helper.FontSize = newToken.GetProperty<Int32Property>(C.Properties.FontSize.ToString()).Value;
8891
helper.LetterSpacing = newToken.GetProperty<DoubleProperty>(C.Properties.LetterSpacing.ToString()).Value;
8992
helper.LineSpacing = newToken.GetProperty<DoubleProperty>(C.Properties.LineSpacing.ToString()).Value;
9093
helper.AntiAliasLevel = newToken.GetProperty<Int32Property>(C.Properties.AntiAliasLevel.ToString()).Value;
91-
var fontFamily = new FontFamily(helper.FontFamily);
92-
helper.FontStyle = fontFamily.IsStyleAvailable(FontStyle.Regular) ? FontStyle.Regular : fontFamily.IsStyleAvailable(FontStyle.Bold) ? FontStyle.Bold : FontStyle.Italic;
94+
helper.FontStyle = helper.FontFamily.IsStyleAvailable(FontStyle.Regular) ? FontStyle.Regular : helper.FontFamily.IsStyleAvailable(FontStyle.Bold) ? FontStyle.Bold : FontStyle.Italic;
9395
helper.TextAlign = (C.TextAlignmentOptions) Enum.Parse(typeof(C.TextAlignmentOptions),
9496
newToken
9597
.GetProperty<StaticListChoiceProperty>(C.Properties.TextAlignment.ToString())
9698
.Value.ToString());
97-
if (newToken.GetProperty<BooleanProperty>(C.Properties.Bold.ToString()).Value && fontFamily.IsStyleAvailable(FontStyle.Bold))
99+
if (newToken.GetProperty<BooleanProperty>(C.Properties.Bold.ToString()).Value && helper.FontFamily.IsStyleAvailable(FontStyle.Bold))
98100
{
99101
helper.FontStyle |= FontStyle.Bold;
100102
}
101-
if (newToken.GetProperty<BooleanProperty>(C.Properties.Italic.ToString()).Value && fontFamily.IsStyleAvailable(FontStyle.Italic))
103+
if (newToken.GetProperty<BooleanProperty>(C.Properties.Italic.ToString()).Value && helper.FontFamily.IsStyleAvailable(FontStyle.Italic))
102104
{
103105
helper.FontStyle |= FontStyle.Italic;
104106
}
105-
if (newToken.GetProperty<BooleanProperty>(C.Properties.Underline.ToString()).Value && fontFamily.IsStyleAvailable(FontStyle.Underline))
107+
if (newToken.GetProperty<BooleanProperty>(C.Properties.Underline.ToString()).Value && helper.FontFamily.IsStyleAvailable(FontStyle.Underline))
106108
{
107109
helper.FontStyle |= FontStyle.Underline;
108110
}
109-
if (newToken.GetProperty<BooleanProperty>(C.Properties.Strikeout.ToString()).Value && fontFamily.IsStyleAvailable(FontStyle.Strikeout))
111+
if (newToken.GetProperty<BooleanProperty>(C.Properties.Strikeout.ToString()).Value && helper.FontFamily.IsStyleAvailable(FontStyle.Strikeout))
110112
{
111113
helper.FontStyle |= FontStyle.Strikeout;
112114
}
@@ -132,7 +134,7 @@ protected override void OnRender(Rectangle[] renderRects, int startIndex, int le
132134
renderBounds.Intersect(renderRects[i]);
133135

134136
//since TextOut does not support transparent text, we will use the resulting bitmap as a transparency map
135-
CopyRectangle(renderBounds, helper.BufferSurface, base.DstArgs.Surface);
137+
CopyRectangle(renderBounds, helper.BufferSurface, DstArgs.Surface);
136138

137139
//clear the remainder
138140
DstArgs.Surface.Clear(new RectInt32(
@@ -156,7 +158,7 @@ private void CopyRectangle(Rectangle area, Surface buffer, Surface dest)
156158
for (int x = area.Left; x < area.Right; x++)
157159
{
158160
//use the buffer as an alpha map
159-
ColorBgra color = base.EnvironmentParameters.PrimaryColor;
161+
ColorBgra color = EnvironmentParameters.PrimaryColor;
160162
ColorBgra opacitySource = buffer[x - helper.Bounds.Left, y - helper.Bounds.Top];
161163
color.A = opacitySource.R;
162164
dest[x, y] = color;

0 commit comments

Comments
 (0)