a 'mooh' point

clearly an IBM drone

Generation of ODF-files on the .Net-platform

Some time ago I wrote a couple of articles about how to generate ODF-files as well as OOXML-files using .Net technology (both articles are in Danish). For generation of OOXML-files I used the - at that time - new .Net 3.0 System.IO.Packaging assembly and for generation of ODF-files I used AODL - a part of ODF Toolkit.

I thought it was time to refresh my skills - and share them with you guys - since the OOXML/ODF-debate has cooled down to a more relaxing level.

A few weeks back Microsoft released the first production-code edition of their OpenXml SDK - version 1.0. I will dig into this a bit later.

I thought I'd kick this series off with a couple of articles about ODF-file generation on the .Net platform, but I was unpleasantly surprised to realize, that it might not be as easy as it sounded. First, I was told that AODL was a dead project. Surely, the latest addition of code was in April 2007 and it seems that nothing has happened since.  It looks as if the resources of ODF Toolkit is focused on ODFDOM - currently a Java-project. The problem is - AODL seems to be the only .Net-project available. I have stumpled across the ODF .Net project by IndependentSoft, but they sell an ODF library as Closed Source Software ... for (brace yourself) €999 a pop! Seriously - selling CSS-libraries is just sooo 2006 ...

And then I come to you, dear reader ... what the hell do I do? Do you know of other .Net libraries that allow me to create and manipulate ODF-files?

Smile

 

Værktøjer, ODF, AODL

I de sidste par dage har jeg kigget på de tilgængelige værktøjer til generering af ODF-filer - pt. AODL. AODL er en del af ODF Toolkit til generering af ODF-dokumenter og AODL er C#-versionen af dette kit. Kildekoden til dette toolkit kan hentes fra OOs' CVS-repository.

Et ODF-dokument er jo reelt "blot" et ZIP-arkiv indeholdende en række folder og filer og det er et eller andet sted ikke noget, som er relevant for særligt mange. AODL ligger som et objektmæssigt abstraktionslag over dette faktum - og når man anvender biblioteket behøver man ikke at vide dette - altså før det tidspunkt, hvor koden ikke virker. Her er det til gengæld væsentligt at have viden om denne ZIP-fils sttruktur i forbindelse med fejlretning. Ved anvendelse af biblioteket danner man et "regnearks-objekt" (SpreadsheetDocument), hvis man vil lave et regneark, et "teksbehandlingsobjekt" (TextDocument), hvis man vil danne et tekstbehandlingsdokument etc. Man kan tilføje et afsnit (Paragraph) til et tekstbehandlingsdokument og lignende. Abstraktionsmæssigt er implementeringen altså meget tæt på den måde, som man bruger et tekstbehandlingssystem på og det er ganske intuitivt at komme igang med at lave dokumenter.

Et eksempel på dette taget fra AODL-code snippets, der viser dannelse af et tekstbehandlingsdokument:

TextDocument document = new TextDocument();
document.New();
Paragraph paragraph = ParagraphBuilder.CreateStandardTextParagraph(document);
FormatedText formText = new FormatedText(document, "T1", "Some formated text!");
formText.TextStyle.TextProperties.Bold = "bold";
paragraph.TextContent.Add(formText);
document.Content.Add(paragraph);
document.SaveTo("formated.odt");

Ganske tilsvarende er eksempelkoden til generering af et regnearksdokument med en celle med indhold:

SpreadsheetDocument spreadsheetDocument = new SpreadsheetDocument();
spreadsheetDocument.New();
Table table = new Table(spreadsheetDocument, "First", "tablefirst");
Cell cell = table.CreateCell("cell001");
cell.OfficeValueType = "string";
cell.CellStyle.CellProperties.Border = Border.NormalSolid;          
Paragraph paragraph = ParagraphBuilder.CreateSpreadsheetParagraph(spreadsheetDocument);
paragraph.TextContent.Add(new SimpleText(spreadsheetDocument, "Some text"));
cell.Content.Add(paragraph);
table.InsertCellAt(2, 3, cell);
spreadsheetDocument.TableCollection.Add(table);
spreadsheetDocument.SaveTo("F:\tests\simple.ods");

Jeg vil vove den påstand, at selv folk uden dyb programmeringsmæssig baggrund vil kunne give en beskrivelse af, hvad koden gør. Two thumbs up herfra!.

Kodegennemgang:

Selve designet af det begrebsmæssige abstraktionslag for ODF er - sagt igen - rigtigt godt. Jeg kan godt lide at arbejde med objekter med semantisk mening og arkitekturen er sådan relativt gennemført. Der anvendes polymorfi, nedarvning og objekterne er definerede via interfaces de rigtige steder. En løs gennemgang af koden indikerer, at de har lavet det rigtigt godt.

Men mest interessant - uderover objektmodellen - er jo hvordan den bruges. På objektet TextDocument findes en SaveTo()-metode med to overloads. SaveTo() er defineret i interfacet IDocument som både TextDocument() og SpreadsheetDocument() implementerer. Den mest simple af disse overloads tager en "filename"-parameter som argument. Naturligt nok gemmes indholdet af dokumentet i den specificerede fil. Den anden metode tager - udover en filename-parameter - et "IExporter"-objekt. Idéen med dette objekt er, at man kan gemme sit ODF-dokument ved at specificere en konkret exporter. Der medfølger en "PDFExporter" med AODL, og man må antage, at den gemmer ODF-dokumentet som en PDF-fil (jeg har ikke prøvet selv). Idéen er faktisk rigtigt god. Det er dermed muligt at implementere en konkret exporter - til HTML, RTF, OOXML eller hvad man måtte ønske - og så medsende den som argument til SaveTo-kaldet. Men der mangler i mine øjne en overload til SaveTo-metoden. Metoderne jeg kunne ønske mig at have i IDocument-interfacet er:

void SaveTo(System.IO.Stream stream);
void SaveTo(System.IO.Stream stream, AODL.Document.Export.IExporter iExporter);

Rationalet er, at det ikke altid er tilfældet, at man har behov for at persistere et dokument i en fysisk fil på disk. Naturligvis man kan hente den dannede fil fra disk og lave en stream af dette, men det er en lidt omvendt metodik. Skal et dokument dannes via en JIT-tankegang og sendes til fx en browser fra en web-applikation, er det et unødigt niveau af kompleksitet først at skulle danne filen på disk. Her kunne det være lækkert at kunne sende dokumentet til fx en MemoryStream og derefter til klienten. I/O-mæssigt ville dette være det mest optimale. At insistere på at danne en fil undervejs afstedkommer nemlig også yderligere problemer. Selve AODL skal nemlig konfigureres til at gemme filer på bestemte steder og i forbindelse med anvendelse af AODL på web er det ganske sandsynligt at det kommer til at give problemer med skriverettigheder til disk. Men hvis jeg lægger mine "bruger-briller" på hylden og i stedet kigger på AODL med udviklingsøjne, så kunne de næsten ikke implementere det på andre måder. Som nævnt ovenfor er et ODF-dokument reelt en fil- og folderstruktur, der er zippet i en fil. Indholdet af denne zip-fil skal jo være et eller andet sted indtil den dannes, og da der mig bekendt ikke findes metoder til at persistere en folderstruktur i hukommelsen, så har de været nødt til at lave den konkrete implementering.

Konklusion

Helt overordnet set er AODL et behageligt framework at arbejde med. Objektmodellen er intuitiv og let tilgængelig og da den er et OSS-projekt er kildekoden til det også fri. Det er helt klart en fordel og jeg drog netop nytte af dette, da jeg skulle finde ud af, hvordan framework'et var skruet sammen. På den negative side tæller at der i mine øjne mangler nogle muligheder for at gemme til andet end en disk. Det er også frustrerende at dokumentationen er sløset sat sammen - fx er kommentaren ved flere metoder og klasser "Summary of <some class>" - hvilket et default-kommentaren i Visual Studio 2003. Det kunne være rart med en mere gennembearbejdet dokumentation. Hvis man dykker helt ned i koden og eksemplerne herover, vil man lægge mærke til, at der efter instantiering af fx TextDocument() kaldes metoden .New(). Det synes jeg ærligt talt er en smule bekymrende, da et generelt designprincip i OOP er, at et objekt som udgangspunkt skal være klar til anvendelse efter en ctor er kaldet. At kræve at en ekstra metode afvikles er blot at introducere fejl. Fejlhåndtering i klassebiblioteket er også reelt ikke-eksisterende, da fejl blot genkastes, og det giver et overordnet indtryk af, at AODL ikke kvalitetsmæssigt er helt i top.

Min vurdering (af max 5 mulige smileys):

SmileSmileSmile

Værktøjer til dokumentgenerering

I Danmark har bølgerne efterhånden lagt sig efter forårets intensive debatter om valg af dokumentformater i den offentlige sektor i Danmark. Det var jo en debat, som jeg deltog ret kraftigt i, og jeg må ærligt erkende, at jeg er glad for, at der kun er nogle få pip tilbage hist og pist. Vi er naturligvis ikke blevet enige - og roen skyldes sikkert også sommerferien - men det er glædeligt, at tonelejet er kommet ned på et behageligt niveau.

Jeg vil derfor lægge diskussionerne på bla. version2.dk lidt til side - eller i det mindste barbere dem næsten helt ned. Jeg har fundet ud af, at jeg kan udøve indflydelse bedre på andre måder end diskussionsfora, så jeg vil i stedet koncentrere mig om noget, der ligger mig tættere på hjertet - nemlig programmering. I de næste uger vil jeg kigge på, hvilke værktøjer der findes til automatisk dokumentgenerering af ODF-dokumenter samt OOXML-dokumenter. Jeg vil sandsynligvis primært kigge på tekstbehandlingsdokumenter samt regneark. Platformen jeg vil anvende er .Net ( C# ) til begge formål.

Som det ser ud lige nu, så vil jeg kigge på mulighederne for dokumentgenerering via de nyeste værktøjer. Det vil konkret sige værktøjerne til OOXML fra Microsoft til .Net 3.0 . Til generering af ODF-dokumenter har jeg fundet pakker som AODL, og disse bliver udgangspunktet for arbejdet med ODF-generering. Jeg har endnu ikke gravet værktøjerne frem til dokumentgenerering til OOXML, men jeg tror, at et godt udgangspunkt vil være openxmldeveloper.org .

Generelt: hvis du kender til værktøjer til C#, der kan generere ODF-dokumenter, så sig endelig til. Jeg får også brug for noget C#-kode, der kan danne MathML til brug i mine ODF-dokumenter, så sådanne biblioteker skal jeg også have fundet.