6

We have a .NET app which prints to both real printers and PDF, currently using PDFsharp, although that part can be changed if there's a better option. Most of the output is generated text or images, but there can be one or more pages that get appended to the end. That page(s) are provided by the end-user in PDF format.

When printing to paper, our users use pre-printed paper, but in the case of an exported PDF, we concatenate those pages to the end, since they're already in PDF format.

We want to be able to embed those PDFs directly into the print stream so they don't need pre-printed paper. However, there aren't really any good options for rendering a PDF to a GDI page (System.Drawing.Graphics).

Is there a vector format the PDF could be converted to by some external program, that could rendered to a GDI+ page without being degraded by conversion to a bitmap first?

Bryce Wagner
  • 2,640
  • 1
  • 26
  • 43
  • we do something similar but render the PDF first to a bitmap - the quality problem can be gotten around when rendering using a high-quality library plus a high dpi value (min. 305, best 1200) which costs some memory/performance... – Yahia Aug 18 '11 at 20:30
  • 1
    I noticed the other day that PDFCreator (http://sourceforge.net/projects/pdfcreator/), despite being primarily aimed at PDF creation, can actually be used to print PDF documents to a wide range of formats, including SVG and bitmapped images. Might be worth a look. – Dave Cluderay Aug 18 '11 at 20:43
  • These PDFs tend to be text with some line art. They would also get printed 1000+ times in a run. Converting a 30 KB PDF to a 5 MB bitmap would probably bring the whole print queue to its knees. – Bryce Wagner Aug 18 '11 at 20:43

4 Answers4

2

In an article titled "How To Convert PDF to EMF In .NET," I have shown how to do this using our PDFOne .NET product. EMFs are vector graphics and you can render them on the printer canvas.

A simpler alternative for you is PDF overlay explained in another article titled "PDF Overlay - Stitching PDF Pages Together in .NET." PDFOne allows x-y offsets in overlays that allows you stitch pages on the edges. In the article cited here, I have overlaid the pages one over another by setting the offsets to zero. You will have set it to page width and height.

DISCLAIMER: I work for Gnostice.

BZ1
  • 1,306
  • 2
  • 8
  • 10
  • We actually already use the Gnostice PDF toolkit in Delphi to stitch the two PDFs together, but we're moving the printing to .NET so there are more options available. I don't see GetPageMetafile() on the Delphi component, is that a new addition, or is specific to the .NET version? – Bryce Wagner Aug 19 '11 at 17:45
  • The other question, does that actually generate standalone .EMF files? We actually tried using a product in the past that was supposed to let you print to .EMF, but the output file only worked as long as the printing program was open, because it had callbacks to the original program that disappeared when you close that program. If the PDFOne.NET can guarantee working .EMF files, that sounds like the solution I'm looking for. – Bryce Wagner Aug 19 '11 at 17:49
  • PDFtoolkit has two methods - RenderToDC (to which you can pass the printer canvas) and RenderToStream (to which you supply an EMF stream). These methods generate EMF similar to PDFOne .NET's GetPageMetafile. In PDFOne .NET, the GetPageMetaFile is available only in the ProPlus edition. – BZ1 Aug 22 '11 at 06:12
  • In the first article given above, I have shown a screenshot of an EMF file that has been opened in Windows Picture and Fax Viewer. It is a standalone EMF file. PDFOne also has a printer component that can directly print PDF pages to the printer. You don't need to use EMF files. EMF files can be directly played on the printer canvas though. Both will work. – BZ1 Aug 22 '11 at 06:18
0

I finally figured out that there is an option that addresses my general requirement of embedding a vector format into a print job, but it doesn't work with GDI based printing.

The XPS file format created by Microsoft XPS Writer print driver can be printed from WPF, using the ReachFramework.dll included in .NET. By using WPF for printing instead of GDI, it's possible to embed an XPS document page into a larger print document.

The downside is, WPF printing works quite a bit different, so all the support code that directly uses stuff in the Sytem.Drawing namespace has to be re-written.

Here's the basic outline of how to embed the XPS document:

Open the document:

XpsDocument xpsDoc = new XpsDocument(filename, System.IO.FileAccess.Read);
var document = xpsDoc.GetFixedDocumentSequence().DocumentPaginator;

// pass the document into a custom DocumentPaginator that will decide
// what order to print the pages:
var mypaginator = new myDocumentPaginator(new DocumentPaginator[] { document });

// pass the paginator into PrintDialog.PrintDocument() to do the actual printing:
new PrintDialog().PrintDocument(mypaginator, "printjobname");

Then create a descendant of DocumentPaginator, that will do your actual printing. Override the abstract methods, in particular the GetPage should return DocumentPages in the correct order. Here's my proof of concept code that demonstrates how to append custom content to a list of Xps documents:

public override DocumentPage GetPage(int pageNumber)
{
    for (int i = 0; i < children.Count; i++)
    {
        if (pageNumber >= pageCounts[i])
            pageNumber -= pageCounts[i];
        else
            return FixFixedPage(children[i].GetPage(pageNumber));
    }
    if (pageNumber < PageCount)
    {
        DrawingVisual dv = new DrawingVisual();
        var dc = dv.Drawing.Append();
        dc = dv.RenderOpen();
        DoRender(pageNumber, dc); // some method to render stuff to the DrawingContext
        dc.Close();
        return new DocumentPage(dv);
    }
    return null;
}

When trying to print to another XPS document, it gives an exception "FixedPage cannot contain another FixedPage", and a post by H.Alipourian demonstrates how to fix it: http://social.msdn.microsoft.com/Forums/da/wpf/thread/841e804b-9130-4476-8709-0d2854c11582

private DocumentPage FixFixedPage(DocumentPage page)
{
    if (!(page.Visual is FixedPage))
        return page;

    // Create a new ContainerVisual as a new parent for page children
    var cv = new ContainerVisual();
    foreach (var child in ((FixedPage)page.Visual).Children)
    {
        // Make a shallow clone of the child using reflection
        var childClone = (UIElement)child.GetType().GetMethod(
            "MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic
            ).Invoke(child, null);

        // Setting the parent of the cloned child to the created ContainerVisual
        // by using Reflection.
        // WARNING: If we use Add and Remove methods on the FixedPage.Children,
        // for some reason it will throw an exception concerning event handlers
        // after the printing job has finished.
        var parentField = childClone.GetType().GetField(
            "_parent", BindingFlags.Instance | BindingFlags.NonPublic);
        if (parentField != null)
        {
            parentField.SetValue(childClone, null);
            cv.Children.Add(childClone);
        }
    }

    return new DocumentPage(cv, page.Size, page.BleedBox, page.ContentBox);
}

Sorry that it's not exactly compiling code, I just wanted to provide an overview of the pieces of code necessary to make it work to give other people a head start on all the disparate pieces that need to come together to make it work. Trying to create a more generalized solution would be much more complex than the scope of this answer.

Bryce Wagner
  • 2,640
  • 1
  • 26
  • 43
0

Ghostscript can output PostScript (which is a vector file) which can be directly sent to some types of printers. For example, if you're using an LPR capable printer, the PS file can be directly set to that printer using something like this project: http://www.codeproject.com/KB/printing/lpr.aspx

There are also some commercial options which can print a PDF (although I'm not sure if the internal mechanism is vector or bitmap based), for example http://www.tallcomponents.com/pdfcontrols2-features.aspx or http://www.tallcomponents.com/pdfrasterizer3.aspx

userx
  • 3,769
  • 1
  • 23
  • 33
  • I don't have any direct control over the printer they use, my guess is most are setup as PCL, not Postscript. The other problem is they want the PDF on the back of one of the pages (thus the pre-printed paper), so it can't be sent as a separate print job. – Bryce Wagner Aug 18 '11 at 21:24
-1

While not open source and not .NET native (Delphi based I believe, but offers a precompiled .NET library), Quick PDF can render a PDF to an EMF file which you could load into your Graphics object.

userx
  • 3,769
  • 1
  • 23
  • 33