Search code examples
libreoffice-writer

Is there a way to call self-defined macro functions from within writer document?


I'd like to call sub and function from libre office basic from document text, such as

[call sub initNumbering()]
text - blah
Some caption defined as "Card Nr."+[call function getCardNr()]
text - blah
[call sub nextCard()]

But I didn't find a way to accomplish calling those functions in such a way.

I tried in libre writer to use menu "Insert-Field-more fields", and there tab "functions" - it lists "execute macro", and I even can navigate to the self written macro, but after "insert" it only shows a thin grey placeholder instead of the contents the macro function should generate to be displayed as part of the text.

Maybe some small "hello world" example - first the macro definition:

REM  *****  BASIC  *****

Function getName() as String
    getName = "Hello world!"
End Function

then the ODT using it, after inserting the function by menu "Insert-Field-more fields", tab "functions", "execute macro":

Generated text: „vnd.sun.star.script:Standard.Test.getName?language=Basic&location=document“

I'd expect to have "Hello world" inserted between the quotes, but it shows only the link of the inserted execute-macro-field, not its result.


Solution

  • From https://help.libreoffice.org/latest/en-US/text/swriter/01/04090003.html:

    Execute Macro Inserts a text field that runs a macro when you click the field in the document. To assign a macro to the field, click the Macro button.

    For example, create a field with reference text "Run GetName" assigned to the following subroutine:

    Function GetName as String
        GetName = "Hello world!"
    End Function
    
    Sub ReplaceField
        Dim oDoc As Object
        Dim oField As Object
        oDoc = ThisComponent
        For Each oField In oDoc.TextFields
            If oField.supportsService("com.sun.star.text.TextField.Macro") Then
                If oField.getPresentation(False) = "Run GetName" Then
                    oField.Hint = GetName()
                End If
            End If
        Next oField
    End Sub
    

    insert field to execute macro

    Click on the field, which will change its text to "Hello world!" instead of "Run GetName".

    You could change the code to go to oField.getAnchor(), delete the field and replace it with whatever text content should go there.

    Now, the initial part of your question is:

    [call sub initNumbering()]
    text - blah
    Some caption defined as "Card Nr."+[call function getCardNr()]
    text - blah
    [call sub nextCard()]
    

    To me, this looks like templating. So for example, instead of [call sub initNumbering()], write {{initNumbering}} in that place. When all of your template fields are ready, run a macro that replaces all template fields with the content that should go in those fields.

    Writer also has a templates feature that does not require macros, so maybe that can do what you need. Or maybe a mail merge, with data for each card read from a database and inserted into the appropriate fields in the document.