Wanted: multi-page printing with preview

Feb 19, 2013 at 4:02 AM
It would be nice to have multi-page printing with preview as part of NShape framework!
Mar 5, 2013 at 9:43 AM
Any chances to have it? :)
Coordinator
Mar 5, 2013 at 1:21 PM
We have not discussed this yet, but I think your chances are quite small as printing is normally part of the host application.

Here is a sample how to implement printing:
private void PrintDiagram(Rectangle bounds, bool usePreview) {
    // Initialize print dialog
    try {
        using (System.Drawing.Printing.PrintDocument doc = new System.Drawing.Printing.PrintDocument()) {
            doc.PrintPage += DoPrint;
            try {
                if (usePreview) {
                    // Print Preview Dialog does not work as expected with *some* virtual printers, e.g. "PDFCreator"
                    // However, it *does* work with others, e.g. "Microsoft XPS Writer"
                    using (System.Windows.Forms.PrintPreviewDialog previewDlg = new System.Windows.Forms.PrintPreviewDialog()) {
                        previewDlg.Document = doc;
                        System.Windows.Forms.DialogResult res = previewDlg.ShowDialog();
                        if (res == System.Windows.Forms.DialogResult.OK)
                            doc.Print();
                    }
                } else {
                    using (System.Windows.Forms.PrintDialog settingsDlg = new System.Windows.Forms.PrintDialog()) {
                        if (Environment.OSVersion.Version.Major >= 6)   // ExDialog is available since Windows 6.0 (VISTA)
                            settingsDlg.UseEXDialog = true;
                        settingsDlg.Document = doc;
                        if (settingsDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                            doc.Print();
                    }
                }
            } finally {
                doc.PrintPage -= DoPrint;
            }
        }
    } catch (Exception exc) {
        MessageBox.Show(exc.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}


private void DoPrint(object sender, System.Drawing.Printing.PrintPageEventArgs e) {
    // Export Diagram
    DiagramTabItem diagramTab = GetSelectedDiagramTabItem();
    if (diagramTab != null) {
        IEnumerable<Shape> shapes = (diagramTab.Display.SelectedShapes.Count > 0) ? diagramTab.Display.SelectedShapes : null;
        using (System.Drawing.Image image = diagramTab.Display.Diagram.CreateImage(ImageFileFormat.EmfPlus, shapes, false)) {
            GraphicsUnit gfxUnit = GraphicsUnit.Pixel;
            Rectangle imgBounds = Rectangle.Round(image.GetBounds(ref gfxUnit));
            bool rotatePicture = false;
            if (e.PageSettings.Landscape)
                rotatePicture = (image.Width < image.Height);
            else rotatePicture = (image.Width > image.Height);
            DoPrintImage(e.Graphics, image, e.MarginBounds, imgBounds, rotatePicture);
        }
    }
}


/// <summary>
/// Draw an image into the specified bounds
/// </summary>
private void DoPrintImage(Graphics gfx, System.Drawing.Image image, Rectangle dstBounds, Rectangle srcBounds, bool rotate) {
    if (gfx == null) throw new ArgumentNullException("gfx");
    if (image == null) throw new ArgumentNullException("image");

    float dpi = (image.HorizontalResolution + image.VerticalResolution) / 2;
    float scale; int angle;
    if (rotate) {
        angle = -90;
        scale = Geometry.CalcScaleFactor(srcBounds.Width, srcBounds.Height, dstBounds.Height, dstBounds.Width);
    } else {
        angle = 0;
        scale = Geometry.CalcScaleFactor(srcBounds.Width, srcBounds.Height, dstBounds.Width, dstBounds.Height);
    }

    // Transform image bounds
    Rectangle sourceBounds = srcBounds;
    Rectangle destinationBounds = Rectangle.Empty;
    destinationBounds.X = (int)Math.Round(dstBounds.X + (dstBounds.Width - ((srcBounds.X + srcBounds.Width) * scale)) / 2f);
    destinationBounds.Y = (int)Math.Round(dstBounds.Y + (dstBounds.Height - ((srcBounds.Y + srcBounds.Height) * scale)) / 2f);
    destinationBounds.Width = (int)Math.Round(srcBounds.Width * scale);
    destinationBounds.Height = (int)Math.Round(srcBounds.Height * scale);

    System.Drawing.Point rotationCenter = System.Drawing.Point.Empty;
    rotationCenter.X = (int)Math.Round(dstBounds.X + dstBounds.Width / 2f);
    rotationCenter.Y = (int)Math.Round(dstBounds.X + dstBounds.Height / 2f);

    // Transform graphics context
    if (angle != 0) {
        gfx.TranslateTransform(rotationCenter.X, rotationCenter.Y);
        gfx.RotateTransform(angle);
        gfx.TranslateTransform(-rotationCenter.X, -rotationCenter.Y);
    }
    gfx.DrawImage(image, destinationBounds, sourceBounds.X, sourceBounds.Y, sourceBounds.Width, sourceBounds.Height, GraphicsUnit.Pixel);
    // Undo transformation
    if (angle != 0) {
        gfx.TranslateTransform(rotationCenter.X, rotationCenter.Y);
        gfx.RotateTransform(-angle);
        gfx.TranslateTransform(-rotationCenter.X, -rotationCenter.Y);
    }
}
Mar 5, 2013 at 2:46 PM
Thank You, but the point is to have multi-page printing: diagram can contain many shapes, and if we want to have (at least) readable text on these shapes - then scaling the diagram to fit a single page is not the right choice.
Coordinator
Mar 6, 2013 at 10:50 AM
I assume you want to devide the diagram into several tiles and each tile should be printed onto one page. Correct?

From the framework's point of view, this is no problem:
Just create an EMF image with the Diagram.CreateImage() method. Pass null for the shapes parameter on order to get an image of the whole diagram.
Afterwards, you can print the different parts of the diagram by drawing the appropriate part of the image on the printer's Graphics context with Graphics.DrawImage(..). You simply have to specify the appropriate source and destination rectangles.
I dont't know how to handle this with the PrintPreview dialog but I'm sure that our good old friend G***le has an answer for that...
Mar 7, 2013 at 10:54 AM
OK, I'll g..le :)