简体   繁体   中英

Printing Canvas to XPS unwanted scaling and cut off

Update

Before writing a reply one might check out SamTheDev's answer and the comments of it. SamTheDev's answer fixed both problems, but I don't understand, why it fixes the first problem.


I am experiencing the following XPS problems and need your help in resolving the issues. A bounty is now running.

Issue 1: 10 cm long line in WPF is not 10 cm long in XPS

I am drawing this 10 cm long black line: 10厘米长的黑线WPF

Making a screenshot of it, pasting it to Word and printing it shows me, that it is really 10 cm long:

用字打印后10厘米长的线

When however printing the generated XPS file, the line is only about 9.7 cm long:

屏幕截图(如果生成了XPS文件)

打印生成的XPS文件

The code used for drawing the black line is as follows. I assumed a resolution of 96 dotchs per inch, I guess I need to do some scaling before creating the XPS file, but I don't know how to do that.

XAML:

<Window x:Class="MWEXps.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:MWEXps"
        mc:Ignorable="d"
        Title="Sample 1 - 10 cm long black line" Height="300" Width="700">
    <Canvas x:Name="canvas">
        <!-- will be filled by code behind -->        
    </Canvas>
</Window>

Code behind: (basically only the constructor and printCanvas method are relevant)

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // a 10 cm long line
        PathFigure lineFigure1 = new PathFigure();
        lineFigure1.StartPoint = cmPoint(3, 1);

        LineSegment lineSegment1 = new LineSegment();
        lineSegment1.Point = cmPoint(13, 1);
        lineFigure1.Segments.Add(lineSegment1);

        drawPathFigure(lineFigure1, canvas, Colors.Black);

        // save as XPS
        printCanvas();
    }


    public void printCanvas()
    {
        XpsDocument doc = new XpsDocument(@".\canvas-10cm.xps", System.IO.FileAccess.Write);
        XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);

        writer.Write(canvas);
        doc.Close();
    }

    // helper creating a point in centimeters
    public Point cmPoint(double x, double y)
    {
        return new Point(cmToWpf(x), cmToWpf(y));
    }


    // helper converting a length in device independent pixels to centimeters
    public double wpfToCm(double wpfValue)
    {
        double factor = (96 / 2.54);
        return wpfValue / factor;
    }

    // helper converting a length in centimeters to device independent pixels
    public double cmToWpf(double cmValue)
    {
        double factor = (96 / 2.54);
        return cmValue * factor;
    }


    // helper for drawing a figure
    public void drawPathFigure(PathFigure figure, Canvas canvas, Color color)
    {
        PathGeometry pathGeometry = new PathGeometry();
        pathGeometry.Figures.Add(figure);


        Path path = new Path();
        path.Stretch = Stretch.None;
        path.StrokeLineJoin = PenLineJoin.Miter;
        path.Stroke = new SolidColorBrush(color);
        path.StrokeThickness = 1;

        path.Data = pathGeometry;
        canvas.Children.Add(path);
    }
}

Issue 2: 100 cm long line cut off on created XPS

In this second example I am drawing a 100 cm long red line. Obviously it longer than the window size. That's not a problem, the problem is, that it's also cut off on the generated XPS file.

100厘米长的红线

The generated XPS file looks as follows: XPS上的100厘米长红线

The code used for drawing the red line is as follows. As one can see, the red line is simply cut off. I need the full 100 cm red line in the XPS - how can I do that?

XAML:

<Window x:Class="MWEXps.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:MWEXps"
        mc:Ignorable="d"
        Title="Sample 2 - 100 cm long red line" Height="300" Width="700">
    <Canvas x:Name="canvas">
        <!-- will be filled by code behind -->        
    </Canvas>
</Window>

Code behind: (basically only the constructor and printCanvas method are relevant)

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // a 100 cm long line
        PathFigure lineFigure1 = new PathFigure();
        lineFigure1.StartPoint = cmPoint(3, 1);

        LineSegment lineSegment1 = new LineSegment();
        lineSegment1.Point = cmPoint(103, 1);
        lineFigure1.Segments.Add(lineSegment1);

        drawPathFigure(lineFigure1, canvas, Colors.Red);

        // save as XPS
        printCanvas();
    }


    public void printCanvas()
    {
        XpsDocument doc = new XpsDocument(@".\canvas-100cm.xps", System.IO.FileAccess.Write);
        XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);

        writer.Write(canvas);
        doc.Close();
    }

    // helper creating a point in centimeters
    public Point cmPoint(double x, double y)
    {
        return new Point(cmToWpf(x), cmToWpf(y));
    }


    // helper converting a length in device independent pixels to centimeters
    public double wpfToCm(double wpfValue)
    {
        double factor = (96 / 2.54);
        return wpfValue / factor;
    }

    // helper converting a length in centimeters to device independent pixels
    public double cmToWpf(double cmValue)
    {
        double factor = (96 / 2.54);
        return cmValue * factor;
    }


    // helper for drawing a figure
    public void drawPathFigure(PathFigure figure, Canvas canvas, Color color)
    {
        PathGeometry pathGeometry = new PathGeometry();
        pathGeometry.Figures.Add(figure);


        Path path = new Path();
        path.Stretch = Stretch.None;
        path.StrokeLineJoin = PenLineJoin.Miter;
        path.Stroke = new SolidColorBrush(color);
        path.StrokeThickness = 1;

        path.Data = pathGeometry;
        canvas.Children.Add(path);
    }
}

Thanks for any support on this!

As a quick reminder, pixel size depends on two factors: the display resolution , and the physical size of the monitor, so there are no fixed relation between physical inches and pixels, there for WPF used the logical inches and by conversion: One logical inch is 96 pixels but this can be changed based on the user preferences.

So a "ruler" inch or centimeter doesn't really exist on wpf, only logical ones that deffer from one screen to another.

For example here the size of your black line on two of my screens :

在此处输入图片说明

在此处输入图片说明

  1. Regarding the red line, you could fix the canvas size based on its children using the Arrange methold, update your PrintCanvas method to this:

      public void printCanvas() { XpsDocument doc = new XpsDocument(@".\\canvas.xps", System.IO.FileAccess.Write); XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc); // The size of the canvas System.Windows.Size size = new System.Windows.Size(cmToWpf(102), cmToWpf(6)); // Arrange canvas canvas.Arrange(new Rect(size)); writer.Write(canvas); doc.Close(); } 

References

DPI and Device-Independent Pixels

What measurement units does Silverlight and WPF use?

Problem 1:

How do you measure the length of the black line? In case you place a ruler on your screen, you should know, that most screens don't have exactly 96 DPI, but something close to it. Print the document to measure the size.

Problem 2:

You can put your Canvas into a fixed page, and adjust the size of the fixed page to your canvas.

var doc = new XpsDocument( "out.xps", FileAccess.Write );
        var xpsdw = XpsDocument.CreateXpsDocumentWriter( doc );

        FixedDocument document = new FixedDocument();

        FixedPage page = new FixedPage();
        FixedPage.SetLeft( yourCanvas, 0 );
        FixedPage.SetTop( yourCanvas, 0 );

        page.Width = pageWidth;
        page.Height = pageHeight;

        page.Children.Add( yourCanvas );

        page.Measure( new Size( pageWidth, pageHeight ) );
        page.Arrange( new Rect( 0, 0, pageWidth, pageHeight ) );
        page.UpdateLayout();


        PageContent page_content = new PageContent();
        ( (IAddChild)page_content ).AddChild( page );

        document.Pages.Add( page_content );

        xpsdw.Write( document );
        doc.Close();

Simply adjust the variables "pageWidth" and "pageHeight" to your needs, eg yourCanvas.ActualWidth and yourCanvas.ActualHeight.

Be aware, that the XPSViewer doesn't render pages that are too large. The maximum value for each dimension is something around 70000 DiP on my machine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM