Search code examples
pythonlibreofficeunolibreoffice-writer

Enumerate FieldMarks in a libreoffice document


Libreoffice represents docx text fields as fieldmarks.

I can create them via the UNO bridge with the following MWE:

# to connect as client`

import uno
from pythonscript import ScriptContext

resolver = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.bridge.UnoUrlResolver')
# start libreoffice as
# libreoffice --writer --accept="socket,host=localhost,port=2002;urp;"`
client = resolver.resolve('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')

XSCRIPTCONTEXT = ScriptContext(client, None, None)
doc = XSCRIPTCONTEXT.getDocument()

def cursor():
    return doc.getCurrentController().getSelection().getByIndex(0)

t = doc.getText()
# insert text before field
t.insertString(cursor(), 'Fieldmark-Start|', False)

# create the field
field = doc.createInstance('com.sun.star.text.Fieldmark')

fieldcursor = cursor()
# add the String 'Field contents' to the document
fieldcursor.setString('Field contents')

# actually insert the field in the document (linked to the string)
field.attach(fieldcursor)
field.setFieldType('Testfieldtype')
field.setName('Fieldname')

# insert text after the field
t.insertString(cursor(), '|Fieldmark-End', False)

After saving, the paragraph is saved correctly as

<text:p text:style-name="Standard">Fieldmark-Start|
    <field:fieldmark-start text:name="Fieldname" field:type="Testfieldtype"/>
        Field contents
    <field:fieldmark-end/>
|Fieldmark-End</text:p>

However, I can't find any present fieldmarks in the document:

# check if the contents are there
doc.getText().getString()
# -> 'Fieldmark-Start|Field contents|Fieldmark-End'

# try to enumerate all fields
tf = doc.getTextFields()
tf.hasElements() # -> true
enum = tf.createEnumeration()
enum.hasMoreElements() # -> false

# field = enum.nextElement()
# -> com.sun.star.container.NoSuchElementException

Solution

  • Even though they don't appear in doc.getTextFields() or doc.getBookmarks(), it is still possible to find them by enumerating the text.

    paragraphs = t.createEnumeration()
    while paragraphs.hasMoreElements():
        text_portions = paragraphs.nextElement().createEnumeration()
        while text_portions.hasMoreElements():
            text_portion = text_portions.nextElement()
            if text_portion.TextPortionType == "TextFieldStart":
                bookmark = text_portion.Bookmark
                bookmark.Name
                bookmark.FieldType
    

    Result:

    'Fieldname'
    'Testfieldtype'
    

    From this point, follow UNO API documents such as https://wiki.openoffice.org/wiki/Documentation/DevGuide/Text/Bookmarks.

    The same question was asked at https://ask.libreoffice.org/en/question/30175/how-access-fieldmarks-with-api/ but does not have a working answer.