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.
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;
}
This question is already present but doesn't provide the answer using PDFsharp but iTextPDF.
Now coming back to question, I know a way to read and extract the String. But I'm having trouble REPLACING the text.
My Code:
var content = ContentReader.ReadContent(page);
var text = content.ExtractText();
text = text.Replace("Replace This", "With This");
XFont font = new XFont("Times New Roman", 11, XFontStyle.BoldItalic);
gfx.DrawString(text, font, XBrushes.Black, new XRect(0, 0, page.Width, page.Height), XStringFormats.Left);
// Save the document...
const string filename = "New Doc.pdf";
document.Save(filename);
}
public static IEnumerable<string> ExtractText(this CObject cObject)
{
if (cObject is COperator)
{
var cOperator = cObject as COperator;
if (cOperator.OpCode.Name== OpCodeName.Tj.ToString() ||
cOperator.OpCode.Name == OpCodeName.TJ.ToString())
{
foreach (var cOperand in cOperator.Operands)
foreach (var txt in ExtractText(cOperand))
yield return txt;
}
}
else if (cObject is CSequence)
{
var cSequence = cObject as CSequence;
foreach (var element in cSequence)
foreach (var txt in ExtractText(element))
yield return txt;
}
else if (cObject is CString)
{
var cString = cObject as CString;
yield return cString.Value;
}
}
This is a sample code and this one would ignore the graphics and images. And end up writing only text in the output file. Is there way I can replace the text without touching Graphics and Images in the content?
The sample seems to be a wrong approach: it returns text only, but ignores graphics, images, and even text positions and text attributes.
You can try to locate the text instructions (TJ, Tj) in the content and replace them with new instructions (also TJ or Tj) without touching anything else in the stream. Such a simple approach would lead to overlapping text or large gaps if the new text has a different lengths.
PDFsharp was not designed to parse the content streams. You have to write your own code to extract text, you have to write your own code to modify text (or use a third-party library that was built on PDFsharp).
To answer your question: yes, there is a way (as outlined above), but you will have to write a whole lot of code to achieve this (or find suitable code written by a third party).
XPS Printer allows us to create xps file no matter if from an image or txt or doc file.
I would like to do the same just programmatically in c#.
How do I send a file to xps printer and let the printer convert it to .xps file?
Any ideas?
I Google this but haven't found much so far.
It is possible also to use print queue to print to the XPS document writer but it will always show the file dialog.
See below other alternatives to convert and print to XPS file.
Programmatically converting files to XPS
This is not as simple as you wish many users out there have tried and there is many different ways to accomplish it all which arent the best.
One way (easiest way) to convert documents to XPS would be to use WORD 2007+ API to do it.
See below a snipped from this MSDN forum question
To programmatically convert docx to xps using Word 2007 see
Document.ExportAsFixedFormat in the Word Object Model Reference
(http://msdn2.microsoft.com/en-us/library/Bb256835.aspx). However,
since this is a server-side scenario you should take note of KB 257757
Considerations for server-side Automation of Office (see
http://support.microsoft.com/kb/257757).
Printing Images to XPS
You can easily print an Image to XPS file using the code below.
The code below is WPF example the image you pass into the write method needs to be wrapped in a canvas see this post for an example: http://denisvuyka.wordpress.com/2007/12/03/wpf-diagramming-saving-you-canvas-to-image-xps-document-or-raw-xaml/
XpsDocument xpsd = new XpsDocument("C:\\YourXPSFileName.xps", FileAccess.ReadWrite);
System.Windows.Xps.XpsDocumentWriter xw = XpsDocument.CreateXpsDocumentWriter(xpsd);
xw.Write(YourImageYouWishToWrite);
See an extended example below:
public void Export(Uri path, Canvas surface)
{
if (path == null) return;
// Save current canvas transorm
Transform transform = surface.LayoutTransform;
// Temporarily reset the layout transform before saving
surface.LayoutTransform = null;
// Get the size of the canvas
Size size = new Size(surface.Width, surface.Height);
// Measure and arrange elements
surface.Measure(size);
surface.Arrange(new Rect(size));
// Open new package
Package package = Package.Open(path.LocalPath, FileMode.Create);
// Create new xps document based on the package opened
XpsDocument doc = new XpsDocument(package);
// Create an instance of XpsDocumentWriter for the document
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
// Write the canvas (as Visual) to the document
writer.Write(surface);
// Close document
doc.Close();
// Close package
package.Close();
// Restore previously saved layout
surface.LayoutTransform = transform;
}
There are third party tools that allow you to print PDFs and other file formats to XPS.
PDF to XPS
Printing XPS files
It is possible to print XPS Documents programmatically You will need .Net 4 at least for this solution.
The example below uses the Print dialog from WPF and some of the classes from System.Windows.Xps and System.Printing.
The code below will print the Xps file to the default printer on the system however if you want to print to a different printer or even print to a print server you need to change the PrintQueue object on the print dialog.
Which is quite simple using the System.Printing namespace.
See the example below.
Please bear in mind that because it is using the WPF dialog it needs to run on a STATThread model.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Printing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
namespace ConsoleApplication
{
class Program
{
[STAThread]
static void Main(string[] args)
{
PrintDialog dlg = new PrintDialog();
XpsDocument xpsDoc = new XpsDocument(#"C:\DATA\personal\go\test.xps", System.IO.FileAccess.Read);
dlg.PrintDocument(xpsDoc.GetFixedDocumentSequence().DocumentPaginator, "Document title");
}
}
}
See below some Helpful links.
Xps Document documentation
Print Dialog from WPF documentation
System.Printing namespace documentation
Hope this helps and fit your needs
class Program
{
[System.MTAThreadAttribute()] // Added for clarity, but this line is redundant because MTA is the default.
static void Main(string[] args)
{
// Create the secondary thread and pass the printing method for
// the constructor's ThreadStart delegate parameter. The BatchXPSPrinter
// class is defined below.
Thread printingThread = new Thread(BatchXPSPrinter.PrintXPS);
// Set the thread that will use PrintQueue.AddJob to single threading.
printingThread.SetApartmentState(ApartmentState.STA);
// Start the printing thread. The method passed to the Thread
// constructor will execute.
printingThread.Start();
}//end Main
}//end Program class
public class BatchXPSPrinter
{
public static void PrintXPS()
{
// Create print server and print queue.
LocalPrintServer localPrintServer = new LocalPrintServer();
PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();
// Prompt user to identify the directory, and then create the directory object.
Console.Write("Enter the directory containing the XPS files: ");
String directoryPath = Console.ReadLine();
DirectoryInfo dir = new DirectoryInfo(directoryPath);
// If the user mistyped, end the thread and return to the Main thread.
if (!dir.Exists)
{
Console.WriteLine("There is no such directory.");
}
else
{
// If there are no XPS files in the directory, end the thread
// and return to the Main thread.
if (dir.GetFiles("*.xps").Length == 0)
{
Console.WriteLine("There are no XPS files in the directory.");
}
else
{
Console.WriteLine("\nJobs will now be added to the print queue.");
Console.WriteLine("If the queue is not paused and the printer is working, jobs will begin printing.");
// Batch process all XPS files in the directory.
foreach (FileInfo f in dir.GetFiles("*.xps"))
{
String nextFile = directoryPath + "\\" + f.Name;
Console.WriteLine("Adding {0} to queue.", nextFile);
try
{
// Print the Xps file while providing XPS validation and progress notifications.
PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob(f.Name, nextFile, false);
}
catch (PrintJobException e)
{
Console.WriteLine("\n\t{0} could not be added to the print queue.", f.Name);
if (e.InnerException.Message == "File contains corrupted data.")
{
Console.WriteLine("\tIt is not a valid XPS file. Use the isXPS Conformance Tool to debug it.");
}
Console.WriteLine("\tContinuing with next XPS file.\n");
}
}// end for each XPS file
}//end if there are no XPS files in the directory
}//end if the directory does not exist
Console.WriteLine("Press Enter to end program.");
Console.ReadLine();
}// end PrintXPS method
}// end BatchXPSPrinter class
In order to parse : Microsoft Word 97/2003 (.doc) & Microsoft Word 2007/2010 (.docx) using C# and WPF without Word installation, I need to know if someone can give me a serious library to use in order to achieve that.
Technically I iterate throught ZipEntry elements like that :
foreach (string file in _listPathFiles)
{
using (Ionic.Zip.ZipFile zip = ZipFile.Read(file))
{
try
{
zip.ToList().ForEach(entry =>
{
if (entry.FileName.EndsWith(".doc") ||
entry.FileName.EndsWith(".docx"))
{
// Extract file into disk
entry.FileName = System.IO.Path.GetFileName(entry.FileName);
entry.Extract(baseStoragePath);
// Get data from file with Parser
string filePath = baseStoragePath + entry.FileName;
// Remove extracted filess
if (File.Exists(filePath))
{
File.Delete(filePath);
Console.WriteLine("Delete : " + filePath);
}
}
});
}
catch (Exception e)
{
Console.WriteLine("Fail to unzip Exception : " + e.StackTrace);
}
}
}
I'm not sure that I can use ZipEntry directly to get the document, may be I will have to unzip it before parsing ?!
And my goal is to get data located after a "Heading 1" Microsoft Word style, so the library should be able to get this kind of properties.
Libraries ideas and code samples are welcome..
GroupDocs.Parser for .NET can be used in your case for extracting the text from Word documents without installing MS Word. The extraction can be performed line by line or at once.
// extracting all the text
WordsTextExtractor extractor = new WordsTextExtractor("sample.docx");
Console.Write(extractor.ExtractAll());
// OR
// Extract text line by line
string line = extractor.ExtractLine();
// If the line is null, then the end of the file is reached
while (line != null)
{
// Print a line to the console
Console.Write(line);
// Extract another line
line = extractor.ExtractLine();
}
Disclosure: I work as Developer Evangelist at GroupDocs.
Check out NPOI (A .NET Port of the Apache NOI API):
http://npoi.codeplex.com/
or
Download OpenXML SDK for reading Office Documents such as MS Word.
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.