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 |
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 |
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 |
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.