Hey there,
I make a pretty extensive use of this trick to batch inspect/update ODT files with macros by invoking LibreOffice in headless mode :
However, I ran into a bug which, so far, seems specific to accessing the CurrentSelection property of a document.
Here’s some sample code of a simplified example:
sub CheckText()
_checkText(ThisComponent, "This file")
end sub
sub CheckTextNoHeadless()
_checkText(ThisComponent, "This file")
ThisComponent.close(true)
end sub
sub CheckTextHeadless(file as String)
document = _openSilent(file)
_checkText(document, file)
document.close(true)
end sub
private sub _checkText(document as object, file)
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
frame = document.CurrentController.Frame
parEnum = document.Text.createEnumeration()
par = parEnum.nextElement()
document.CurrentController.select(par.Anchor.Start)
dispatcher.executeDispatch(frame, ".uno:EndOfLineSel", "", 0, Array())
selection = document.getCurrentSelection()
firstLine = selection.getByIndex(0).getString()
msgbox("First line of first paragraph:" & Chr(10) & firstLine)
end sub
' Provides ability to invoke the macro headless from different files
' https://superuser.com/a/1630932/3130029
private function _openSilent(filePath as string) as object
dim fileProperties(1) as new com.sun.star.beans.PropertyValue
fileProperties(0).Name = "Hidden"
fileProperties(0).Value = true
_openSilent = StarDesktop.loadComponentFromURL("file://" & filePath, _
"_blank", 0, fileProperties())
end function
When I invoke CheckText interactively from Writer, it works as expected (e.g. displays the first line of the first paragraph).
From the command line, I invoke it as follows and it also works as expected:
soffice macro:///Standard.Reformat.CheckTextNoHeadless sample_file.odt
FYI, the main reason to have a separate macro is that CheckTextNoHeadless will close the file when it’s done, allowing to loop over multiple files, e.g.:
for f in *.odt; do soffice macro:///Standard.Reformat.CheckTextNoHeadless "$f"; done
The main problem with the above command is that it spawns a new Writer UI for each file, making the computer unusable while the command is running. Hence the headless trick, which, by the way, is also orders of magnitude faster!
Headless command for a unique file:
soffice --headless macro:///Standard.Reformat.CheckTextHeadless\("/absolute/path/to/sample_file.odt"\)
Batch headless command:
for f in /absolute/path/to/*.odt; do soffice --headless macro:///Standard.Reformat.CheckTextHeadless\("$f"\); done
Small tip: In the above command, LibreOffice will fail to load files that contain a comma. Here’s how to work around it (assuming you run it in the same folder):
rename 's/,/WXYZ1234/g' *.odt; for f in /absolute/path/to/*.odt; do soffice --headless macro:///Standard.Reformat.CheckTextHeadless\("$f"\); done; rename 's/WXYZ1234/,/g' *.odt
To be clear, in my experience, invoking macros headless works extremely well. I do it with dozens of macros that perform numerous different actions on thousands of documents. Hence my surprise, since when I invoke this macro headless on the same file(s), I get the following error:
BASIC runtime error.
Object variable not set.
Indeed, when I add a breakpoint, selection is null, as well as the document.CurrentSelection property.
Once again, I’m able to headlessly access and update many different properties of the document and its children objects, as well as dispatch UNO commands. The issue seems specific to the CurrentSelection property 
Should I report a bug?
PS: Sorry for the lengthy post, but I initially quite struggled to find some good information about batch headless macro invocation. So I thought gathering it all in one post might help others in the future!