Creating a Numbered List Style or a Numbered Paragraph Style with UNO

I’m trying to create a new numbering style of com.sun.star.style.NumberingType.TEXT_CARDINAL (i.e. One, Two, Three…) using UNO. My attempts so far have been unsuccessful, and I’ve searched high and low for an example with no joy.

I tried the following function (preceded by test calls), which works fine when called to create a paragraph style. But it fails when called to create a numbering style, throwing a com.sun.star.lang.WrappedTargetException on the line xMPS.setPropertyValues(prop_names, prop_values);. Also, part of the trace spewed by my IDE along with the exception was the line “at com.sun.proxy.$Proxy11.setPropertyValues(Unknown Source)”.

createStyle("Paragraph Centered", "ParagraphStyle", "ParagraphStyles", new String[]{"ParaAdjust"}, new Object[]{com.sun.star.style.ParagraphAdjust.CENTER});
createStyle("Numbering Cardinal", "NumberingStyle", "NumberingStyles", new String[]{"NumberingType"}, new Object[]{com.sun.star.style.NumberingType.TEXT_CARDINAL});

private void createStyle (String new_style_name, String base_style_name, String style_family_name, String[] prop_names, Object[] prop_values) throws Exception {
    // External functions provide xStyle and xFamily with no problems
    XStyle xStyle = getXStyle(base_style_name);
    XNameContainer xFamily = getStyleFamily (style_family_name);

    // Unless it already exists, customize and then add style to LO
    if (xFamily.hasByName(new_style_name) == false) {
        if (prop_names.length > 0) {
            XMultiPropertySet xMPS = (XMultiPropertySet)UnoRuntime.queryInterface(XMultiPropertySet.class, xStyle);
            xMPS.setPropertyValues(prop_names, prop_values);
        }
        
        xFamily.insertByName (new_style_name, xStyle);
    }
}

I’ve also tried creating a paragraph style with numbering added to it using the following function (again, preceded by a test call). It fails with a throw of com.sun.star.beans.UnknownPropertyException on the line xPropertySet.setPropertyValue(“NumberingRules”, xNum);. Also, similar to before, the IDE’s trace along with the exception included “at com.sun.proxy.$Proxy13.setPropertyValue(Unknown Source)”.

createNumParaStyle("Numbering Cardinal", "ParagraphStyle", "ParagraphStyles");private

void createNumParaStyle (String new_style_name, String base_style_name, String style_family_name) throws Exception {
    // External functions provide xStyle and xFamily with no problems
    XStyle xParaStyle = getXStyle(base_style_name);
    XNameContainer xFamily = getStyleFamily (style_family_name);
    
    if (xFamily.hasByName(new_style_name) == false) {
        XIndexAccess xNum = (XIndexAccess) UnoRuntime.queryInterface( XIndexAccess.class, msFactory.createInstance("com.sun.star.text.NumberingRules"));
        XIndexReplace xReplace = (XIndexReplace) UnoRuntime.queryInterface(XIndexReplace.class, xNum);
        
        PropertyValue[] aProps = (PropertyValue []) xNum.getByIndex(0);
        for (int j = 0 ; j < aProps.length ; ++j) {
            if (aProps[j].Name.equals ("NumberingType")) {
                aProps[j].Value = Short.valueOf(com.sun.star.style.NumberingType.TEXT_CARDINAL);
                continue;
            }
        }
        xReplace.replaceByIndex (0,aProps);
        
        XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xParaStyle);
        xPropertySet.setPropertyValue("NumberingRules", xNum);
        xPropertySet.setPropertyValue("NumberingLevel", (short)0);
                    
        xFamily.insertByName (new_style_name, xParaStyle);
    }
}

It’s pretty clear I’m missing something related to setPropertyValue in each case. In the latter case, the exception seems to suggest (at least to me) that I might need to get a different interface than XPropertySet to provide access to the paragraph styles properties. But I don’t know what that interface would be or where to get it from.

Any suggestions as to what I might be missing or doing wrong?

1 Like

You might find [ SwVbaListFormat::ConvertNumbersToText](vbalistformat.cxx (revision 286a7f3e) - OpenGrok cross reference for /core/sw/source/ui/vba/vbalistformat.cxx), which does a lot of UNO stuff on lists (to do an opposite - convert lists to plain text) a useful code pointer for your task.

Thanks for the suggestion, @mikekaganski. It seems, however, that SwVbaListFormat::ConvertNumbersToText is operating on paragraphs in an existing document rather than on either paragraph or list styles. From IDE error info, I did manage to find on https://opengrok.libreoffice.org/ the point in LO where the exception is thrown when calling createNumParaStyle: line 2076 of /xref/core/sw/source/core/unocore/unostyle.cxx; I’ll have to do some more digging around that point and see if I can get a clue.

The same python code doesn’t work, do you know why?

    temp = numbering_rules.getByIndex(0)
    for item in temp:
        if item.Name == "ListFormat":
            item.Value = "%1%"
>   numbering_rules.replaceByIndex(0, temp)
E   uno.com.sun.star.lang.IllegalArgumentException

tests\test_writer_style_elements\test_heading.py:85: com.sun.star.lang.IllegalArgumentException

Find solution:

numbering_rules[0] = temp

Here is some Basic code that I got to work based on [Solved] [Basic] How to program a NumberingStyle? (View topic) • Apache OpenOffice Community Forum. I think the idea is to define all attributes rather than trying to pick out only the one you want to change. It seems significantly different from how other styles are created such as paragraph or character or frame styles.

doc = ThisComponent
oNumRules = ThisComponent.StyleFamilies.getByName("NumberingStyles").getByName("Numbering abc").NumberingRules
oNumRule0 = oNumRules.getByIndex(0)

dim arrProps(11) as new com.sun.star.beans.PropertyValue  'Why is this 11 instead of 12? I don't understand Basic apparently.
arrProps(0).Name="Adjust": arrProps(0).Value=2
arrProps(1).Name="ParentNumbering": arrProps(1).Value=1
arrProps(2).Name="Prefix": arrProps(2).Value=""
arrProps(3).Name="Suffix": arrProps(3).Value=""
arrProps(4).Name="CharStyleName": arrProps(4).Value=""
arrProps(5).Name="StartWith": arrProps(5).Value=1
arrProps(6).Name="PositionAndSpaceMode": arrProps(6).Value=1
arrProps(7).Name="LabelFollowedBy": arrProps(7).Value=0
arrProps(8).Name="ListtabStopPosition": arrProps(8).Value=1270
arrProps(9).Name="FirstLineIndent": arrProps(9).Value=-681
arrProps(10).Name="IndentAt": arrProps(10).Value=400
arrProps(11).Name="NumberingType": arrProps(11).Value=4
oNumRules.replaceByIndex(0, arrProps)

oNumStyle = doc.createInstance("com.sun.star.style.NumberingStyle")
oNumStyle.NumberingRules = oNumRules

styleObjs = doc.getStyleFamilies().getByName("NumberingStyles")
styleObjs.insertByName("MyNewNumberingStyle", oNumStyle)

After running this code, I can see the result by modifying a paragraph style and, under the tab “Outline & Numbering,” my new numbering style can now be selected.

The following line caused a crash for me when trying to insert the style into the document, so maybe it’s better to start with an existing style. Or maybe there’s a bug somewhere in my code.

oNumRules = doc.createInstance("com.sun.star.text.NumberingRules")

Note: I read Line Numbering and Outline Numbering - Apache OpenOffice Wiki but didn’t get a working result.

1 Like

Thanks, @jimk! From your example, I think I see now!

As I understand it, the key is to get a NumberingRules object from an existing numbering style, change the properties in that one way or another, and then replace the NumberingRules object of that style with the changed object. Or, as in your example, create a new numbering style, replacing its NumberingRules object with the changed object.

So, after a rewrite, it works! Below is some heavily commented working code to make a numbering style Widget One:, Widget Two:, Widget Three:.... Hopefully the comments make sense.

I think you’re right that all of the attributes (i.e. properties) need to be defined. Fortunately, the Java UNO implementation provides default values, at least when getting the attribute array from the NumberingRules object of an existing style. In that case, it’s only necessary to change the ones of interest. Unfortunately, it’s necessary to iterate through the array to find those to change since no direct access to the attributes seems to be provided.

customNumStyle ("Numbering Cardinal");

private void customNumStyle (String new_style_name) throws Exception {
    String style_family_name = "NumberingStyles";
    
    // Create new numbering style with a default format
    // (getXStyle & getStyleFamily are just custom utility functions to make getting this stuff less verbose...)
    XStyle xStyle = getXStyle("NumberingStyle");
    XNameContainer xFamily = getStyleFamily (style_family_name);
    if (xFamily.hasByName(new_style_name) == false)
        xFamily.insertByName (new_style_name, xStyle);
    
    // Get a NumberingRules object from the numbering style
    // (getNamedStyle is another custom utility function...)
    Object oNumStyle = getNamedStyle(new_style_name, style_family_name);
    XPropertySet xPropertySet = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, oNumStyle);
    Object oNumRules = xPropertySet.getPropertyValue("NumberingRules");
    
    // Get XIndexReplace from the NumberingRules object...
    // (https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1style_1_1ParagraphProperties.html#aed1885e6b2631c15f6ac8c832473c1e2)
    // Provides facility to replace the numbering level array at a given index with a given array.
    // (See the NumberingExample method in file:///usr/lib64/libreoffice/sdk/examples/DevelopersGuide/Text/TextDocuments.java)
    XIndexReplace xReplace = (XIndexReplace) UnoRuntime.queryInterface(XIndexReplace.class, oNumRules);
    
    // Get XIndexAccess from XIndexReplace object...
    // (https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1container_1_1XIndexReplace.html)
    // Provides access to the set of properties for different numbering levels.
    // (See the NumberingExample method in file:///usr/lib64/libreoffice/sdk/examples/DevelopersGuide/Text/TextDocuments.java)
    XIndexAccess xAccess = (XIndexAccess) UnoRuntime.queryInterface(XIndexAccess.class, xReplace);
    
    ///////
    // Iterate through the NumberingRules properties, changing those of interest.
    // (As suggested by the NumberingExample method in file:///usr/lib64/libreoffice/sdk/examples/DevelopersGuide/Text/TextDocuments.java)
    //
    // Get the array of property values for numbering level 0...
    // In this way, all elements in the array are defined from the start.
    //   // Property values (i.e. attributes) available for a given numbering level are listed at
    //   // https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1text_1_1NumberingLevel.html
    PropertyValue[] aProps = (PropertyValue []) xAccess.getByIndex(0);
    //
    for (int j = 0 ; j < aProps.length ; ++j) {
        if (aProps[j].Name.equals ("NumberingType")) {
            aProps[j].Value = Short.valueOf(com.sun.star.style.NumberingType.TEXT_CARDINAL);
            continue;
        }
        if (aProps[j].Name.equals ("Prefix")) {
            aProps[j].Value = "Widget ";
            continue;
        }
        if (aProps[j].Name.equals ("Suffix")) {
            aProps[j].Value = ":";
            continue;
        }
        if (aProps[j].Name.equals ("PositionAndSpaceMode")) {
            aProps[j].Value = com.sun.star.text.PositionAndSpaceMode.LABEL_ALIGNMENT;
            continue;
        }
        if (aProps[j].Name.equals ("LabelFollowedBy")) {
            aProps[j].Value = com.sun.star.text.LabelFollow.LISTTAB;
            continue;
        }
    }
    //
    // Replace the array of property values with the array containing the desired changes.
    xReplace.replaceByIndex (0,aProps);
    ///////
    
    // Finally, replace the style's NumberingRules object with the changed NumberingRules object
    xPropertySet.setPropertyValue("NumberingRules", oNumRules);		
}

Incidentally, about the BASIC array size… It doesn’t make sense to me either, but 11’s correct. See Arrays - Apache OpenOffice Wiki.

Glad to see you found a way that does not require every property to be explicitly initialized.