Posts Tagged: programozás

2007
03
júl

Gondolatok keretrendszerekről


Az elmúlt másfél-két évben az ASP.NET-el való foglalatoskodásom eredményeként sikerült kisebb-nagyobb eredményeket felmutatnom a webes fejlesztések területén. Persze egy fejlesztő sosem – vagy csak meglehetősen ritkán kötelezi el magát – egyetlen programozási nyelv, fejlesztőkörnyezet vagy netán platform mellett. Magamból kiindulva mindig keresem az új lehetőségeket és mindig akad valami ami aktuálisan lázba tud hozni. A kezdeti lelkesedés aztán gyakran rövid idő alatt elmúlik de van olyan is, hogy valami végleg vagy legalábbis hosszabb időre belopja magát a szívembe. Kezdetben az ASP.NET és az egész .NET keretrendszer pont ilyen volt számomra. Nem igazán értettem, hogy miért jó nekünk egy újabb “Java szerű” nyelv egy “Java szerű” virtuális géppel stb. Aztán persze a C# nyelv megismerését követően enyhült az ellenszenvem. Az i-re a pontot végül a Mono project tette fel, amikor megteremtette a valódi platformfüggetlen alkalamazásfejlesztés lehetőségét.

Azt hiszem, hogy senkinek sem kell azt bizonygatnom, hogy mennyire szeretem a C#-ot, az ASP.NET-et vagy a Mono keretrendszert. Rengeteg dologra nyitotta rá a szemem ez a trió. Annó úgy gondoltam, hogy a C nyelvnél és a gcc fordítónál jobbra sosem lesz szükségem, hisz ezekkel mindent meg lehet csinálni amiben csak valaha gondolkodni mertem. Persze ez az állítás továbbra is igaz, de tekintve, hogy a fejlesztési idők drasztikusan csökkennek, nekem ma már sem időm, sem kedvem azzal bajlódni, hogy memóriát allokáljak és szabadítsak fel az alkalmazásomnak vagy azon gondolkodjak, hogy egy adatszerkezetet hogyan írok ki lemezre, ne adj isten hogyan továbbítom azt hálózaton keresztül. Nem újkeletű a problémán, a hozzám hasonló cipőben járók tudják, hogy miről beszélek.

Persze most a kliens oldali alkalmazások fejlesztésről beszéltem, de lassan majdcsak oda érek amiről írni szeretnék :-) Úgy négy éve lehetett mikor elkezdtem nyitni a .NET felé. Ez még messze volt attól, hogy komolyan foglalkoztam volna vele, túlságosan új volt számomra az egész technológia ahhoz, hogy átlássam és megértsem. Mindazonáltal érdekelt a dolog és túl voltam már azon, hogy puszta dacból elutasítsam az egészet a Java fejlesztők által azóta is gyakran hangoztatott indokokra hivatkozva.

Nagyjából három éve kezdtem el aktívan foglalkozni a keretrendszerrel és a C# nyelvvel. Akkoriban Linuxos (és/vagy Windowsos) desktop megoldásokkal foglalatoskodtam, úgy mint például a Gnome-RDP és a SharpSzámla, de akadtak szép számmal belső fejlesztések is. Később egyre gyakrabban merült fel az igény webes alkalmazások fejlesztésére. Ekkor kezdtem el kacérkodni az ASP.NET gondolatával. A hagyományos ASP-s oldalak készítésével kapcsolatban volt már tapasztalatom és pont ez volt sokáig a visszatartó erő, de a C# mindezt feledtette velem és szép lassan sikerült elmerülnöm az új technológia nyújtotta előnyökben. Ezzel szemben persze a PHP volt a “konkurencia”, azonban az ASP.NET használatával oly mértékben lerövidült a fejlesztési idő és annyi kész megoldást kaptam a 2.0-ás keretrendszer használatával, hogy már gondolni sem mertem a PHP-s session- és formkezelésre vagy például a felhasználók hitelesítésére. Persze itt még sokáig tudnám folytatni a kellemes és hasznos dolgok taglalását, de szeretnék egy kicsit az dolgok árnyoldalával is foglalkozni és legfőképpen kitérni arra amit mások már megint évekkel előttem észrevettek :-)

A Mono-t az Microsoft .NET frameworkjének a mintájára készítik. Azt nem tudom, hogy milyen módszereket alkalmaznak, őszintén megvallva nem is érdekel, egy dolog biztos: az aktuális Microsoft féle .NET keretrendszerhez képest a Mono-nak mindig kisebb-nagyobb lemaradása van. Ha valaki a legfrissebb újdonságokra épít, biztos lehet benne, hogy a programja nem fog hiba nélkül működni Mono-val. Ez a kisebbik probléma. Sajnos sokszor egy-egy feature máshogy működik az egyik keretrendszerben mint a másikban. Épp csak egy kicsit, de az épp elég ahhoz, hogy rendellenes eredményt produkáljon. Csak, hogy egy példát is hozzak erre:

Az MS féle ASP.NET-et használva a DataGrid-nek az oszlopait futási időben eltüntethetjük vagy megjeleníthetjük, Mono esetén kivételt kapunk, hogy ez a feature még nem került implementálásra. És sok-sok hasonlót lehetne még hozni. Ha jól tudom, akkor a Vistát már a 3.0-ás .NET frameworkkel szállították, Monos oldalról itt igen tekintélyes lemaradás tapasztalható. Mindezek mellett persze a Mono framework egy kiemelkedő és hiánypótló alkotás és nem lehet a fent említett hiányosságokat részére hibaként felróni. Az elmaradások érthetőek és nem szorulnak magyarázatra.

Számomra a legfájóbb hiányosság Monos oldalról a hosting tekintetében tapasztalható. Nem nagyon lehet találni olyan hosting céget aki Linuxos szerveren ASP.NET 2.0-ás szolgáltatást biztosítana. Eddig csupán egy amerikai céget találtam akiknél megfizethető áron lehet Linuxos kiszolgálással ASP.NET-es szolgáltatásra előfizetni, de sajnos csak 1.1-esre. Hát, azt pedig köszi, de inkább már nem! Windowsos persze van, de nem hiszem, hogy magyarázatra szorul az, hogy ettől miért nem jövök lázba.

Ez volt tehát a fekete leves, most pedig jöjjön az amit az imént említettem, ami rányitotta a szemem a saját vakságomra. Ha azt mondom, hogy Ruby On Rails akkor a tapasztalt webes fejlesztők már fel sem kapják a fejüket hiszen teljesen természetes dologról van számukra szó. Nem úgy az olyan maradi, Pascal és C nyelven felnőtt “programozóknak” mint például én. Számomra sokáig nem állt össze a kép a fent említett nyelv – vagy nevezzük inkább keretrendszernek – kapcsán. Félreértés ne essék, nem egy Ruby On Rails áradozás fog következni.

Egy-két posttal ezelőtt elkezdtem ismételt áradozásomat a Drupal CMS-ről. Akkor is említettem, hogy mennyire jó a Drupal modul fejlesztőknek szóló dokumentációja. Ekkor indultam el azon az úton, hogy hosszú idő után ismét PHP-t kezdtem idomítani. Erre alapvetően három okom volt:

  1. az előbb említett ASP.NET-es hosting hiányosság,
  2. a Drupal CMS kellemes programozhatósága,
  3. Mac OS X-en is fejleszthető, nem kell virtuális gépen kódolnom vagy másik rendszert bootolnom.

A Drupal kapcsán aztán rájöttem, hogy több régóta érlelt projektötlet is van amit szeretnék megvalósítani vagy akár migrálni a szélesebb körben való elterjedés érdekében PHP-s alapokon. Mindaddig amíg egy egyszerű Drupal modul megvalósításáról beszélünk könnyű dolga van a programozónak, – de a saját véleményem szerint – egy komplexebb projekt megvalósítása már komoly tervezési és megvalósítási nehézségekbe ütközhet ha elrugaszkodunk a Drupal alapverő mivoltától, attól, hogy ez egy tartalomkezelő rendszer.

Így találtam rá véletlenül valamelyik nap a Prado-ra. A Prado, hogy egészen pontos legyek egy PHP 5-ös komponens keretrendszer (component framework for PHP 5), amely az ASP.NET szolgáltatásaival elég komoly hasonlóságot mutat. Csak, hogy néhány fontosabbat említsek a teljesség igénye nélkül:

  • Eseményvezérelt programozási keretrendszer
  • Felhasználókezelés
  • “CodeBehind” alkalmazása, az oldal és az azt vezérlő kód elszeparálása
  • MVC (Model-View-Controller) architektúra
  • DAO (MySQL, PSQL, MS-SQL és Sqlite), Active Record, SQL táblák osztályokhoz rendelése (SqlMap Data Mapper)
  • Integrált AJAX-os vezérlők
  • XML alapú konfigurációs állományok
  • SOAP szolgáltatások
  • Mester és tartalmi oldalak
  • Kivételkezelés

Adódik a kérdés, hogy mindezekhez mire van szükség? Mindössze egy webszerverre, amely rendelkezik legalább PHP 5.1.0 támogatással. Valljuk be ez nem túl nagy igény :-) A Prado oldalán lévő dokumentáció és a példaprogramok elég komolyan rávilágítanak arra, hogy mi mindent lehet megvalósítani a keretrendszerrel és mindezt mennyi idő alatt, mekkora befektetéssel. Valamint arról sem szabad elfeledkeznünk, hogy mennyire marad áttekinthető és módosítható a későbbiek során a kódunk. Lássuk a HelloWorld példát:

  • Home.page:
  • <html>
      <body>
        <com:TForm>
          <com:TButton Text="Click me" OnClick="buttonClicked" />
        </com:TForm>
      </body>
    </html>
  • Home.php:
  • class Home extends TPage
    {
        public function buttonClicked($sender,$param)
        {
            // $sender refers to the button component
            $sender->Text="Hello World!";
        }
    }

    Prado-t leginkább a Ruby On Rails-hez tudnám hasonlítani koncepcióban valamint az ASP.NET-hez szintaktikában és filozófiában. Mindezt PHP-s megvalósítással. Elég érdekes, ugyanakkor ütőképesnek tűnő egyvelege ez a különböző nyelveknek és technológiáknak. Nekem nagyon felkeltette az érdeklődésemet és tekintve, hogy 2004 óta fejlesztik, elég kiforrottnak is tűnik. Ami pedig a dolog apropója, hogy pont a napokban jelent meg – egy év fejlesztést követően – a 3.1.0-ás verzió ami számos nagyon hasznos újítást hozott magával.

    Egy gondolat és egy kérdés maradt a végére:

    • Nem tudtam róla, hogy ilyen sok jó PHP-s framework létezik: Prado, Symfony vagy CakePHP. Nyitottabb szemmel kellene járnom.
    • A kérdés: hogyan lehet védeni a PHP-s forráskódot. Létezik erre valami technika, hogy a PHP-ben írt alkalmazás kódja védhető legyen a külső módosításoktól? Nem a webszerver felőli védelemre gondolok, hanem a tényleges PHP állományok védelmére.
2007
22
ápr

Knight Rider


KittA Cocoa programozás megismerése során olyan dolgokat tudhatunk meg, mint például azt, hogy a Knight Rider nagy sikerű sorozat főszereplőjének autója a “Knight Industries 2000 Super Car” nevet viseli és ezért hívják “Kitt”-nek. Mindez a Helper Object-ekkel áll összefüggésben:

…The problem was that to subclass Michael Knight, you would need to know an awful lot about his guts so that you could wire them to guns and booster rockets. So instead, they created a helper object, the Knight Industries 2000 Super Car, or “Kitt”…

Cocoa Programming for MacOSX – 2nd Edition

2007
15
ápr

Programozási nyelv és ismerkedés


Mint kezdő Mac-es az elmúlt hetem nagyjából azzal telt, hogy megismerjem és testre szabjam frissen használatba vett rendszeremet. Ez persze egy örömteli feladat, ha egy kellemesen használható rendszeren teszi az ember. Keresgélni, válogatni a mindennapi munkához és szórakozáshoz elengedhetetlen programokat. Tesztelgeti, kísérletezgetni a rendszer képességeivel, próbálgatni a már meglévő hardverelemekkel a kompatibilitást jó móka. Jó móka akkor, ha minden működik, ha nem arról szól a történet, hogy izzadva, kínkeservesen hozzunk össze valamit. Szerencsére erről jelen esetben szó sincs és napról-napra egyre elégedettebb vagyok a kis MacBookkal.

Ha minden igaz, akkor mostanra már minden korábban használt programom – vagy annak valamilyen alternatívája – működik a gépemen. A programválaszték szerencsére mégsem olyan szegényes mint ahogyan arra egy korábbi postban utaltam. A harverek tesztelésének is a végére értem. Itt nem olyan széles a paletta: két mobiltelefon (SE T610 és RAZR v3i), egy PDA (Acer n311), külső winchesterek (USB és FireWire) és fényképezőgép (Samsung Cyber530). Ezek mindegyikét sikerült működésre bírni. Bár a “működésre bírás” egy kicsit túlzás, mert minden működik mindenféle erőltetett haxolás és konfigolás nélkül. A PDA-val van csupán egy kis bibi, mivel az n311-en már WM5.0 van így a szinkronizálás nem nagyon akar menni a Missing Sync nevű program használatával USB-s kapcsolódás esetén. Ha RNDIS-ről serial módra váltok az USBSwitch nevű program segítségével, akkor látja egymást a MacBook és a PDA, de mind a sync-elés mind az állományok másolgatása megakad, így teljesen használhatatlan. Bluetooth alkalmazása esetén nincs semmi gond egyedül a sebesség hagy kivetnivalót maga után :-( ötletek jöhetnek, ha valakinek van tapasztalata ezen a téren és érti, hogy miről beszélek. Jó lenne az iTunes-al direktben szikronizálni a PDA-ra USB-n keresztül, Bluetooth-al nincs türelmem kivárni míg egy album MP3 átmásolódik.

A hét legnagyobb dilemmája a programozási nyelv választása volt. Mint programozó, az új platform megismerésének és belsővé tételének egyik sarkalatos pontja egy olyan programozási nyelv választása, melynek segítségével fájdalommentesen tudok az adott platformra fejleszteni kisebb alkalmazásoktól kezdve akár nagyobb projekteket is. Természetesen a .NET-es vonalat nem kívánom elhagyni és szerver oldali fejlesztéseknél továbbra is .NET-es irányban tervezek tevékenykedni, de gondolni kell a házi fejlesztésekre is. A .NET nem tűnik túl jó választásnak Mac-es fejlesztések esetére. Természetesen a mono-t portolták OSX-re is és még a MonoDevelopot is lehet futtatni, de a GTK+ nem nyújt valami gyönyörű megjelenést az OSX aqua-s ablakai között. így úgy tűnik, hogy két választásom maradt: Java vagy Objective-C – és Cocoa. A Java a .NET-es múlt és a korábban megszerzett Java ismeretek alapján szimpatikus. A garbage collectort már megszoktam C#-ból, a malloc-ot meg már kezdem elfelejteni gcc-ből, de úgy tűnik, hogy ideje lesz elővennem a Linuxos gcc-ben szerzett tapasztalatokat, mert az Objective-C lesz a nyerő. Bár a szintaktika nagyon idegen egy rövidke ismerkedés után úgy érzem, hogy sikerült ráállnom és – bár még mindig meglehetősen idegen a nyelv, – határozottan kezd megtetszeni. Persze ez nem azt jelenti, hogy akkor a Java vagy valami más szóba sem jöhet, hiszen mindig a cél határozza meg az eszközt és meghatározott feladatokra valószínűleg most is a Java-t választanám. Az Objective-C-re és Cocoa-ra azért esett a választásom, mert úgy tűnik, hogy ezzel a párossal tudok a leginkább natív kinézetű és működésű alkalmazásokat gyártani OSX-re. Ha pedig valaki OSX-re vált akkor az nagyon jól tudja, hogy a natív megjelenés mennyire fontos egy ilyen gyönyörű szép operációs rendszer esetében és nem kíván engedni belőle.

így állok most, de az ismerkedési folyamat még nem ért véget…

2006
13
aug

XML serialization


A .NET keretrendszer egyik általam rendkívül kedvelt szolgáltatása az XML szerializáció. Mostanában az adatszerkezeteket úgy tervezem meg, hogy előtte egy XML file-ban jelenítem meg az elérni kívánt adatstruktúrát, majd ehhez igazítom az adatokat kezelő osztályt. Egy egyszerű példával élve a Pocket Revelation-ből, ahol az XML állomány formátuma eleve adott:

<?xml version="1.0" encoding="utf-8" ?>
<revelationdata version="0.4.7" dataversion="1">
 
	<entry type="folder">
		<name>Folder</name>
		<description>Description of the folder</description>
		<updated>1153820841</updated>
 
		<entry type="folder">
			<name>Subfolder</name>
			<description>Description of the subfolder</description>
			<updated>1153904513</updated>
 
			<entry type="creditcard">
				<name>CreditCard name</name>
				<description>CreditCard description</description>
				<updated>1153820891</updated>
				<field id="creditcard-cardtype">Card type</field>
				<field id="creditcard-cardnumber">Card number</field>
				<field id="creditcard-expirydate">Expirity date</field>
				<field id="creditcard-ccv">CCV number</field>
				<field id="generic-pin">PIN</field>
			</entry>
		</entry>
 
		<entry type="email">
			<name>Email name</name>
			<description>Email description</description>
			<updated>1153821003</updated>
			<field id="generic-email">Email</field>
			<field id="generic-hostname">hostname</field>
			<field id="generic-username">username</field>
			<field id="generic-password">password</field>
		</entry>
 
	</entry>
</revelationdata>

Az XML állomány kezeléséhez kapcsolódó C# osztály kódja:

using System;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;
using System.IO;
using System.Text;
 
namespace PocketRevelation
{
	[XmlRoot ("revelationdata")]
	public class RevelationData
	{
		[XmlElement ("entry", Type = typeof (Entry))]
		public ArrayList  Entries;
		[XmlAttribute ("version")]
		public string     Version;
		[XmlAttribute ("dataversion")]
		public int        DataVersion;
 
		public RevelationData ()
		{
			Entries = new ArrayList ();
		}
 
		public byte[] Serialize ()
		{
			XmlSerializer xmlSerializer = new XmlSerializer (typeof (RevelationData));
			MemoryStream memoryStream = new MemoryStream ();
			xmlSerializer.Serialize (memoryStream, this);
			byte[] serializedXml = memoryStream.GetBuffer ();
			memoryStream.Close ();
			return serializedXml;
		}
 
		public static RevelationData Deserialize (string xmlFileName)
		{
			byte[] buffer = new byte[1024];
			FileStream fileStream;
			try {
				fileStream = File.OpenRead (xmlFileName);
			}
			catch (Exception e) {
				throw new FileNotFoundException(e.Message);
			}
			MemoryStream memoryStream = new MemoryStream ();
			while (true)
			{
				int cbRead = fileStream.Read (buffer, 0, buffer.Length);
				if (cbRead <= 0)
					break;
				memoryStream.Write (buffer, 0, cbRead);
			}
			memoryStream.Position = 0;
 
			XmlSerializer xmlSerializer = new XmlSerializer (typeof (RevelationData));
			RevelationData revelationData = (RevelationData) xmlSerializer.Deserialize (memoryStream);
			memoryStream.Close ();
			fileStream.Close ();
			return revelationData;
		}
	}
 
	public class Entry
	{
		[XmlIgnoreAttribute]
		private static readonly DateTime MinValue = new DateTime(1970, 1, 1);
 
		[XmlElement ("name")]
		public string		Name;
		[XmlElement ("description")]
		public string		Description;
		[XmlIgnoreAttribute]
		public DateTime		Updated;
		[XmlAttribute ("type")]
		public string		Type;
		[XmlElement ("field", Type = typeof (Field))]
		public ArrayList	Fields;
		[XmlElement ("entry", Type = typeof (Entry))]
		public ArrayList	Childs;
 
		[XmlElement ("updated")]
		public long	UpdatedLong {
			get { return (long)(Updated - MinValue).TotalSeconds; }
			set { Updated = MinValue + new TimeSpan (value * TimeSpan.TicksPerSecond); }
		}
 
		public Entry()
		{
			Fields = new ArrayList ();
			Childs = new ArrayList ();
		}
	}
 
	public class Field
	{
	    	[XmlAttribute ("id")]
		public string		Id;
		[XmlTextAttribute]
		public string		Value;
	}
 
	public class RevelationDatabase
	{
		private string            _xmlFileName;
		private RevelationData    _revelationData;
 
		public string filename {
			get { return _xmlFileName; }
			set { _xmlFileName = value; }
		}
 
		public RevelationData revelationData {
			get { return _revelationData; }
		}
 
		public RevelationDatabase (string filename)
		{
			_xmlFileName = filename;
			_revelationData = new RevelationData ();
		}
 
		public void Load ()
		{
			_revelationData = RevelationData.Deserialize (_xmlFileName);
		}
	}
}
2005
19
júl

Mono C# nyomtatás


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.