My C# application prints some pages to a xps file, however i have discovered that if the default printer is a networked printer then the created xps file is invalid "The XPS viewer cannot open this document".
This confuses me since i'm not even writing to a networked printer.. but to a file.
If i don't have the default printer set to a networked printer (default printer is "send to OneNote" or "Microsoft XPS Document Writer"), then the bellow code correctly creates a XPS file with 2 pages when executed:
pageCounter = 0;
PrintDocument p = new PrintDocument();
p.PrintPage += delegate(object sender1, PrintPageEventArgs e1)
{
// 8.5 x 11 paper:
float x0 = 25;
float xEnd = 850 - x0;
float y0 = 25;
float yEnd = 1100 * 2 - y0; // bottom of 2ed page
Font TitleFont = new Font("Times New Roman", 30);
if (pageCounter == 0) // for the first page
{
e1.Graphics.DrawString("My Title", TitleFont, new SolidBrush(Color.Black), new RectangleF(300, 15, xEnd, yEnd));
e1.HasMorePages = true; // more pages
pageCounter++;// next page counter
}
else // the second page
{
e1.Graphics.DrawString("Page 2", TitleFont, new SolidBrush(Color.Black), new RectangleF(300, 15, xEnd, yEnd));
}
};
// now try to print
try
{
p.PrinterSettings.PrintFileName = fileName; // the file name set earlier
p.PrinterSettings.PrintToFile = true; // print to a file (i thought this would ignore the default printer)
p.Print();
}
catch (Exception ex)
{
// for the Bug I have described, this Exception doesn't happen.
// it creates an XPS file, but the file is invalid in the cases mentioned
MessageBox.Show("Error", "Printing Error", MessageBoxButton.OK);
}
so my question is... why does this happen, what am i doing wrong?
Well, there's no specific question here, but I'll tell you what I know. You are using the default printer's driver to generate the output document that is being saved to a file. Some drivers output xps content that is then consumed by the printer to put ink/toner on the page. Other drivers output postscript, PCL, PDF, or some other data format. So, depending on the default printer, you could be saving data in any one of these formats.
To ensure that you actually produce XPS content, you would need to specify the "Microsoft XPS Document Writer" as the printer to use in p.PrinterSettings.PrinterName. Of course, this could fail if that print queue has been renamed or removed. You could jump through some hoops with PrinterSettings.InstalledPrinters to try and determine which queue is the XPS Document Writer, but again, this will fail if the printer has been removed. A more robust solution would be to generate the XPS content directly with an XpsDocumentWriter, however that would require some substantial changes.
Related
I am fairly new to C#, but am using it to convert some older files in a WMF/PMF format to PDF. I have the script working, but some of the fonts in the original document are not coming through. For example, some of these old documents are payroll check runs and the checks use a special MICR font (where the account/routing numbers are displayed). This MICR line comes through the conversion as what appears to be a random base font.
I am simply using iTextSharp to convert the WMF to an image, then adding the image as a page in the PDF.
I have researched embedding fonts, but my problem is that I might not know what the original font name was. These files could be any number of things, and they could be quite old. Is there a way to include a font library that would expand iTextSharp's basic fonts so they are more likely to be recognized during the conversion?
This is the function performing the conversion. All of the WMF files (one for each check) are put in a directory and the function loads them all into a single PDF:
static bool BuildPDF(string pDecodeDir)
{
Document pdfDoc = new Document();
DirectoryInfo decodeDir = new DirectoryInfo(pDecodeDir);
int vCount = 0;
foreach (var file in decodeDir.GetFiles("*.WMF"))
{
try
{
iTextSharp.text.Image img1 = ImgWMF.GetInstance(file.FullName);
if (vCount == 0)
{
// in order to inherit the document size properly, we need to load the first image before creating the PDF object
pdfDoc = new Document(img1);
PdfWriter.GetInstance(pdfDoc, new FileStream(targetPath, FileMode.Create));
pdfDoc.Open();
}
else
{
pdfDoc.NewPage();
}
Console.WriteLine("Adding page {0}: {1}", vCount.ToString(), file.Name);
img1.SetAbsolutePosition(0, 0);
pdfDoc.Add(img1);
vCount++;
}
catch (System.Exception docerr)
{
Console.WriteLine("Doc Error: {0}", docerr.Message);
return false;
}
}
Console.WriteLine("{0} created!", targetPath);
pdfDoc.Close();
return true;
}
I have the following code which prints text file from C# to printer its based on the this article it prints plain text perfect but when I try to print .docx and .pdf file it prints but convert the content to some-kind of encoded characters. How can I fix this to print pdf and doc file?
private void btnPrint_Click(object sender, EventArgs e)
{
// Select the desired printer. ps.Duplex = Duplex.Simplex; // This works
pdocFile.PrinterSettings.PrinterName = cboPrinter.Text;
pdocFile.PrinterSettings.Duplex = Duplex.Horizontal;
// Print the checked files.
foreach (string filename in clbFiles.CheckedItems)
{
Console.WriteLine("Printing: " + filename);
// Get the file's name without the path.
FileInfo file_into = new FileInfo(filename);
string short_name = file_into.Name;
// Set the PrintDocument's name for use by the printer queue.
pdocFile.DocumentName = short_name;
// Read the file's contents.
try
{
FileContents = File.ReadAllText(filename).Trim();
}
catch (Exception ex)
{
MessageBox.Show("Error reading file " + filename +
".\n" + ex.Message);
return;
}
// Print.
pdocFile.Print();
}
MessageBox.Show("Spooled " + clbFiles.CheckedItems.Count +
" files for printing.");
}
//
private string FileContents;
// Print a page of the text file.
private void pdocTextFile_PrintPage(object sender, PrintPageEventArgs e)
{
// Make a font for printing.
using (Font font = new Font("Courier New", 10))
{
// Make a StringFormat to align text normally.
using (StringFormat string_format = new StringFormat())
{
// See how much of the remaining text will fit.
SizeF layout_area = new SizeF(e.MarginBounds.Width, e.MarginBounds.Height);
int chars_fitted, lines_filled;
e.Graphics.MeasureString(FileContents, font,
layout_area, string_format,
out chars_fitted, out lines_filled);
// Print as much as will fit.
e.Graphics.DrawString(
FileContents.Substring(0, chars_fitted),
font, Brushes.Black, e.MarginBounds,
string_format);
// Remove the printed text from the string.
FileContents = FileContents.Substring(chars_fitted).Trim();
}
}
// See if we are done.
e.HasMorePages = FileContents.Length > 0;
}
see Image link below
Your example above is taking a binary file format and trying to print it using a method that uses plain text, which will not work. You have a few options on how you could approach this.
Some printers allow you to submit various file types directly to them over a protocol like FTP. And example of this can be seen here. This method works great in enterprise environments which have business printers but is limited to the file types supported by each printer, and each printer's unique requirements.
For some formats, you can use third-party libraries like iText in your C# code to handle the actual printing. This option gives you a ton of control over the formatting, with the overhead of having to maintain additional code for every file type you wish to support.
You can also use the example code posted here to utilize already installed applications. In this example, it takes advantage of the Print verb made available by Adobe Acrobat, Word, etc. You'll need to make sure the applications have their defaults and surface the correct verb (which typically correlates with the context menu when right-clicking on a file name). This method is probably the most straight-forward option to handle files as-is.
I'm having a somewhat odd print quality problem in my C# application. I have an XPS file (it's basically just a 1 page image, that was originally a scanned black and white image) that I'm trying to print to an IBM InfoPrint Mainframe driver via a C# application. I've printed to numerous other print drivers and never had a problem, but this driver gives me terrible quality with the AFP file it creates. If I open the same file in the Microsoft XPS viewer application and print to the same driver, the quality looks fine.
Trying to work though the problem I've tried 3 or 4 different approaches to printing in the C# app. The original code did something like this (trimmed for brevity):
System.Windows.Xps.XpsDocumentWriter writer = PrintQueue.CreateXpsDocumentWriter(mPrintQueue);
mCollator = writer.CreateVisualsCollator();
mCollator.BeginBatchWrite();
ContainerVisual v = getContainerVisual(xpsFilePath);
//tried all sorts of different options on the print ticket, no effect
mCollator.Write(v,mDefaultTicket);
That code (which I've truncated) certainly could have had some weird issues in it, so I tried something much simpler:
LocalPrintServer localPrintServer = new LocalPrintServer();
PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();
PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob("title", xpsDocPath, false);
Same results.
I even tried using the WCF print dialog, same poor quality (http://msdn.microsoft.com/en-us/library/ms742418.aspx).
One area I haven't tried yet, is using the old-school underlying print API's, but I'm not sure why that would behave differently. One other option I have, is my original document is a PDF, and I have a good 3rd party library that can make me an EMF file instead. However, every time I try to stream that EMF file to my printer, I get garbled text.
Any ideas on why this quality is lost, how to fix, or how to stream an EMF file to a print driver, would be much appreciated!
UPDATE:
One other note. This nice sample app: http://wrb.home.xs4all.nl/Articles_2010/Article_XPSViewer_01.htm experiences the same quality loss. I've also now performed tests where I open the PDF directly and render the Bitmaps to a Print Document, same fuzziness of the resulting images. If I open the PDFs in Acrobat and print they look fine.
So to close this issue, it seems that the IBM Infoprint driver (at least the way it's being used here) has quite different quality depending on how you print in C#.
In this question I was using:
System.Windows.Documents.Serialization.Write(Visual, PrintTicket);
I completely changed my approach, removing XPS entirely, and obtained an emf (windows metafile) rendition of my document, then sent that emf file to the Windows printer using the windows print event handler:
using (PrintDocument pd = new PrintDocument())
{
pd.DocumentName = this.mJobName;
pd.PrinterSettings.PrinterName = this.mPrinterName;
pd.PrintController = new StandardPrintController();
pd.PrintPage += new PrintPageEventHandler(DoPrintPage);
pd.Print();
}
(I've obviously omitted a lot of code here, but you can find examples of how to use this approach relatively easily)
In my testing, most print drivers were equally happy with either printing approach, but the IBM Infoprint driver was EXTREMELY sensitive to the quality. One possible explanation is that the Infoprint printer was required to be configured with a weird fixed DPI and it may be doing a relatively poor job converting.
EDIT: More detailed sample code was requested, so here ya go. Note that getting an EMF file is a pre-req for this approach. In this case I'm using ABC PDF, which lets you generate an EMF file from your PDF with a relatively simple call.
class AbcPrintEmf
{
private Doc mDoc;
private string mJobName;
private string mPrinterName;
private string mTempFilePath;
private bool mRenderTextAsPolygon;
public AbcPdfPrinterApproach(Doc printMe, string jobName, string printerName, bool debug, string tempFilePath, bool renderTextAsPolygon)
{
mDoc = printMe;
mDoc.PageNumber = 1;
mJobName = jobName;
mPrinterName = printerName;
mRenderTextAsPolygon = renderTextAsPolygon;
if (debug)
mTempFilePath = tempFilePath;
}
public void print()
{
using (PrintDocument pd = new PrintDocument())
{
pd.DocumentName = this.mJobName;
pd.PrinterSettings.PrinterName = this.mPrinterName;
pd.PrintController = new StandardPrintController();
pd.PrintPage += new PrintPageEventHandler(DoPrintPage);
pd.Print();
}
}
private void DoPrintPage(object sender, PrintPageEventArgs e)
{
using (Graphics g = e.Graphics)
{
if (mDoc.PageCount == 0) return;
if (mDoc.Page == 0) return;
XRect cropBox = mDoc.CropBox;
double srcWidth = (cropBox.Width / 72) * 100;
double srcHeight = (cropBox.Height / 72) * 100;
double pageWidth = e.PageBounds.Width;
double pageHeight = e.PageBounds.Height;
double marginX = e.PageSettings.HardMarginX;
double marginY = e.PageSettings.HardMarginY;
double dstWidth = pageWidth - (marginX * 2);
double dstHeight = pageHeight - (marginY * 2);
// if source bigger than destination then scale
if ((srcWidth > dstWidth) || (srcHeight > dstHeight))
{
double sx = dstWidth / srcWidth;
double sy = dstHeight / srcHeight;
double s = Math.Min(sx, sy);
srcWidth *= s;
srcHeight *= s;
}
// now center
double x = (pageWidth - srcWidth) / 2;
double y = (pageHeight - srcHeight) / 2;
// save state
RectangleF theRect = new RectangleF((float)x, (float)y, (float)srcWidth, (float)srcHeight);
int theRez = e.PageSettings.PrinterResolution.X;
// draw content
mDoc.Rect.SetRect(cropBox);
mDoc.Rendering.DotsPerInch = theRez;
mDoc.Rendering.ColorSpace = "RGB";
mDoc.Rendering.BitsPerChannel = 8;
if (mRenderTextAsPolygon)
{
//i.e. render text as polygon (non default)
mDoc.SetInfo(0, "RenderTextAsText", "0");
}
byte[] theData = mDoc.Rendering.GetData(".emf");
if (mTempFilePath != null)
{
File.WriteAllBytes(mTempFilePath + #"\" + mDoc.PageNumber + ".emf", theData);
}
using (MemoryStream theStream = new MemoryStream(theData))
{
using (Metafile theEMF = new Metafile(theStream))
{
g.DrawImage(theEMF, theRect);
}
}
e.HasMorePages = mDoc.PageNumber < mDoc.PageCount;
if (!e.HasMorePages) return;
//increment to next page, corrupted PDF's have occasionally failed to increment
//which would otherwise put us in a spooling infinite loop, which is bad, so this check avoids it
int oldPageNumber = mDoc.PageNumber;
++mDoc.PageNumber;
int newPageNumber = mDoc.PageNumber;
if ((oldPageNumber + 1) != newPageNumber)
{
throw new Exception("PDF cannot be printed as it is corrupt, pageNumbers will not increment properly.");
}
}
}
}
Question 298829 describes how linearizing your PDFs lets them stream page-by-page into the user's browser, so the user doesn't have to wait for the whole document to download before starting to view it. We have been using such PDFs successfully, but now have a new wrinkle: We want to keep the page-by-page streaming, but we also want to insert a fresh cover page at the front of the PDF documents each time we serve them up. (The cover-page will have time-sensitive information, such as the date, so it's not practical to include the cover page in the PDFs on disk.)
To help with this, are there any PDF libraries that can quickly append a cover page to a pre-linearized PDF and yield a streamable, linearized PDF as output? What's of the greatest concern is not the total time to merge the PDFs, but how soon we can start streaming part of the merged document to the user.
We were trying to do this with itextsharp, but it turns out that library can't output linearized PDFs. (See http://itext.ugent.be/library/question.php?id=21) Nonetheless, the following ASP.NET/itextsharp scratch code demonstrates the sort of API we're thinking of. In particular, if itextsharp always output linearized PDFs, something like this might already be the solution:
public class StreamPdf : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/pdf";
RandomAccessFileOrArray ramFile = new RandomAccessFileOrArray(#"C:\bigpdf.pdf");
PdfReader reader1 = new PdfReader(ramFile, null);
Document doc = new Document();
// We'll stream the PDF to the ASP.NET output
// stream, i.e. to the browser:
PdfWriter writer = PdfWriter.GetInstance(doc, context.Response.OutputStream);
writer.Open();
doc.Open();
PdfContentByte cb = writer.DirectContent;
// output cover page:
BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
Font font = new Font(bf, 11, Font.NORMAL);
ColumnText ct = new ColumnText(cb);
ct.SetSimpleColumn(60, 300, 600, 300 + 28 * 15, 15, Element.ALIGN_CENTER);
ct.AddText(new Phrase(15, "This is a cover page information\n", font));
ct.AddText(new Phrase(15, "Date: " + DateTime.Now.ToShortDateString() + "\n", font));
ct.Go();
// output src document:
int i = 0;
while (i < reader1.NumberOfPages)
{
i++;
// add next page from source PDF:
doc.NewPage();
PdfImportedPage page = writer.GetImportedPage(reader1, i);
cb.AddTemplate(page, 0, 0);
// use something like this to flush the current page to the
// browser:
writer.Flush();
s.Flush();
context.Response.Flush();
}
doc.Close();
writer.Close();
s.Close();
}
}
}
Ideally we're looking for a .NET library, but it would be worth hearing about any other options as well.
You could try GhostScript, I think its possible to stitch PDF's together but dont know about linearizing when it comes to PDF. I have a C# GhostScript Wrapper that can be used with the GhostScript dll directly, I am sure this can be modified to Merge PDFs. contact details at: redmanscave.blogspot.com
Anybody know how to get all of these in a C# app that leads the user to a print dialogue screen?
What I mean is this:
In the gui the user selects a document, right clicks on the doc, chooses Print. Instead of immediately printing out the doc, the print dialogue comes up.
Anybody know the syntax for all of this? I tried using all of the interops for the MS Office apps and trying the printdialogue method...but no such luck.
Can anybody provide me with some code? I've seriously been at this for HOURS and can't come up with a thing.
Maybe see this previous Q&A:
send-document-to-printer-with-c#
Or, for a WPF app that prints a FlowDocument :
private void DoThePrint(System.Windows.Documents.FlowDocument document)
{
// Clone the source document's content into a new FlowDocument.
// This is because the pagination for the printer needs to be
// done differently than the pagination for the displayed page.
// We print the copy, rather that the original FlowDocument.
System.IO.MemoryStream s = new System.IO.MemoryStream();
TextRange source = new TextRange(document.ContentStart, document.ContentEnd);
source.Save(s, DataFormats.Xaml);
FlowDocument copy = new FlowDocument();
TextRange dest = new TextRange(copy.ContentStart, copy.ContentEnd);
dest.Load(s, DataFormats.Xaml);
// Create a XpsDocumentWriter object, implicitly opening a Windows common print dialog,
// and allowing the user to select a printer.
// get information about the dimensions of the seleted printer+media.
System.Printing.PrintDocumentImageableArea ia = null;
System.Windows.Xps.XpsDocumentWriter docWriter = System.Printing.PrintQueue.CreateXpsDocumentWriter(ref ia);
if (docWriter != null && ia != null)
{
DocumentPaginator paginator = ((IDocumentPaginatorSource)copy).DocumentPaginator;
// Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device.
paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight);
Thickness t = new Thickness(72); // copy.PagePadding;
copy.PagePadding = new Thickness(
Math.Max(ia.OriginWidth, t.Left),
Math.Max(ia.OriginHeight, t.Top),
Math.Max(ia.MediaSizeWidth - (ia.OriginWidth + ia.ExtentWidth), t.Right),
Math.Max(ia.MediaSizeHeight - (ia.OriginHeight + ia.ExtentHeight), t.Bottom));
copy.ColumnWidth = double.PositiveInfinity;
//copy.PageWidth = 528; // allow the page to be the natural with of the output device
// Send content to the printer.
docWriter.Write(paginator);
}
}