i'm developing an app in wpf that is printing photos in a loop, so I would like to get actual moment when the job i really done and I can proceed to another.
I'm printing using PrintDocument class, I tried EndPrint event but it is hit when file is sent to the printer, not when printer did its job. Also in system queue the document disappears before end of printing. Is it possible to get some how information of status of printer?
PrintDocument pd = new PrintDocument();
PrinterSettings.PaperSizeCollection ps = pd.PrinterSettings.PaperSizes;
PaperSize size = new PaperSize();
foreach (PaperSize Psz in ps)
{
if (Psz.PaperName == ev.PaperSize)
{
size = Psz;
break;
}
}
pd.PrinterSettings.PrinterName = ev.printerName;
pd.DefaultPageSettings.PaperSize = size;
pd.DocumentName = "aaaaa";
pd.PrintPage += PrintPage2;
pd.EndPrint += new PrintEventHandler(this.PrintEnd2);
pd.Print();
var myPrintServer = new LocalPrintServer();
var pq = myPrintServer.GetPrintQueue(ev.printerName);
var jobs = pq.GetPrintJobInfoCollection();
foreach (var job in jobs)
{
var done = false;
while (!done)
{
pq.Refresh();
job.Refresh();
done = job.IsCompleted || job.IsDeleted || job.IsPrinted;
}
}
ai++;
PopAction();
It can be find out through PrintSystemJobInfo.IsCompleted.
https://learn.microsoft.com/en-us/dotnet/api/system.printing.printsystemjobinfo.iscompleted?view=netframework-4.7.2
Related
I used pdfiumViewer to print a label created by itextshape, i finded this code in stackoverflow, it's work good for A4 paper but for custom paper like my label the problem going to happening. This is code:
public static void PrintPDF(string printer, string paperName, string filename, int copies, bool isduplex = false, bool isHorizontal = false, bool printLabel = false)
{
try
{ // Create the printer settings for our printer
var printerSettings = new PrinterSettings
{
PrinterName = printer,
Copies = (short)copies,
Duplex = Duplex.Simplex,
};
if (isduplex && printerSettings.CanDuplex && isHorizontal)
{
printerSettings.Duplex = Duplex.Horizontal;
}
if (isduplex && printerSettings.CanDuplex && isHorizontal == false)
{
printerSettings.Duplex = Duplex.Vertical;
}
// Create our page settings for the paper size selected
var pageSettings = new PageSettings(printerSettings)
{
};
if (printLabel == true)
{
PaperSize paper = new PaperSize("label", 460, 260);
pageSettings.PaperSize = paper;
pageSettings.Margins = new Margins(0, 0, 0, 0);
}
else
{
foreach (PaperSize paperSize in printerSettings.PaperSizes)
{
if (paperSize.PaperName == paperName)
{
pageSettings.PaperSize = paperSize;
break;
}
}
}
// Now print the PDF document
if (printerSettings.IsValid)
{
using (var document = PdfiumViewer.PdfDocument.Load(filename))
{
using (var printDocument = document.CreatePrintDocument(PdfiumViewer.PdfPrintMode.CutMargin))
{
printDocument.PrintController = new StandardPrintController();
printDocument.OriginAtMargins = true;
printDocument.PrinterSettings = printerSettings;
printDocument.DefaultPageSettings = pageSettings;
printDocument.Print();
}
}
}
}
catch
{
throw;
}
}
The problem is the HardMarginX alway is 20. It is readonly properties so i can not change. So when i print, paper alway margin left some space. So anyway i can do to fix this problem.
Thank for reading
PaperSize paperSize = new PaperSize("Test", 315, 300);
paperSize.RawKind = (int)PaperKind.Custom;
Using This Code
So I have a function that renders a Queue Stream class. Based on a conditional, I would like to add an additional byte array to this queue. I thought I could do this by converting the byte array to a stream, and then Enqueueing it with this additional stream.
I do notice that the count of the streams goes up after enqueueing the original stream, and I pass that into a function that prints the streams. This is where I receive the exception "A generic error occured in GDI+."
EDIT: After going through each page, I realized that when combining the TermsandConditions Byte array to the Stream Queue, it was showing as just one byte Array, where as the T&C's is 2 pdfs.
So my next question is how to convert a pdf to an image?
Here's the function where I'm combining the queues:
internal static void DoPrintInvoice(int orderID, SalesOrderBLL.DocumentType doctype, string printer, int copies, List<string> lines)
{
using (var context = rempscoDataContext.CreateReadOnlyContext())
using (MiniProfiler.Current.Step("DoPrintInvoice()"))
{
// Generate Report
using (var report = GetSalesOrderReport(orderID, _DocumentTypeDescriptions[doctype], doctype != DocumentType.InvoiceLetterhead, lines))
{
// render queue streams for printing
var streams = PrintingBLL.RenderStreams(report, landscape: false);
//add additional byte array to stream.
var TermsAndConditions = GetTermsAndConditions().ToArray();
Stream TCStream = new MemoryStream(TermsAndConditions);
if (doctype == DocumentType.OrderAcknowledgement)
{
streams.Enqueue(TCStream);
}
// render and save pdf in background, print report
BackgroundTask.ParallelInvoke(
() => SaveSalesOrderPDF(orderID, doctype, report),
() => PrintingBLL.PrintStreams(streams, string.Format("Sales Order ({0})", report.DisplayName), printer, copies, false)
);
}
}
}
Then here's the PrintStreams function:
internal static void PrintStreams(Queue<Stream> streams, string documentName, string printer, int copies, bool landscape)
if (copies > 0)
{
// get printer details
using (var pd = new PrintDocument())
{
lock (_PrintLock)
{
pd.PrinterSettings.PrinterName = printer.Trim();
if (!pd.PrinterSettings.IsValid)
throw new ArgumentOutOfRangeException(string.Format("Invalid printer \"{0}\". Please try again with a different printer.", printer));
}
pd.DocumentName = documentName;
pd.PrintController = new StandardPrintController();
pd.PrinterSettings.Copies = (short)copies;
pd.PrinterSettings.Collate = true;
pd.PrinterSettings.DefaultPageSettings.PaperSize = pd.PrinterSettings.PaperSizes.Cast<PaperSize>().First(ps => ps.Kind == PaperKind.Letter);
pd.DefaultPageSettings.Landscape = landscape;
pd.DefaultPageSettings.Margins = new Margins()
{
Top = 0,
Bottom = 0,
Left = 0,
Right = 0,
};
var numPages = streams.Count;
var currentPage = 0;
pd.PrintPage += (s, ev) =>
{
BackgroundTask.SetCurrentStatus(
currentPage / numPages,
string.Format("Printing page {0} of {1}. [{2}]", currentPage, numPages, documentName));
// get next page
var ms = streams.Dequeue();
// if we have any streams left, then we have another page
ev.HasMorePages = streams.Any();
// reset stream
ms.Position = 0;
// read page image
var image = new Metafile(ms);
var r = new Rectangle()
{
X = 0,
Y = 0,
Width = 825,
Height = 1075,
};
if (landscape)
{
r.Height = 825;
r.Width = 1075;
}
// draw image directly on page
ev.Graphics.DrawImage(image, r);
// destroy stream
ms.Close();
currentPage++;
};
try
{
lock (_PrintLock)
pd.Print();
BackgroundTask.SetCurrentStatus(100, string.Format("Finished printing {0}", documentName));
}
catch (Exception e)
{
BackgroundTask.SetCurrentStatus(0, string.Format("Printing Error: {0}", e.Message));
throw new InvalidOperationException(
string.Format("The document failed to print. Arguments were: documentName = {0}; printer = {1}; copies = {2}; landscape = {3}",
documentName,
printer,
copies,
landscape),
e);
}
}
}
}
I need to print a pdf with specific printer settings from my c# application. Its required that the printer uses best quality in image compression, fine setting for the pattern option and 600 dpi as resolution.
Currently I try to set these values with the PrintParams, but the result is not the same as when I set the values manually with the printer settings GUI.
Any idea how to set these values?
using (Document doc = new Document(fi.FullName))
{
if (doc != null)
{
Console.WriteLine("created print document object with given pdf");
}
using (PrintUserParams userParams = new PrintUserParams())
{
if (userParams != null)
{
Console.WriteLine("created user parameters for printing");
}
userParams.PrintParams.BinaryOK = false;
userParams.StartPage = 0;
userParams.EndPage = doc.NumPages;
Console.WriteLine(string.Format("document contains {0} pages", doc.NumPages));
userParams.NCopies = 1;
userParams.ShrinkToFit = false;
userParams.PrintParams.ShrinkToFit = false;
userParams.PrintParams.ExpandToFit = false;
userParams.DeviceName = printerName;
userParams.PrintParams.BitmapResolution = 600;
userParams.PrintParams.OptimizeForSpeed = false;
userParams.PrintParams.FlattenInfoExternalDPI = 600;
userParams.PrintParams.GradientResolution = 600;
userParams.PrintParams.UseFullResolutionJP2KData = false;
doc.Print(userParams, null);
Console.WriteLine(string.Format("document {0} has been sent to printer {1}", fi.FullName, printerName));
}
}
Try using DEVMODE instead PrintUserParams.You can open printer properties from c# using windows api and modify/save DEVMODE.
Is there a way to print a PDF and select the paper tray to use programmatically?
I'm open to suggestions such as converting the PDF to a different format and printing from there.
I can print to the correct tray using PaperSource() and PrintDocument() is it possible to convert PDFs into a format that these functions can understand?
Thanks.
Based on getting the paper tray PaperSource from something like here on MSDN, if you don't mind using Ghostscript.NET, this should work for you:
public void PrintPdf(string filePath, string printQueueName, PaperSource paperTray)
{
using (ManualResetEvent done = new ManualResetEvent(false))
using (PrintDocument document = new PrintDocument())
{
document.DocumentName = "My PDF";
document.PrinterSettings.PrinterName = printQueueName;
document.DefaultPageSettings.PaperSize = new PaperSize("Letter", 850, 1100);
document.DefaultPageSettings.PaperSource = paperTray;
document.OriginAtMargins = false;
using (var rasterizer = new GhostscriptRasterizer())
{
var lastInstalledVersion =
GhostscriptVersionInfo.GetLastInstalledVersion(
GhostscriptLicense.GPL | GhostscriptLicense.AFPL,
GhostscriptLicense.GPL);
rasterizer.Open(filePath, lastInstalledVersion, false);
int xDpi = 96, yDpi = 96, pageNumber = 0;
document.PrintPage += (o, p) =>
{
pageNumber++;
p.Graphics.DrawImageUnscaledAndClipped(
rasterizer.GetPage(xDpi, yDpi, pageNumber),
new Rectangle(0, 0, 850, 1100));
p.HasMorePages = pageNumber < rasterizer.PageCount;
};
document.EndPrint += (o, p) =>
{
done.Set();
};
document.Print();
done.WaitOne();
}
}
}
With reference to my previous question, I already wrote my program and it is working awesome when I am using VS2008 for run it.
I have two more questions:
1. I want to check with you guys when I run my program all the mail are appearing in VS output as xml file, but I never used to print them in output. is it usual for all or I need to add something to remove it. I feel it takes long time from my PC to show mails in output.
2. my second question is that when I want to use only exe file stand alone(run program via exe file not with VS ) I am receiving below error and program is hanging & close.
"MailReader has encountered a problem and needs to close. We are
sorry for the inconvenience."
As I mentioned above this program is working fine in VS.
I copy part of my code that read mails and split them for your reference.
public void ReadMail()
{
ServicePointManager.ServerCertificateValidationCallback += delegate(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) { return true; };
try
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.TraceEnabled = true;
service.Credentials = new WebCredentials(_username, _password); //Modify this
service.Url = new Uri(_exchange); //Modify this
service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri(_exchange);
service.TraceEnabled = true;
service.Credentials = new WebCredentials(_username, _password); //Modify this
service.Url = new Uri(_exchange);
//SearchFilter to get unreaded messages only.
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
ItemView itemview = new ItemView(Int16.MaxValue);
//DateTime searchdate = new DateTime(2012, 7, 6); //Year, month, day
SearchFilter greaterthanfilter = new SearchFilter.IsGreaterThan(ItemSchema.DateTimeSent, Convert.ToDateTime(startDate));
SearchFilter lessthanfilter = new SearchFilter.IsLessThan(ItemSchema.DateTimeSent,Convert.ToDateTime(finishDate));
SearchFilter[] f = { greaterthanfilter, lessthanfilter };
SearchFilter filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, f);
//Folder folder = Folder.Bind(this.m_Service, WellKnownFolderName.MsgFolderRoot); //Or the folder you want to search in
//FindItemsResults<Item> results = folder.FindItems(filter, new ItemView(1000));
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.SentItems,filter, itemview);
Action action = () => fr.setText(findResults.Items.Count + "mails need to analysis from "+startDate +" to "+ finishDate);
fr.Invoke(action, null);
action = () => fr.setMaximumProgressBar(findResults.Items.Count);
fr.Invoke(action, null);
dmd = new List<DailyMailDetails>();
foreach (Item item in findResults.Items)
{
string messageDate = "Error in Date";
string messageSubj = "Error in Subject";
int index = 0;
try
{
PropertySet propertySet = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.DateTimeSent, ItemSchema.Body, ItemSchema.Subject);
propertySet.RequestedBodyType = BodyType.Text;
EmailMessage message = EmailMessage.Bind(service, item.Id, propertySet);
string temp = startSign.ToUpper();
int start = message.Body.Text.ToUpper().IndexOf(temp) + temp.Length;
int end = message.Body.Text.ToUpper().IndexOf(finishSign.ToUpper());
int len = end - start;
string text = message.Body.Text.Substring(start, len);
index = findDmdIndex(message.DateTimeSent.ToShortDateString().ToString());
if (index == -1)
{
dmd.Add(new DailyMailDetails(message.DateTimeSent.ToShortDateString().ToString(), (List<PeopleSigniture>)Extensions.Clone<PeopleSigniture>(OrginallistPeopleSign)));
index = dmd.Count - 1;
}
bool signExist = false;
for (int i = 0; i < listPeopleSign.Count; i++)
if (text.ToUpper().Contains(dmd[index].peopleSign[i].Signiture.ToUpper()))
{
dmd[index].peopleSign[i].addResponse(message.DateTimeSent.ToString(), message.Subject.ToString());
signExist = true;
break;
}
messageDate = message.DateTimeSent.ToString();
messageSubj = message.Subject.ToString();
if (!signExist)
dmd[index].peopleSign[dmd[index].peopleSign.Count - 2].addResponse(message.DateTimeSent.ToString(), message.Subject.ToString());
}
catch (Exception ex)
{
dmd[index].peopleSign[dmd[index].peopleSign.Count - 1].addResponse(messageDate, messageSubj);
}
action = () => fr.increasePrograss();
fr.Invoke(action, null);
}
}
catch (Exception ex)
{
MessageBox.Show("Class: Mail Function:ReadMail" + ex.Message);
}
Action action2 = () => fr.setText(ToString(true),dmd);
fr.Invoke(action2, null);
}
For issue #1 - you are viewing the XML output likely because you have EWS tracing enabled. You need to set ExchangeService.TraceEnabled to false or comment it out entirely. (You also have many duplicate lines of code you need to cleanup.)
service.TraceEnabled = false;
For issue #2 - you need to determine the actual .NET exception. Without this - we cannot help you further. It could be crashing for countless reasons. Please provide a stack trace.