Wednesday, May 27, 2009

Holy grail found

Every now and then, a 'programming holy grail' emerges: this is normally something that I very much want to learn how to do in a program, but can't find out how to do it. Such a holy grail has to be something incidental - otherwise the program's development will be stymied and never completed. The latest holy grail is something which has been bothering me for some time - how to print from a Delphi program bold text within a Word document.

Creating a Word document and adding contents to it is called automation; whilst there is a fair amount of information about this on the internet, it tends to be basic, such as how to create the document, add some text and then close it. The finer points of formatting tend not to be discussed in internet articles. As it happens, I bought several months ago an excellent book on Office Automation, "Microsoft Office Automation with Visual FoxPro"; the only problem with this book being that it's written for Visual FoxPro (duh!) whereas I'm using Delphi, and the syntax is different. It's not too different, but sometimes sufficiently so and not clear enough which object has which properties and methods.

To create a Word document, and some text and then close it is done like this in Delphi:
var
 wrdApp, wrdDoc: variant;

begin
 wrdApp:= CreateOleObject ('Word.Application');
 wrdApp.visible:= false;
 wrdDoc:= wrdApp.documents.add;
 wrdDoc.select;

 wrdApp.selection.typetext ('Wow - I''m adding text to a Word document from  Delphi');
 wrdApp.selection.typeparagraph;

 wrdApp.visible:= true;
 wrdDoc:= unassigned;
 wrdApp:= unassigned;
end;
All calls to the wrdApp object are slow, and so I've found all sorts of ways to improve the duo of typetext and typeparagraph; the latter call is unnecessary, as appending a carriage return (character 13) to the string being printed is enough to force the cursor to a new line. When printing blocks of text, I now add the text to a wide string variable, and only pass that variable to the typetext method when all the text has been added.

So how does one add bold text? As it turns out, it's very simple, although this method negates the above optimisation. First of all, I define a new variant, wrdSel:
wrdSel:= wrdApp.selection;
This by itself simplifies the typing of the original code, for now I can replace wrdApp.selection with wrdSel, thus saving 10 characters each time. This may not seem important, but it does save wear and tear of the fingers. The 'selection' object has a 'font' record which holds a 'bold' property, and in order to print bold, all one has to do is turn this property on (and afterwards turn it off). So the simple code now becomes
var
 wrdApp, wrdDoc, wrdSel: variant;

begin
 wrdApp:= CreateOleObject ('Word.Application');
 wrdApp.visible:= false;
 wrdDoc:= wrdApp.documents.add;
 wrdDoc.select;
 wrdSel:= wrdApp.selection;

 wrdSel.typetext ('Wow - I''m adding ');
 wrdSel.font.bold:= integer (true);
 wrdSel.typetext ('bold');
 wrdSel.font.bold:= integer (false);
 wrdSel.typetext (' text to a Word document from Delphi' + #13);

 wrdApp.visible:= true;
 wrdSel:= unassigned;
 wrdDoc:= unassigned;
 wrdApp:= unassigned;
end;
Fortunately, I doubt whether I'll be using bold text very much in the body of a print-out; it's more likely that a paragraph heading will be in bold and the paragraph body in regular text. In this case, all the body can be loaded into one string and then printed.

I don't know how long I've been searching for this holy grail, but I'm pleased now that I've found it. I imagine that soon there'll be a new grail to search for.

No comments: