This is a WPF desktop app related to ID Card printing. One of the new features we're trying to add is magstripe encoding. After having spent several days, I'm still not sure where to start. New questions keep popping up the more I google. I'll summarize them here. I'll be glad to hear from experts (even if someone can answer one/some of the questions):
Do magstripe printers work as normal printers too (means can they print text and graphics too, or is that we print the cards on other, regular printers in the first pass and then insert them into magstripe printer for encoding magnetic data onto them, in the 2nd pass)?
If answer to Q1 is yes, how do I send magstripe data to the printer during regular printing job (done through WPF, using PrintDialog, FixedDocument etc).
I downloaded and examined Zebra printers SDK. It looks like these printers DO support text/graphics printing in addition to magstripe encoding, but their SDK requires me to call their native printing functions, which doesn't fit in WPF's standard printing model. How to overcome this?
In another place I read that magstripe printers require simple ASCII text in specific format to get them encoded onto the card, and that I can do this even from Notepad. If this is true, the answer to Q1 might be negative. But then again, how does this method work in conjunction with regular WPF printing?
Edit
I also learnt that there are magstripe fonts that when placed in a document, end up being encoded to the magnetic stripe instead of regular printing. If this is true, it would very nicely fit in WPF printing model. But googling hasn't returned too many promising results for magstripe fonts. Maybe this is a brand-specific feature.
I am currently working on a similar WPF project that requires magnetic encoding as well as image printing on ID cards. I have found that magnetic encoding is very simple as long as the drivers for the printer with magnetic coding are installed on the host. One vital piece to watch out for is the delimiter being used by the driver. This could be NULL, ZERO, or Space. This comes into play when encoding a specific track (i.e. Track 2 but not Track 1 as we are). I use the NULL setting which allows only the Track 2 data to be sent in the job. This setting is found in the Printer Preferences for the Fargo printers (Control Panel -> Hardware and Sound -> Devices and Printers -> Right-Click Printer -> Printer Preferences). Here is an example of these Preferences (note the ASCII Offset field):
I do not believe that you MUST use the SDK for the printer you are using. I am using a Fargo printer but wrote my own Print functionality using PrintDocument and PrintPage for both magnetic encoding and images.
An example and quick test is to send Track 2 data to the printer using Notepad++ (or similar). Copy and paste this into the first line of the editor and print (using the card printer).
~2;000099990000?
The driver should pick up the fact that it is Track data and handle it accordingly without any more input from you. You may need to play with the printer preferences as stated.
The ~2; denotes Track 2 followed by a 12-character data string followed by the end sentinel (?). There is plenty of documentation pertaining to Track data and layouts online. This is assuming a NULL delimiter value (between Track 1 and Track 2).
Printing on both sides of the card can be cumbersome, but that does not appear to be within the scope of this question. I recommend using Windows native PrintDocument and PrintPage methods within your WPF application; the SDK you have downloaded is likely using these methods in the background, anyhow.
An example of PrintDocument/PrintPage:
private int PageCount { get; set; }
public void Print()
{
PageCount = 0;
PrintDocument pd = new PrintDocument
{
// Define your settings
PrinterSettings = {
Duplex = Duplex.Horizontal,
PrinterName = ConfigurationManager.AppSettings["PrinterName"]
}
};
Margins margins = new Margins(0, 0, 0, 0);
pd.OriginAtMargins = true;
pd.DefaultPageSettings.Margins = margins;
pd.PrintPage += new PrintPageEventHandler(this.PrintPage);
PrintPreviewDialog ppd = new PrintPreviewDialog();
ppd.Document = pd;
// Uncomment to show a Print Dialog box
//if (ppd.ShowDialog() == DialogResult.OK)
pd.Print();
pd.Dispose();
}
private void PrintPage(object o, PrintPageEventArgs e)
{
PrintDocument p = (PrintDocument)o;
p.DefaultPageSettings.Landscape = true;
p.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
p.DefaultPageSettings.PaperSize = new PaperSize("YourPaperSizeName", 340, 215);
p.OriginAtMargins = true;
o = p;
e.PageSettings.PrinterSettings.DefaultPageSettings.Landscape = true;
e.PageSettings.Landscape = true;
// Do Print First Side (MAG ENCODE)
if (PageCount == 0)
{
// Do Side 1 Stuff
// If Two-Sided Printing: true
e.HasMorePages = true;
//If no more, set above to false and PageCount = 0, else...
PageCount++;
}
else
{
// Do Print on Other Side
// STUFF
// STUFF
// Since only two sides/card printing: false
e.HasMorePages = false;
PageCount = 0;
}
}
Again, magnetic encoding should not be brand-specific and you should not have to rely solely on their SDK to perform print jobs.
I hope this helps!
Related
I have to write a functionality that takes a PDF-document and sends it to a printer with some PJL commands. So far so good, I take the document, convert it to Postscript, send the postscript file to the printer with the required commands and the printer prints the document.
Now to the actual problem: Most of the document that needs to be printed by our software are invoices, therefore they are carefully made, such that every element is precisely positioned, and if it's off by millimeters the printed document is invalid. When printing the document directly through Adobe or any pdf viewing software, I can select the actual size option and everything is fine. Although if I print it via C# and PJL the document has different margins depending on the printer it has been printed on. Until now we used pdfprinting.net, and that option could be selected through pdfPrint.Scale = PdfPrint.ScaleTypes.None, but how can I do it via PJL?
// This are all the commands that I've tried, none of which achieved what I need
var parameters = new Dictionary<string, string>
{
{ "SET USERNAME",userName},
//{"SET PAPER", "A4" },
//{"SET MEDIATYPE", "PAPER" },
//{"SET TOPMARGIN", "TM6MM" },
//{"SET PRINTAREA", "INKEDAREA" },
{"SET MARGINS", "SMALLER" },
//{ "ENTER LANGUAGE","PDF"},
{ "ENTER LANGUAGE","POSTSCRIPT"},
};
var documentText = "\x1B%-12345X#PJL JOB NAME=" + jobName + " DISPLAY=" + jobDisplay;
foreach (var parameter in parameters)
{
documentText += "\r\n#PJL " + parameter.Key + "=" + parameter.Value;
}
documentText += "\r\n";
documentText += pdfString;
documentText += "\r\n\x0D\x0A\x1B%-12345X\r\n";
RawPrint(printerAddress, documentText, documentName);
// RawPrint() calls the printer methods found in 'winspool.drv', imported via 'DllImport'
Checking the PJL Reference Manual (Edition 12, which is the latest I have seen) there is simply no way to scale the page content in PJL.
Even if there were, I'd be surprised if it carried over to the PostScript (as opposed to PCL) interpreter environment, because PostScript has a rich feature set to handle this sort of setup. So basically you're going to need to get the PostScript correct.
Now, when you take a PDF file and produce PostScript from it, you are almost certainly producing generic PostScript; its device-neutral so it doesn't take account of aspects of the physical device.
Most obviously this will be things like hardware margins and unprintable areas. Many devices have limitations on which parts of the media they can print on, due to the paper handling. These will, of course, be different between different printers.
Of course, when you print from the operating system, the printer device driver knows what the printable area of the media is (because its the specific driver for the printer in question), and so it can arrange for the content to be scaled to the actual media.
Ghostscript certainly can produce PostScript (using the ps2write device) which is suitably scaled and translated for a given printer, provided you know what the characteristics of that printer are. In fact if the printer is sophisticated enough, the PostScript program may be able to interrogate the printer to retrieve some of these characteristics (ImagingBBox, PageOffset, Margins, ImageShift) and its then possible to write a PostScript program to dynamically resize the content of the page, based on these values (the PostScript produced by ps2write does not do this...).
I'm trying to send a file to print without opening it trough Adobe as suggested by several Answers here; instead, I'm using the PrintQueue library (from System.Drawing.Printing).
What I've accomplished so far:
I have the correct PrintQueue referenced as pq:
PrintQueue pq; //Assume it's correct. The way to get here it isn't easy and it is not needed for the question.
// Call AddJob
PrintSystemJobInfo myPrintJob = pq.AddJob();
// Write a Byte buffer to the JobStream and close the stream
Stream myStream = myPrintJob.JobStream;
Byte[] myByteBuffer = ObjectIHave.ToArray(); //Ignore the ObjectIhave, it actually is Linq object which is correct as well.
myStream.Write(myByteBuffer, 0, myByteBuffer.Length);
myStream.Close();
As I understood from the Microsoft Library it's correctly done but it is not working. Any ideas?
EDIT: Debugging the code I can see that something is being send to the printer but it seems the file is not sent.
You need to render your PDF to the printer. Calling the shell print verb on the file would be the ideal means to do that. If you insist on doing the low level rendering yourself then I recommend using a library like Ghostscript.NET and choose the mswinpr2 device as output.
The mswinpr2 device uses MS Windows printer drivers, and thus should work with any printer with device-independent bitmap (DIB) raster capabilities. The printer resolution cannot be selected directly using PostScript commands from Ghostscript: use the printer setup in the Control Panel instead.
See SendToPrinterSample.cs for example:
string printerName = "YourPrinterName";
string inputFile = #"E:\__test_data\test.pdf";
using (GhostscriptProcessor processor = new GhostscriptProcessor())
{
List<string> switches = new List<string>();
switches.Add("-empty");
switches.Add("-dPrinted");
switches.Add("-dBATCH");
switches.Add("-dNOPAUSE");
switches.Add("-dNOSAFER");
switches.Add("-dNumCopies=1");
switches.Add("-sDEVICE=mswinpr2");
switches.Add("-sOutputFile=%printer%" + printerName);
switches.Add("-f");
switches.Add(inputFile);
processor.StartProcessing(switches.ToArray(), null);
}
If the file has to be printed in both sides you just need to add:
switches.Add("-dDuplex");
switches.Add("-dTumble=0");
You can not just write PDF bytes to a print job. The printer doesn't know how to handle it. The RAW data you send to the printer must describe the document in a printer language specific to the printer. That's what the printer driver does.
You can not print a PDF by just sending it to the printer. You need some piece of software that renders the PDF and then sends the rendered image to the printer.
As the documentation states:
Use this method to write device specific information, to a spool file, that is not automatically included by the Microsoft Windows spooler.
I enhanced the important part of this information.
When sending a document, lets say TIFF images to a printer we can send meta-data with the image such as the paper size "Legal, Photo etc". The printer is able to use this information to select a paper tray matching this paper size.
I have a program that generates a tif document and uses PrintDocument to generate a Print job. This process occurs programmatically (no UI). Is it possible to alter the metadata of the tif image programmatically before I send the job to the printer?
E.G. I want to change the paper size of the image to "Legal". This way I can tell the printer which tray to use. I have explored generating an XPS document out of the TIF. Then going back through a XPS API to set the property. However, this solution feels a little heavy. I hope for someone with more experience in this type of programming to point me in the right direction.
The paper size option is available in PrintDocument
private void SetPaperSize()
{
int legalPaperIndex = 5;//See all types: http://msdn.microsoft.com/en-us/library/system.drawing.printing.papersize.rawkind.aspx
for (int i = 0; i < printDocument.PrinterSettings.PaperSizes.Count - 1; i++)
{
if (printDocument.PrinterSettings.PaperSizes[i].RawKind == legalPaperIndex)
{
printDocument.DefaultPageSettings.PaperSize = printDocument.PrinterSettings.PaperSizes[i];
}
}
}
I am making one application where user will print invoices which I am displaying using Crystal Report.
The user showed me his current application made using ForPro. In that application, under Printer Options form, one can see all the printers currently installed and the user could select default printer. When the invoice is made, the user presses the print button, then there is one dialog asking for no. of copies. When it's entered, the invoice gets printed directly, without any Print Dialog Box. If the user wants to change the printer again he/she will change it in the Printer Option form.
I want to know if similar thing is possible in Crystal Report and need guidance on how to approach for it.
Take a look at the ReportDocument.PrintToPrinter SAP Docs or MSDN Docs for how to specify the PrinterName and then Print using the ReportDocument object.
If you can try and get away from how the FoxPro app UI for printer selection. Instead use the standard print dialog box to select the printer.
You should note that if you don't set the PrinterName before sending the report to the printer it will use the default on the crystal file. Not to be confused with the user's OS default printer.
Here's an example of showing the PrintDialog settings some parameters using the SetParameterValue method and then sending the report document to a printer
// Note: untested
var dialog = new PrintDialog();
Nullable<bool> print = dialog.ShowDialog();
if (print.HasValue && print.Value)
{
var rd = new ReportDocument();
rd.Load("ReportFile.rpt");
rd.SetParameter("Parameter1", "abc");
rd.SetParameter("Parameter2", "foo");
rd.PrintOptions.PrinterName = dialog.PrinterSettings.PrinterName;
rd.PrintToPrinter(1, false, 0, 0);
}
The code above no longer works as advertised which has been admitted by SAP
You need to set the report document to an ISCDReportClientDocument and then print it. This is a more robust way of making sure the print job doesn't go to the default printer. The last two lines can be replaced with this code.
CrystalDecisions.ReportAppServer.Controllers.PrintReportOptions printReportOptions = new CrystalDecisions.ReportAppServer.Controllers.PrintReportOptions();
CrystalDecisions.ReportAppServer.Controllers.PrintOutputController printOutputController = new CrystalDecisions.ReportAppServer.Controllers.PrintOutputController();
CrystalDecisions.ReportAppServer.ClientDoc.ISCDReportClientDocument rptClientDoc;
rptClientDoc = cryRtp.ReportClientDocument;
printReportOptions.PrinterName = pDialog.PrinterSettings.PrinterName;
rptClientDoc.PrintOutputController.PrintReport(printReportOptions);
Here is another good link
http://mattruma.azurewebsites.net/?p=258
I'm looking for a simple way to print DIN-A4 paper with data from a database on it. The data should be filled into multiple tables with borders. Some of the data should have a different text format p.e. bold or underlined. I should also be able to print multiple images onto that sheet.
The program should be a Windows Forms Application or a console application written in C#.
Which is the easiest / most common way to format data and print it like that?
Any suggestions apreciated :)
EDIT:
this is my current code with almost no success. It actually prints but what I get is simply the xml file printed.
private void printButton_Click(object sender, EventArgs e)
{
string printPath = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
fileToPrint = new System.IO.StreamReader(printPath + #"\test.xml");
printFont = new System.Drawing.Font("Arial", 10);
PrintDocument printDocument1 = new PrintDocument();
printDocument1.PrintPage += new PrintPageEventHandler(printDocument1_PrintPage);
printDocument1.Print();
fileToPrint.Close();
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
float yPos = 0f;
int count = 0;
float leftMargin = e.MarginBounds.Left;
float topMargin = e.MarginBounds.Top;
string line = null;
float linesPerPage = e.MarginBounds.Height / printFont.GetHeight(e.Graphics);
while (count < linesPerPage)
{
line = fileToPrint.ReadLine();
if (line == null)
{
break;
}
yPos = topMargin + count * printFont.GetHeight(e.Graphics);
e.Graphics.DrawString(line, printFont, Brushes.Black, leftMargin, yPos, new StringFormat());
count++;
}
if (line != null)
{
e.HasMorePages = true;
}
}
Printing in .NET is quite hard without additional tools.
Visual Studio 2008 and Report Wizard
Its my favorite tool. It's based on Microsoft Reporting Services which is available in Visual Studio 2008. It works similar to MS Access reports functionality.
Unfortunatelly i was not able to run it and design reports in other Visual Studio versions. Reporting Services are available in .NET Framework and you can use them with any Visual Studio, but there is no Report Designer/Wizard tool in versions other than 2008).
Crystal Reports (expensive but really good).
If you have some time you may fight with pixels like this:
Simplified .NET printing in C# Dave Brighton at codeproject.com
WebBrowser control, as #colosso wrote in another answer, but this depends on Internet Explorer version and i personally don't like this method.
I found two possibilities:
1) In my opinion this is the worse one. I found a third-party software called "Antena house formatter". You can find it on www.antennahouse.com but it's unfortunately neither open source nor free software. This software allows you to convert xml, xsl or xsl-fo data into pdf and other formats. From there you could print it with standard c#.
I didn't chose this way for some reasons: To be hooked on a third-party software is no good solution in my opinion, although this Antennahouse formatter is a quite nice, reliable and fast software.
2) This is the solution I have chosen. You can create a simple WebBrowser control and fill it either with a saved html file or you can just fill it with a dynamically created string. I now generate a string witch contains the whole html document an load it into the Webbrowser control:
webBrowser1.Document.OpenNew(true);
string strHtml = "<html><head></head><body></body></html>";
webBrowser1.Document.Write(strHtml);
When loading the form I open a new "tab" with:
webBrowser1.Navigate("about:blank");
You can either show the Webbrowser control to have a "preview" of the site that is getting printed or just hide it. Finally, when you loaded you html file into the control you can print it with:
webBrowser1.Print();
It will ow print the document with you default printer. I know, using a html file to print a site like that feels some sort of "hacky" but it's the easiest way I found to do something like that. Especially if you net to print very complex Sites with many different things on it.
Nice to know:
I had to print DIN-A4 sites. I set my content with to 670px and that fits nice.
You are printing with a Webbrowser control. He will take the printer settings from your local installed Internet Explorer (or the registry if we want to be exactly). If you want to change it go into your IE > Printing > Page settings... and set you desired options or just change it in your registry.
Hope this helps someone :)