在WPF中创建旋转文本

时间:2012-09-27 19:17:28

标签: c# wpf text

我正在尝试创建一些旋转的文本并将该图像保存为PNG文件。生成的PNG不应大于所需的(或最小填充)。只要没有旋转,我就可以使用它,但是一旦我旋转文本,它就会在文件中被剪掉。我确信它与调整RotateTransform的CenterX和CenterY或创建TranslateTransform有关,但是我找不到任何关于如何正确执行它并且我的试错法测试已经变成试验 - 和 - 挫折

我的示例代码如下。我正在寻找一种能够以任意角度工作而不仅仅是-45度的解决方案。

最后,如果有人知道如何满足这些要求,但是说使用“旧式”图形对象而不是WPF工具,我也对这个解决方案持开放态度。

private static void CreateImageFile()
{
    FormattedText ft;
    Geometry textBox;
    string fontName;
    Typeface face;
    DrawingVisual viz;
    RotateTransform rt;
    TranslateTransform tt;
    Rect rect;
    RenderTargetBitmap bmp;
    PngBitmapEncoder encoder;

    ft = CreateText("Lorem ipsum dolor sit amet, consectetur adipisicing" + Environment.NewLine + "elit, sed do eiusmod tempor", "Verdana", 12, false, false);
    textBox = ft.BuildHighlightGeometry(new Point());

    fontName = "Arial";
    face = new Typeface(fontName);

    // now create the visual we'll draw them to
    viz = new DrawingVisual();
    rt = new RotateTransform() { Angle = -45 };
    rect = rt.TransformBounds(ft.BuildHighlightGeometry(new Point(0, 0)).Bounds);

    using (DrawingContext dc = viz.RenderOpen())
    {
        dc.PushTransform(rt);
        dc.DrawText(ft, new Point(0, 0));
        dc.Pop();
    }

    bmp = new RenderTargetBitmap((int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Pbgra32);
    bmp.Render(viz);

    encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bmp));
    using (FileStream file = new FileStream("TextImage.png", FileMode.Create))
        encoder.Save(file);
}

private static FormattedText CreateText(string text, string typeface, double fontSize, bool bold, bool italic)
{
    FontStyle fontStyle = FontStyles.Normal;
    FontWeight fontWeight = FontWeights.Medium;

    if (bold == true) fontWeight = FontWeights.Bold;
    if (italic == true) fontStyle = FontStyles.Italic;

    // Create the formatted text based on the properties set.
    FormattedText formattedText = new FormattedText(
        text,
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        new Typeface(new FontFamily(typeface),
            fontStyle,
            fontWeight,
            FontStretches.Normal),
        fontSize,
        Brushes.Black, // This brush does not matter since we use the geometry of the text. 
        null,
        TextFormattingMode.Display
        );

    return formattedText;
}

更新

根据以下一些建议,我决定在GUI中尝试不同的方法和实验。我创建了一个这样的窗口:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="160" Width="160" Loaded="Window_Loaded">
    <Grid>
        <Canvas Name="WorkCanvas" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock RenderTransformOrigin="0.5,0.5">
                <TextBlock.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform Angle="-45"/>
                        <TranslateTransform/>
                    </TransformGroup>
                </TextBlock.RenderTransform>This is a test</TextBlock>
        </Canvas>
    </Grid>
</Window>

如您所见,它同时使用RotateTransform和建议的RenderTransformOrigin,结果如下图所示。而且,正如您所看到的,文本会经过Canvas的中间位置。 似乎是我的整个问题。如何旋转文本使其正确居中。

Example


更新2

这次我决定尝试Grid而不是Canvas,现在我可以将文本正确地放在网格中心,但因为我无法使用FormattedText对象,我似乎无法衡量实际的边界框。 TextBlockGrid的所有测量值都会返回,就好像它根本没有旋转一样(查看ActualWidthActualHeightDesiredSize)。如果我无法获得旋转的边界框大小,我无法保存PNG而不会被剪切。

哦,我尝试在未旋转的网格中旋转文本并旋转网格本身,在尝试确定尺寸时都会得到相同的结果。

3 个答案:

答案 0 :(得分:2)

您可以尝试将文本包装在具有rendertransformOrigin的元素中。让您更改该元素。尝试使用画布或网格。

答案 1 :(得分:1)

我认为您缺少的是您需要将RenderTransformOrigin设置为0.5,0.5,以便您的旋转变换位于图像的中心,而不是左上角。

<强>更新

回复上述更新。问题是使用画布。如果您完全删除了转换,您将看到TextBlock不以中心开头。它实际上围绕着它的中心旋转,只是中心不是画布的中心。试试这个:

<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock TextAlignment="Center" RenderTransformOrigin="0.5,0.5">
            <TextBlock.RenderTransform>
                    <RotateTransform Angle="-45"/>
            </TextBlock.RenderTransform>
            This is a test
        </TextBlock>
</Grid>

答案 2 :(得分:0)

经过多次探索并在Matt和kbo4sho88的帮助下,我终于找到了正确的方法。除了其他海报的帮助之外,我终于发现我需要调用TransformToVisual和TransformBounds来获取正确文件大小所需的边界框。但是,在此之前,我必须调用测量和排列,因为这些对象不会显示在屏幕上。

呼!

private static void CreateImageFile()
{
    Grid workGrid;
    TextBlock workTextBlock;
    RenderTargetBitmap bitmap;
    PngBitmapEncoder encoder;
    Rect textBlockBounds;
    GeneralTransform transform;

    workGrid = new Grid()
    {
        VerticalAlignment = VerticalAlignment.Center,
        HorizontalAlignment = HorizontalAlignment.Center
    };

    workTextBlock = new TextBlock()
    {
        Text = "Lorem ipsum dolor sit amet, consectetur adipisicing" + Environment.NewLine + "elit, sed do eiusmod tempor",
        FontFamily = new FontFamily("Verdana"),
        FontSize = 36,
        TextAlignment = TextAlignment.Center,
        RenderTransformOrigin = new Point(0.5, 0.5),
        LayoutTransform = new RotateTransform(-45)
    };

    workGrid.Children.Add(workTextBlock);

    /*
     * We now must measure and arrange the controls we just created to fill in the details (like
     * ActualWidth and ActualHeight before we call TransformToVisual() and TransformBounds()
     */
    workGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
    workGrid.Arrange(new Rect(0, 0, workGrid.DesiredSize.Width, workGrid.DesiredSize.Height));

    transform = workTextBlock.TransformToVisual(workGrid);
    textBlockBounds = transform.TransformBounds(new Rect(0, 0, workTextBlock.ActualWidth, workTextBlock.ActualHeight));

    /*
     * Now, create the bitmap that will be used to save the image. We will make the image the 
     * height and width we need at 96DPI and 32-bit RGBA (so the background will be transparent).
     */
    bitmap = new RenderTargetBitmap((int)textBlockBounds.Width, (int)textBlockBounds.Height, 96, 96, PixelFormats.Pbgra32);
    bitmap.Render(workGrid);

    encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmap));
    using (FileStream file = new FileStream("TextImage.png", FileMode.Create))
        encoder.Save(file);
}

当然这只是一个样本(但正在工作)的方法,最后一个方法将被参数化。

最后一部分是在WPF: Getting new coordinates after a Rotation

找到的