我的图像有一些区域(矩形)用于文本。文本可以是任意长度,一个单词或长字符串(必须包装)。我需要:
主要要求:获取fontSize的最大值,文本应填充所有矩形。
我做了什么。 我在NuGet:ImageProcessor中找到了不错的第三方。但是,ImageFactory.Watermark只获取起始点,而不是矩形。
好的,我已经实施了自己的解决方案:
问题1:我在WPF中使用来自GDI +的Graphics.MeasureString。这不是线程安全的,我必须使用锁。
问题2:MeasureString返回错误的高度,好像文本有很大的余量。例如,我有3个相互靠近的矩形:
渲染后,我看到它们之间有很大的空间。
我很高兴能得到我需要的第三方!如果不是,修复我的代码也会很棒。谢谢!
代码:
private static void RenderText(string text, RectangleF rectangle,
string fontFamily, int maximumFontSize, Color textColor, Graphics graphics)
{
var font = new Font(fontFamily, maximumFontSize, FontStyle.Regular);
var stringFormat = new StringFormat
{
// LineAlignment = StringAlignment.Center,
FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.FitBlackBox,
Trimming = StringTrimming.None
};
var previewSize = graphics.MeasureString(text, font,
new SizeF(rectangle.Width, rectangle.Height), stringFormat);
if (previewSize.Height > rectangle.Height)
{
var scale = Math.Sqrt(rectangle.Height / previewSize.Height);
font = new Font(fontFamily, (float) (maximumFontSize * scale), FontStyle.Regular);
}
graphics.DrawString(text, font, new SolidBrush(textColor), rectangle, stringFormat);
}
附加说明:
我试图解决的问题是同时进行文本换行和字体缩放。请参阅this sketch作为示例。
答案 0 :(得分:1)
“因为(..)用户在UI中看不到带有文字的图像”
这并不重要。窗口/屏幕只是一个表面。您可以使用WPF组件打印到打印机或渲染到位图。您无需在UI上看到它们。
请参阅this answer,了解如何将屏幕外组件设置为给定大小,然后将其渲染为位图。
“即使所有这些都发生在UI中:( ..)TextBlock如何填充文本(var length)以获得固定控制高度”
如果我正确地阅读了您的请求,那么您不是在进行高度包装,而是缩放字体,因此适合它。
在这种情况下,就像Bijington在评论中所说,只需使用ViewBox
组件。请参阅下面的代码 - 我将ViewBox设置为根据可用区域向上或向下缩放。玩窗口,看看“文字”如何“缩放”。
实际上,它不是文本本身 - 所有文本框都具有相同的风格。这是正在缩放的文本所在的区域。可以把它想象成应用于ViewBox内容的智能“缩放”。
<Window x:Class="stack43479959.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:stack43479959"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="3" Columns="3">
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="5"
BorderThickness="1" BorderBrush="Red">
<Viewbox StretchDirection="Both" Stretch="Uniform">
<TextBlock TextWrapping="WrapWithOverflow">
Lorem ipsum dolor sit amet, consectetur adipiscing elit,<LineBreak/>
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.<LineBreak/>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.<LineBreak/>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.<LineBreak/>
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</TextBlock>
</Viewbox>
</Border>
</Grid>
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="5"
BorderThickness="1" BorderBrush="Red">
<Viewbox StretchDirection="Both" Stretch="Uniform">
<TextBlock TextWrapping="WrapWithOverflow">
Lorem ipsum dolor sit amet,<LineBreak/>
sed do eiusmod tempor
</TextBlock>
</Viewbox>
</Border>
</Grid>
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="5"
BorderThickness="1" BorderBrush="Red">
<Viewbox StretchDirection="Both" Stretch="Uniform">
<TextBlock TextWrapping="WrapWithOverflow">
Lorem ipsum<LineBreak/>
dolor sit amet
</TextBlock>
</Viewbox>
</Border>
</Grid>
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="5"
BorderThickness="1" BorderBrush="Red">
<Viewbox StretchDirection="Both" Stretch="Uniform">
<TextBlock TextWrapping="WrapWithOverflow">
Lorem ipsum<LineBreak/>
dolor sit amet
</TextBlock>
</Viewbox>
</Border>
</Grid>
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="5"
BorderThickness="1" BorderBrush="Red">
<Viewbox StretchDirection="Both" Stretch="Uniform">
<TextBlock TextWrapping="WrapWithOverflow">
L<LineBreak/>
O
</TextBlock>
</Viewbox>
</Border>
</Grid>
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="5"
BorderThickness="1" BorderBrush="Red">
<Viewbox StretchDirection="Both" Stretch="Uniform">
<TextBlock TextWrapping="WrapWithOverflow">
L
</TextBlock>
</Viewbox>
</Border>
</Grid>
</UniformGrid>
</Window>
答案 1 :(得分:0)
感谢大家的帮助。我完成了这个任务,非常酷!我无法分享所有代码(商业项目),但有些关键时刻(我浪费了很多时间)。
使用WPF进行渲染 - 好主意。只需创建网格+图像+文本块(不在UI中,在内存中)。 textBlock示例:
var titleTextBlock = new TextBlock
{
FontSize = layout.TitleFontSize,
FontFamily = new FontFamily(layout.TitleFontFamily),
LineStackingStrategy = LineStackingStrategy.BlockLineHeight,
LineHeight = layout.TitleFontSize,
FontWeight = FontWeights.Normal,
Foreground = new SolidColorBrush(Color.FromRgb(18, 75, 14)),
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Width = layout.TitleRectangle.Width,
Margin = new Thickness(layout.TitleRectangle.Left,
layout.TitleRectangle.Top, 0, 0),
TextWrapping = TextWrapping.WrapWithOverflow
};
Grid.SetRow(titleTextBlock, 0);
Grid.SetColumn(titleTextBlock, 0);
grid.Children.Add(titleTextBlock);
增量拟合:
private static void FitToRectangle(TextBlock textBlock, RectangleF rectangle, bool fitToHeight)
{
while (true)
{
if (fitToHeight)
{
textBlock.Measure(new Size(double.PositiveInfinity, rectangle.Height));
}
else
{
textBlock.Measure(new Size(rectangle.Width, double.PositiveInfinity));
}
var size = textBlock.DesiredSize;
var rect = new Rect(size);
textBlock.Arrange(rect);
if ((fitToHeight && textBlock.ActualHeight > rectangle.Height) ||
(!fitToHeight && textBlock.ActualWidth > rectangle.Width))
{
textBlock.FontSize -= 5;
textBlock.LineHeight -= 5;
}
else
{
if (fitToHeight)
{
FitToRectangle(textBlock, rectangle, false);
}
break;
}
}
我需要很多并行线程来渲染。您只能在STA线程内创建Grid。如果您将UI线程的SyncContext传递给Task.StartNew - 这将起作用,但UI线程将在渲染期间冻结。这有助于:
public static Task StartSTATask(Action<object> func, object parameter)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(param =>
{
try
{
func(param);
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start(parameter);
return tcs.Task;
}
在文本框拟合之后,不要忘记测量并排列网格。
最后,渲染“屏幕外”的代码控制到文件:
var drawingVisual = new DrawingVisual();
using (var ctx = drawingVisual.RenderOpen())
{
var visualBrush = new VisualBrush
{
AutoLayoutContent = true,
Visual = grid
};
ctx.DrawRectangle(visualBrush, null,
new Rect(0, 0, layout.Size.Width, layout.Size.Height));
}
var bmp = new RenderTargetBitmap((int) layout.Size.Width, (int) layout.Size.Height,
96, 96, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (Stream stm = File.Create(filename))
{
encoder.Save(stm);
lock (this.sync)
{
this.rendered++;
UpdatePercentage();
}
}