cesar bueno
cesar bueno

Reputation: 1

Rich Text Box To PNG - Wrong Line Spacings

I'm developing a WPF RICH TEXT BOX Desktop Application.

It must work like a text editor, basically, where the user is able to change font family, size, color and line spacing and add a background image. No problem so far. The problem lies here: the rtb content must be outputted to PNG. Not a problem either IF I don't mess with line heights.

This is an example:

Example

I've been looking for a solution for days now and have tried different approaches suggested in other posts but haven't found an answer.

This is a part of the xaml:

<ToolBar Name="mainToolBar" Height="30" DockPanel.Dock="Top">
    <Slider x:Name="slider01" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="60" Cursor="Arrow" ValueChanged="comboLineHeight_ValueChanged" SmallChange="10" TickPlacement="TopLeft" Foreground="Black" Background="#FFCACACA" IsSnapToTickEnabled="True" Maximum="400" Minimum="1" />                    
</ToolBar>

<RichTextBox Name="mainRTB" FontSize="20" AcceptsTab="True" FontFamily="Arial" >
    <RichTextBox.Resources>
        <Style TargetType="{x:Type Paragraph}">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Padding" Value="0"/>
        </Style>
    </RichTextBox.Resources>
    <FlowDocument>

    </FlowDocument>
</RichTextBox>

This is the method that creates the bitmap (Saving RichTextBox FlowDocument to image):

public BitmapSource FlowDocumentToBitmap(FlowDocument document, Size size)
{
    //Make a copy
    document = CloneDocument(document);

    //Set margins to zero otherwise they will be "Auto"
    SetZeroMargin(document);

    document.ColumnWidth = size.Width;

    document.PagePadding = new Thickness(0, 0, 0, 0);

    var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
    paginator.PageSize = size;

    var visual = new DrawingVisual();
    using (var drawingContext = visual.RenderOpen())
    {
        if (myBackground != null)
        {
            imgBrush.ImageSource = new BitmapImage(new Uri(myBackground));

            drawingContext.DrawImage(imgBrush.ImageSource, new Rect(size));
        }
    }
    visual.Children.Add(paginator.GetPage(0).Visual);

    var bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    bitmap.Render(visual);
    return bitmap;
}

In order to render the richtextbox to png I must use a copy of it otherwise it wrecks the rtb content (can't edit it anymore or it vanishes).

public FlowDocument CloneDocument(FlowDocument document)
{
    var copy = new FlowDocument();
    var sourceRange = new TextRange(document.ContentStart, document.ContentEnd);
    var targetRange = new TextRange(copy.ContentStart, copy.ContentEnd);

    using (var stream = new MemoryStream())
    {
        sourceRange.Save(stream, DataFormats.XamlPackage);
        targetRange.Load(stream, DataFormats.XamlPackage);
    }

    return copy;
}

Problem #1: the paragraph margins are not copied - they are set to Auto.
Solution: As soon as the document is cloned I set the margins to zero thickness.

void SetZeroMargin(FlowDocument flowDoc)
{
    foreach (Block block in flowDoc.Blocks)
    {
        if (typeof(Paragraph).IsAssignableFrom(block.GetType()))
        {
            Paragraph p = (Paragraph)block;
            p.Margin = new Thickness(0, 0, 0, 0);
        }
    }
}

This would be fine if the ability to adjust line heights were not a requirement.

I added a slider to the user interface to adjust the line height. It collects the selected lines (paragraphs) and applies the lineHeight:

private void comboLineHeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs\<double\> e)
{
Slider slider = (Slider)sender;

    foreach (Paragraph p in GetSelectedLines())
    {
        p.LineHeight = Convert.ToDouble(slider.Value);
    }

}

Therefore, it is possible to have different line heights in the document.

Example with lineHeights adjusted (rtb print screen):

RTB print screen with lineHeight adjusted

Ouputted PNG:

Ouputted PNG with wrong spacings

Question: how can I render the rtb content with the correct line heights?

Thanks a lot for any help.

Upvotes: 0

Views: 70

Answers (1)

cesar bueno
cesar bueno

Reputation: 1

I've managed to render the rtb content with the correct line heights. Since my document has only one page (always), I just took a snapshot of it instead of adding the paginator to the DrawingVisual method. I passed the rtb as a parameter to this method:

    private RenderTargetBitmap SnapshotGet(FrameworkElement element)
{
    double width = element.ActualWidth;
    double height = element.ActualHeight;
    RenderTargetBitmap bmpCopied = new RenderTargetBitmap((int)Math.Round(width), (int)Math.Round(height), 96, 96, PixelFormats.Default);
    DrawingVisual dv = new DrawingVisual();
    using (DrawingContext dc = dv.RenderOpen())
    {
        VisualBrush vb = new VisualBrush(element);
        dc.DrawRectangle(vb, null, new Rect(new System.Windows.Point(), new System.Windows.Size(width, height)));
    }

    bmpCopied.Render(dv);

    return bmpCopied;
}

Then I save the snapshot:

    void SnapshotSave()
    {
        BitmapSource bmSource = SnapshotGet(mainRTB);


        //var encoder = new JpegBitmapEncoder();
        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bmSource));


        SaveFileDialog sfd = new SaveFileDialog();

        sfd.Filter = "png|*.png"; 
        sfd.FilterIndex = 1;
        sfd.InitialDirectory = DTO.folderRender;
        sfd.AddExtension = true;

        if (sfd.ShowDialog() == true)
        {
            using (var stream = new FileStream(sfd.FileName, FileMode.Create))
            {
                encoder.Save(stream);
            }
        }
    }

Upvotes: 0

Related Questions