Using OpenOffice.org Basic macros to publish documents on the web

Convert and Publish


Coding your own document formatting solution might sound like a daunting proposition, but it requires only a couple of relatively simple macros.

By Dmitri Popov

When it comes to publishing your OpenOffice.org Writer documents on the web, you have several options. If you use MediaWiki as your web publishing platform, you can use the Sun Wiki Publisher to convert documents into wiki pages. OpenOffice also lets you save pages in HTML format for use with content management systems that support HTML formatting (the HTML output produced by OpenOffice is far from perfect, though). But what if you are using a publishing platform that uses its own markup? In this case, you might want to consider creating a DIY OpenOffice Basic solution.

To begin, start with a macro that formats the Writer document with a specific markup. For this exercise, I'll use DokuWiki syntax, but you can adapt the final macro to other markup systems. For simplicity, I focus on the core formatting options: headings (from Heading 1 to Heading 5), bold, italic, and underlined, as well as hyperlinks. The way the macro works is simple: It finds all occurrences of formatted text and replaces them with appropriate DokuWiki markup: text in bold becomes  text in bold , text in italic becomes //text in italic//, and so on. Because the macro performs a number of similar find and replace operations, it is a perfect candidate for functions. In this case, I need three functions for converting headings, URLs, and formatted text. The function that does the latter is shown in Listing 1.

Listing 1: Converting Formatted Text
01 Function MarkupTextFunc (SearchAttrName, SearchAttrValue, ReplaceStr)
02 Dim SearchAttributes(0) As New com.sun.star.beans.PropertyValue
03 ThisDoc=ThisComponent
04 SearchAttributes(0).Name=SearchAttrName
05 SearchAttributes(0).Value=SearchAttrValue
06 ReplaceObj=ThisDoc.createReplaceDescriptor
07 ReplaceObj.SearchRegularExpression=true
08 ReplaceObj.searchStyles=false
09 ReplaceObj.searchAll=true
10 ReplaceObj.SetSearchAttributes(SearchAttributes)
11 ReplaceObj.SearchString=".*"
12 ReplaceObj.ReplaceString=ReplaceStr
13 ThisDoc.replaceAll(ReplaceObj)
14 End Function

The function requires three parameters: SearchAttrName, SearchAttrValue, and ReplaceStr. The SearchAttrName and SearchAttrValue variables find specific formatting in the document. For example, to find text fragments in bold, you should assign the CharWeight value to the SearchAttrName variable and the com.sun.star.awt.FontWeight.BOLD value to the SearchAttrValue variable. The ReplaceStr variable specifies the appropriate formatting tags wrapped around the strings found. In DokuWiki, the   tag is used to mark bold text, so in this case, the required value is  & . Note that the function also uses the statement ReplaceObj.SearchRegularExpression=true to enable the regular expressions in the search, and it uses the ".*" regular expression to perform the specified search in the document text.

To format headings, I need another function. Headings in Writer documents are usually formatted by paragraph styles, so each heading can be treated as a paragraph. This way, the function can do its job in three steps. First, it identifies paragraphs in the document as objects. Second, for each paragraph object, the function locates the portion formatted with a heading paragraph style, and third, wraps the text portion into the specified tags (Listing 2). This function also requires three parameters: StyleName, StartTag, and EndTag. StyleName specifies the paragraph style. In this case, the values that can be assigned to the variable are Heading 1, Heading 2, Heading 3, and so on. Finally, I need one more function to format hyperlinks in the document (Listing 3). As you can see, this function is almost identical to the one in Listing 2. The only difference is that it looks for hyperlinks instead of heading formatting. Also note that the function doesn't require any arguments.

Listing 2: Converting Headings
01 Function MarkupHeadingsFunc (StyleName, StartTag, EndTag)
02 ThisDoc=ThisComponent
03 ThisText=ThisDoc.Text
04 ParaEnum=ThisText.createEnumeration
05 While ParaEnum.hasmoreElements
06  Para=ParaEnum.nextElement
07   PortionEnum=Para.createEnumeration
08    While PortionEnum.hasMoreElements
09     Portion=PortionEnum.nextElement
10      If Portion.paraStyleName = StyleName Then
11        Portion.String = StartTag + Portion.String + EndTag
12       End If
13    Wend
14 Wend
15 End Function
Listing 3: Converting Hyperlinks
01 Function MarkupURLFunc()
02 ThisDoc=ThisComponent
03 ThisText=ThisDoc.Text
04 ParaEnum=ThisText.createEnumeration
05 While ParaEnum.hasmoreElements
06  Para=ParaEnum.nextElement
07   PortionEnum=Para.createEnumeration
08    While PortionEnum.hasMoreElements
09     Portion=PortionEnum.nextElement
10      If Portion.HyperlinkURL <> "" Then
11        Portion.String = "[[" + Portion.HyperlinkURL +"|" +Portion.String + "]]"
12       End Jf
13    Wend
14 Wend
15 End Function

With all functions in place, you can work on the macro. This part is probably the easiest. All you have to do is call the functions and provide them with the appropriate parameters. For example, the statement that converts headings formatted with the Heading 1 style should be:

MarkupHeadingsFunc("Heading 1", "====== ", " ======")

and the statement to convert text fragments in bold should be:

MarkupTextFunc("CharWeight", com.sun.star.awt.FontWeight.BOLD, "**&**")

So the entire macro that processes and converts the current Writer document looks like Listing 4.

Listing 4: Converting the Writer Document
01 Sub Markup
02 ThisDoc=ThisComponent
03 MarkupHeadingsFunc("Heading 1", "====== ", " ======")
04 MarkupHeadingsFunc("Heading 2", "===== ", " =====")
05 MarkupHeadingsFunc("Heading 3", "==== ", " ====")
06 MarkupHeadingsFunc("Heading 4", "=== ", " ===")
07 MarkupHeadingsFunc("Heading 5", "== ", " ==")
08 MarkupTextFunc("CharWeight", com.sun.star.awt.FontWeight.BOLD, "**&**")
09 MarkupTextFunc("CharPosture", com.sun.star.awt.FontSlant.ITALIC, "//&//")
10 MarkupTextFunc("CharUnderline", com.sun.star.awt.FontUnderline.SINGLE, "__&__")
11 MarkupURLFunc
12 End Sub

Figure 1: To open the macro editor, click on Tools | Macros | Organize Macros | OpenOffice.org Basic, then choose the sample macro (Module1 in this case), and click Edit.

Improving the Macro

Although this simple macro does the job, you can improve it in a number of ways. For example, you might want to tweak it so that it saves the converted document as a plain text file. To do this, you need to make several modifications to the original macro. To begin, you have to define a new variable at the beginning of the macro:

Dim Args(0) As New com.sun.star.beans.PropertyValue

Also, you need to add a code block to check that the document has been saved (i.e., it has a location on the hard disk). This can be done in just three lines of code:

If ThisDoc.hasLocation=False Then
 MsgBox ("You have to save the document first!", 64, "Attention") :End
End If

Next, the macro has to initiate the built-in Tools library with the following steps:

If (Not GlobalScope.BasicLibraries.isLibraryLoaded("Tools")) Then
 GlobalScope.BasicLibraries.LoadLibrary("Tools")
End If

The macro then uses the DirectoryNameOutOfPath routine from the Tools library to obtain the directory where the current document is stored:

DocURL=ThisDoc.getURL()
DocDir=DirectoryNameOutOfPath(DocURL, "/")

Additionally, the macro has to pull the name of the document from the document's path:

FileName=Left(Dir(DocURL, 0), Len(Dir(DocURL, 0))-4)

Finally, the macro uses the obtained information to save the document as a plaintext file at the same location:

Args(0).Name="FilterName"
Args(0).Value="Text"
TextFilePath=ConvertToURL(DocDir & "/" & FileName & ".txt")
ThisDoc.StoreToURL(TextFilePath, Args())

That's it. Listing 5 is the entire macro.

Listing 5: The Markup Macro
01 Sub Markup
02 Dim Args(0) As New com.sun.star.beans.PropertyValue
03 ThisDoc=ThisComponent
04
05   If ThisDoc.hasLocation=False Then
06   MsgBox ("You have to save the document first!", 64, "Attention") :End
07 End If
08
09 MarkupHeadingsFunc("Heading 1", "====== ", " ======")
10 MarkupHeadingsFunc("Heading 2", "===== ", " =====")
11 MarkupHeadingsFunc("Heading 3", "==== ", " ====")
12 MarkupHeadingsFunc("Heading 4", "=== ", " ===")
13 MarkupHeadingsFunc("Heading 5", "== ", " ==")
14 MarkupTextFunc("CharWeight", com.sun.star.awt.FontWeight.BOLD, "**&**")
15 MarkupTextFunc("CharPosture", com.sun.star.awt.FontSlant.ITALIC, "//&//")
16 MarkupTextFunc("CharUnderline", com.sun.star.awt.FontUnderline.SINGLE, "__&__")
17 MarkupURLFunc
18
19 If (Not GlobalScope.BasicLibraries.isLibraryLoaded( "Tools")) Then
20   GlobalScope.BasicLibraries.LoadLibrary("Tools")
21 End If
22
23 DocURL=ThisDoc.getURL()
24 DocDir=DirectoryNameOutOfPath(DocURL, "/")
25 FileName=Left(Dir(DocURL, 0), Len(Dir(DocURL, 0))-4)
26 Args(0).Name="FilterName"
27 Args(0).Value="Text"
28 TextFilePath=ConvertToURL(DocDir & "/" & FileName & ".txt")
29 ThisDoc.StoreToURL(TextFilePath, Args())
30 End Sub

Adding FTP Upload

The great thing about DokuWiki is that it stores all pages as plaintext .txt files. This means that you can upload converted documents without any additional tweaking. Of course, you can do it manually with an FTP client, or you can write another macro that would do this for you. In this way, you can convert, save, and upload any Writer document in one fell swoop. The best part is that writing a macro that does the upload is rather easy because you can reuse most of the code from the conversion macro, as you can see in the FTPUpload macro in Listing 6.

Listing 6: The FTPUpload Macro
01 Sub FTPUpload
02 Dim Args(0) As New com.sun.star.beans.PropertyValue
03
04 ThisDoc=ThisComponent
05 If (Not GlobalScope.BasicLibraries.isLibraryLoaded("Tools")) Then
06 GlobalScope.BasicLibraries.LoadLibrary("Tools")
07 End If
08
09 DocURL=ThisDoc.getURL()
10 DocDir=DirectoryNameOutOfPath(DocURL, "/")
11 FileName=Left(Dir(DocURL, 0), Len(Dir(DocURL, 0))-4)
12
13 FTPPath="ftp://username:password@ftp.server.com/"
14 SaveFTP = FTPPath & FileName & ".txt"
15 Args(0).Name="FilterName"
16 Args(0).Value="Text"
17 ThisDoc.storeToURL(SaveFTP, Args())
18 End Sub

The only difference here is the FTPPath="ftp://username:password@ftp.server.com/" statement that specifies the FTP connection string. Obviously, you have to replace the placeholders with an actual username, password, and FTP address for this macro to work properly. If you prefer not to hard-wire the connection string into the macro, you can replace it with an input box that prompts the user to enter the connection string:

FTPPath=InputBox("FTP Address", "Input required", "ftp://user:password@192.168.1.7/pub/")

Listing 7 shows how to save the connection string in a text file and tweak the macro to read the string (replace path/to/connectftp.txt with the filepath): Then, modify the Markup macro to ask the user whether to upload the converted file:

Answer=MsgBox("Upload file?",36, "Upload")
If Answer=6 Then
FTPUpload
End If

Pressing the Yes button triggers the FTPUpload macro.

Listing 7: Reading a Connection String from a File
01 FTPPath=ConvertToURL ("path/to/connectftp.txt")
02 f1=FreeFile()
03 Open FTPPath For Input As #f1
04 Do While not Eof(f1)
05 Line Input #f1, FTPString
06 SaveFTP = FTPString & FileName & ".txt"
07 Args(0).Name="FilterName"
08 Args(0).Value="Text"
09 ThisDoc.storeToURL(SaveFTP, Args())
10 Loop

Final Word

With some tweaking, this macro can work with any markup, for example. The described macros provide basic functionality, so you might want to add support for lists and tables.