WPF的第三方允许在矩形中绘制包装文本?

时间:2017-04-18 18:46:22

标签: .net wpf textwrapping

我的图像有一些区域(矩形)用于文本。文本可以是任意长度,一个单词或长字符串(必须包装)。我需要:

  1. 计算字体大小(按字符串,字体和矩形)
  2. 在此矩形中绘制文字(包裹!)
  3. 主要要求:获取fontSize的最大值,文本应填充所有矩形。

    我做了什么。 我在NuGet:ImageProcessor中找到了不错的第三方。但是,ImageFactory.Watermark只获取起始点,而不是矩形。

    好的,我已经实施了自己的解决方案:

    1. 尝试以非常大的字体大小绘制文本(使用MeasureText,而不是真正的渲染)。
    2. 例如,我的矩形的高度为100,但MeasureString返回200.很棒,将fontSize从50改为25.
    3. 使用新的fontSize,每个字符变小,不仅高度和宽度!这就是为什么我将 newFontSize = oldFontSize *(measuredHeight / requiredHeight)替换为 newFontSize = oldFontSize * Math.Sqrt(measuredHeight / requiredHeight)
    4. 看起来更好。但我仍有问题。
    5. 问题1:我在WPF中使用来自GDI +的Graphics.MeasureString。这不是线程安全的,我必须使用锁。

      问题2:MeasureString返回错误的高度,好像文本有很大的余量。例如,我有3个相互靠近的矩形:

      • RECTANGLE1
      • RECTANGLE2
      • RECTANGLE3

      渲染后,我看到它们之间有很大的空间。

      我很高兴能得到我需要的第三方!如果不是,修复我的代码也会很棒。谢谢!

      代码:

          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作为示例。

2 个答案:

答案 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;
            }
        }
  1. 适合宽度也很重要!如果文本有非常的VerylongWordWithoutSeparators,“Wrap”只是在中间剪切并包裹! “WrapWithOverflow” - 长字的宽度将大于限制。这就是我们在高度拟合后需要最终宽度配合的原因。
  2. 我需要很多并行线程来渲染。您只能在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;
    }
    
  3. 在文本框拟合之后,不要忘记测量并排列网格。

  4. 最后,渲染“屏幕外”的代码控制到文件:

            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();
                }
            }