A minap egy érdekes problémába ütköztem. Fejlesztek egy Mono-s alkalmazást, melynek mind Linux-on mind Windows-on futnia kell és ami a lényeg, nyomtatni is kell a programból. A program felületét Gtk#-al oldottam meg a platformfüggetlenség miatt, így a natív kinézet mindkét platformon megoldott és problémamentes. Voltak kísérleteim a Windows.Forms-al is, ez azonban a Mono-ban még nagyon hiányos. A Gtk pedig egyébként is közelebb áll a szívemhez. A fejlesztés egyik kritikus pontja volt a megfelelő felület kiválasztása, amely a Gtk#-al meg is oldódott. Nem is gondoltam, hogy a nyomtatás lesz az a terület, amely a legkomolyabb hiányosságokat fogja mutatni.
Egy szó mint száz a fejlesztés azon szakaszához értem, ahol a nyomtatás került volna megvalósításra, azonban azt kellett, hogy tapasztaljam ez nem fog olyan könnyen menni mint ahogyan azt előzőleg gondoltam. A .NET keretrendszerben a System.Drawing.Printing névtér felelős a nyomtatások megvalósításáért. Íme egy egyszerű példa:
public void Printing(string printer)
{
try {
streamToPrint = new StreamReader (filePath);
try {
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler (ppEventHandler);
pd.PrinterSettings.PrinterName = "HP_LJ4540";
if (pd.PrinterSettings.IsValid) {
pd.Print();
}
else
System.Console.WriteLine("Printer is invalid...");
}
finally {
streamToPrint.Close();
}
}
catch(Exception ex) {
System.Console.WriteLine("Error: {0}", ex.Message);
}
}
Az első probléma a PrinterSettings.InstalledPrinters property használatával jön elő. Ez a property volna felelős a renszerben telepített nyomtatók lekérdezéséért, melyeket egy StringCollection-ön keresztül ad vissza. Mono-val futtatva a kódot azonban NotImplementedException-el áll le futás a fenti nyomató lekérdezéshez érve. Ez összességében véve még nem jelentett volna számomra túl nagy problémát, hiszen a probléma áthidalható lenne. A beállításokat tartalmazó XML file-ba fel lehetne venni egy olyan értéket amely a program számára alapértelmezett – és természetesen a rendszerben telepített – nyomtató nevét tartalmazza és a programból csak azt kérdezném le. De mint kiderült ez csak a kisebbik probléma. Tulajdonképp a nyomtatás a Mono-ban – úgy ahogy van – még erős fejlesztés alatt áll. A PrintDocument.Print() metódus egy – ha több oldalas a dokumentum akkor több – Jpeg képet generál az aktuális könyvtárba ‘documentname xxxx.jpg’ néven, ahol az xxxx az oldal számát reprezentálja. A jelnség okát a forráskód OnEndPage() metódusában meg is találhatjuk:
public override void OnEndPage(PrintDocument document, PrintPageEventArgs e)
{
//TODO: print current page
// - image to print is this.image
// - page settings are in e.PageSettings
// - printer settings are in document.PrinterSettings
// - don't forget to use document.OriginAtMargins (only if .NET 1.1)
// actually, "print" == "save to a file"
try {
string fileName = document.DocumentName + " " + page.ToString("D4") + ".jpg";
Console.WriteLine("StandardPrintController: Print page \"{0}\"", fileName);
image.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch (Exception) {}
if (e.Graphics != null) e.Graphics.Dispose();
}
Gondoltam arra, hogy számomra a generált Jpeg file-ok is megfelelőek lesznek a nyomtatási produktum előállításához, hiszen az lpr parancsot megfelelően felparaméterezve “bemásolhatom” a képet a nyomtatási sorba – amennyiben Linuxon fut a kód – és azzal le is tudtam a nyomtatást. Az elkészült kép minősége Linuxon azonban hagy némi kivetni valót maga után. Igényes minődégű dokumentumok sajnos így nem nyomtathatóak. Ott volna még az a lehetőség, hogy az OnEndPage() metódusban a System.Drawing.Imaging.ImageFormat osztály kimenetét TIFF-re, PNG-re vagy esetleg BMP-re módosítva javítani lehet a minőségen. Be kell azonban látni, hogy a Mono mainstream kódját módosítani önös célok érdekében nem érdemes, sőtt tűzzel, vassal megelőzendő.
Alternatív megoldást kellet, hogy találjak. Na igen, ott volt még a libgnomeprint, de irtóztam attól, hogy két eltérő megoldással működjön a nyomtatás – vagy bármely más funkciója a programnak – attól függően, hogy milyen platformon fut. Ez elsősorban a programozói lustaságomnak köszönhető, de egyébként sem tartom szerencsésnek, hogy a kód ennyire szerteágazzon.
Szerencsére találtam egy módszert, amely kiválóan megfelel az igényeimnek. Lehet, hogy nevetséges, de működik. Találtam a SourceForge-on egy library-t amely PDF file-ok készítésére alkalmas. A library neve sharpPDF. Ezt hozzáadva a kódunkhoz, játszi könnyedséggel tudunk készíteni futás időben PDF dokumentumokat, amelyet aztán az adott platformon – bármely PDF nézegetővel – már küldhetünk is a nyomtatóra. Egy sharpPDF példa:
pdfDocument myDoc = new pdfDocument("HelloWorld","ME");
pdfPage myPage = myDoc.addPage();
myPage.addText("Hello World!",200,450,predefinedFont.csHelvetica,20);
myDoc.createPDF("/tmp/test.pdf");
myPage = null;
myDoc = null;
Látható, hogy a library-t tényleg nagyon egyszerű használatba venni. Én a programban úgy oldottam meg, hogy a konfigurációs XML file tartalmat egy bejegyzést a PDF nézegetőre vonatkozóan:
/usr/bin/gpdf
Ezt felhasználva a kódból, az alkalmazást felparaméterezve a generált PDF dokumentummal már látható és nyomtatható is az elkészült anyag:
private Config cfg = new Config();
try {
myDoc.createPDF(System.IO.Path.Combine(cfg.GetTempPath(), "szamla.pdf"));
}
catch (System.Exception ex) {
System.Console.WriteLine("Error: {0}", ex.Message);
}
System.Diagnostics.Process.Start(cfg.GetProperty("PDFviewer"), System.IO.Path.Combine(cfg.GetTempPath(), "szamla.pdf"));
Végül így sikerült megoldanom a Mono-ban jelenleg még hiányos nyomtatási függvények mellőzését.









