Chapter 6. Text Styles

This chapter focuses on how text documents styles can be examined and manipulated. This revolves around the XStyleFamiliesSupplier interface in GenericTextDocument, which is highlighted in Fig. 36 (a repeat of Fig. 26 in Chapter 5. Text API Overview).

Diagram of The Text Document Services, and some Interfaces

Fig. 36 :The Text Document Services, and some Interfaces.

XStyleFamiliesSupplier has a getStyleFamilies() method for returning text style families. All these families are stored in an XNameAccess object, as depicted in Fig. 36.

XNameAccess is one of Office’s collection types, and employed when the objects in a collection have names. There’s also an XIndexAccess for collections in index order.

XNameContainer and XIndexContainer add the ability to insert and remove objects from a collection.

Diagram of Style Families and their Property Sets

Fig. 37 :Style Families and their Property Sets.

Five style family names are used by text documents: CharacterStyles, FrameStyles, NumberingStyles, PageStyles, and ParagraphStyles. The XNameAccess families collection can be accessed with one of those names, and returns a style family. A style family is a modifiable collection of PropertySet objects, stored in an XNameContainer object.

Fig. 37 shows that if the ParagraphStyles family is retrieved, it contains property sets labeled “Header”, “List”, “Standard”, and quite a few more. Each property set can format a paragraph, change the text’s font, size, and many other attributes. These property sets are called styles.

The CharacterStyles family is a container of property sets (styles) which affect selected sentences, words, or characters in the document. The FrameStyles container holds property sets (styles) for formatting graphic and text frames. The NumberingStyles family is for adding numbers or bullets to paragraphs. The PageStyles family is for formatting pages.

The names of the property sets (styles) in the style families can be listed using LibreOffice’s GUI. If you create a new text document in Writer, a “Styles and Formatting” dialog window appears when you press F11 (or click on the brown spanner icon in the “Formatting” toolbar). Within the window you can switch between five icons representing the five style families. Fig. 38 shows the list of property set (style) names for the paragraph styles family. They corresponds to the property set names shown in Fig. 37.

Screen shot of Styles and Formatting Window in Writer

Fig. 38 :Styles and Formatting Window in Writer.

The names listed in the window are the same as the names used in the API, except in two cases: the “Default Paragraph Style” name that appears in the GUI window for “Paragraph Styles” and “Page Styles” is changed to “Standard” in the API. Strangely, the “Default Style” name for “Character Styles” in the GUI is called “Default Style” in the API.

Accessing a style (a property set) is a three-step process, shown below. First the style families, then the style family (ex: “ParagraphStyle”), and then the style (ex: “Standard”):

# 1. get the style families
xsupplier = Lo.qi(XStyleFamiliesSupplier, doc)
name_acc = xsupplier.getStyleFamilies()

# 2. get the paragraph style family
para_style_con = Lo.qi(XNameContainer, name_acc.getByName("ParagraphStyles"))

# 3. get the 'standard' style (property set)
standard_props = Lo.qi(XPropertySet, para_style_con.getByName("Standard"))

The code that implements this process in the Write utility class is a bit more complicated since the calls to getByName() may raise exceptions if their string arguments are incorrect.

The calls to Lo.qi() cast the object returned from a collection into the correct type.

6.1 What Properties are in a PropertySet?

The “Standard” name in the “ParagraphStyles” style family refers to a property set (style). Each set is a collection of name=value pairs, and there are get and set methods using a name to get/set its value. This is simple enough, but what names should the programmer use? Each property set (style) in the same style family contain the same properties, but with different values. For instance, in Fig. 37 the “Header”, “Title”, “Standard”, “List”, and “Table” sets contain the same named properties.

The names of the properties used by the sets in a style family can be found in the documentation for their XXXStyle service. Table 4 summarizes the mapping.

Table 4 Properties Information for Each Style Family.

Style Family Name

Service where Properties are Defined

CharacterStyles

CharacterStyle

FrameStyles

FrameStyle (??)

NumberingStyles

NumberingStyle

PageStyles

PageStyle

ParagraphStyles

ParagraphStyle

The easiest way of finding Office documentation for the services in the second column of Table 4 is with lodoc. For example, the page about “CharacterStyle” can be found with lodoc CharacterStyle service.

The FrameStyle service (full name: com.sun.star.style.FrameStyle) has a “??” against it since there’s no online documentation for that service, although such a service exists.

A style’s properties are usually defined across several classes in an inheritance hierarchy. The hierarchies for the five styles are summarized in Fig. 39.

Diagram of The Inheritance Hierarchies for the Style Services.

Fig. 39 :The Inheritance Hierarchies for the Style Services.

Fig. 39 shows the hierarchies for the five style services: CharacterStyle, FrameStyle, NumberingStyle, PageStyle, and ParagraphStyle. There’s clearly a lot of similarities between them, so we are focused on CharacterStyle.

There are three services containing character style properties: CharacterStyle, Style, and CharacterProperties. If you visit the online documentation for CharacterStyle, the properties are listed under the heading “Public Attributes”, which is shown in Fig. 40.

Screen shot of Styles and Formatting Window in Writer

Fig. 40 :Part of the Online Documentation for CharacterStyle.

CharacterStyle defines six properties itself, but there are many more inherited from the Style and CharacterProperties services. If you click on the triangles next to the “Public Attributes inherited from” lines, the documentation expands to display those properties.

Fig. 39 contains two “(??)” strings – one is to indicate that there’s no documentation for FrameStyle, so it is a guess about its inheritance hierarchy.

The other “(??)” is in the ParagraphStyle hierarchy. The documentation for ParagraphStyle, and the information in the developers guide, indicate that ParagraphStyle inherits only Style and ParagraphCharacter. We believe this to be incorrect, based on my coding with ParagraphStyle (some of which you’ll see in the next sections). ParagraphStyle appears to inherits three services: Style, ParagraphCharacter, and CharacterStyle, as indicated in Fig. 41.

Diagram of The Paragraph Service and its Superclasses

Fig. 41 :The Paragraph Service and its Super-classes.

For more information of the styles API, start in the development guide in the “Overall Document Features” section, online at: https://wiki.openoffice.org/wiki/Documentation/DevGuide/Text/Overall_Document_Features

6.2 Listing Styles Information

The Styles Info example illustrates some of the Class WriteDoc and Class Info utility methods for examining style families and their property sets. The show_styles() function starts by listing the style families names:

def show_styles(doc: WriteDoc) -> None:
    # get all the style families for this document
    families = doc.get_style_families()
    style_families = families.get_names()

    print(f"No. of Style Family Names: {len(style_families)}")
    for style_family in style_families:
        print(f"  {style_family}")
    print()

    # list all the style names for each style family
    for i, style_family in enumerate(style_families):
        print(f'{i} "{style_family}" Style Family contains containers:')
        style_names = Info.get_style_names(doc.component, style_family)
        Lo.print_names(style_names)

    # Report the properties for the paragraph styles family under the "Standard" name
    Props.show_props(
        'ParagraphStyles "Standard"',
        Info.get_style_props(doc.component, "ParagraphStyles", "Header"),
    )
    print()

Partial output lists the seven family names:

No. of Style Family Names: 7
    CellStyles
    CharacterStyles
    FrameStyles
    NumberingStyles
    PageStyles
    ParagraphStyles
    TableStyles

Info.get_style_names() starts by calling Info.get_style_container() which in turn calls Info.get_style_families(). get_style_families() gets XStyleFamiliesSupplier that is passed to get_style_container() which in turn gets XNameContainer that is passed to get_style_names(). The family names in that collection are extracted with style_container.getElementNames():

@staticmethod
def get_style_families(doc: object) -> XNameAccess:
    try:
        xsupplier = Lo.qi(XStyleFamiliesSupplier, doc, True)
        return xsupplier.getStyleFamilies()
    except MissingInterfaceError:
        raise
    except Exception as e:
        raise Exception("Unable to get family style names") from e

@classmethod
def get_style_container(cls, doc: object, family_style_name: str) -> XNameContainer:
    name_acc = cls.get_style_families(doc)
    xcontianer = Lo.qi(XNameContainer, name_acc.getByName(family_style_name), True)
    return xcontianer

@classmethod
def get_style_names(cls, doc: object, family_style_name: str) -> List[str]:
    try:
        style_container = cls.get_style_container(doc=doc, family_style_name=family_style_name)
        names = style_container.getElementNames()
        lst = list(names)
        lst.sort()
        return lst
    except Exception as e:
        raise Exception("Could not access style names") from e

Styles Info example, the show_styles() function continues by looping through the list of style family names, printing all the style (property set) names in each family:

# list all the style names for each style family
for i, style_family in enumerate(style_families):
    print(f'{i} "{style_family}" Style Family contains containers:')
    style_names = Info.get_style_names(doc, style_family)
    Lo.print_names(style_names)

The output is lengthy, but informative:

0 "CellStyles" Style Family contains containers:
No. of names: 0


1 "CharacterStyles" Style Family contains containers:
No. of names: 27
  'Bullet Symbols'  'Caption characters'  'Citation'  'Definition'
  'Drop Caps'  'Emphasis'  'Endnote anchor'  'Endnote Symbol'
  'Example'  'Footnote anchor'  'Footnote Symbol'  'Index Link'
  'Internet link'  'Line numbering'  'Main index entry'  'Numbering Symbols'
  'Page Number'  'Placeholder'  'Rubies'  'Source Text'
  'Standard'  'Strong Emphasis'  'Teletype'  'User Entry'
  'Variable'  'Vertical Numbering Symbols'  'Visited Internet Link'

2 "FrameStyles" Style Family contains containers:
No. of names: 7
  'Formula'  'Frame'  'Graphics'  'Labels'
  'Marginalia'  'OLE'  'Watermark'

3 "NumberingStyles" Style Family contains containers:
No. of names: 11
  'List 1'  'List 2'  'List 3'  'List 4'
  'List 5'  'No List'  'Numbering 123'  'Numbering ABC'
  'Numbering abc'  'Numbering IVX'  'Numbering ivx'

4 "PageStyles" Style Family contains containers:
No. of names: 10
  'Endnote'  'Envelope'  'First Page'  'Footnote'
  'HTML'  'Index'  'Landscape'  'Left Page'
  'Right Page'  'Standard'

5 "ParagraphStyles" Style Family contains containers:
No. of names: 125
  'Addressee'  'Appendix'  'Bibliography 1'  'Bibliography Heading'
  'Caption'  'Contents 1'  'Contents 10'  'Contents 2'
  'Contents 3'  'Contents 4'  'Contents 5'  'Contents 6'
  'Contents 7'  'Contents 8'  'Contents 9'  'Contents Heading'
  'Drawing'  'Endnote'  'Figure'  'Figure Index 1'
  'Figure Index Heading'  'First line indent'  'Footer'  'Footer left'
  'Footer right'  'Footnote'  'Frame contents'  'Hanging indent'
  'Header'  'Header and Footer'  'Header left'  'Header right'
  'Heading'  'Heading 1'  'Heading 10'  'Heading 2'
  'Heading 3'  'Heading 4'  'Heading 5'  'Heading 6'
  'Heading 7'  'Heading 8'  'Heading 9'  'Horizontal Line'
  'Illustration'  'Index'  'Index 1'  'Index 2'
  'Index 3'  'Index Heading'  'Index Separator'  'List'
  'List 1'  'List 1 Cont.'  'List 1 End'  'List 1 Start'
  'List 2'  'List 2 Cont.'  'List 2 End'  'List 2 Start'
  'List 3'  'List 3 Cont.'  'List 3 End'  'List 3 Start'
  'List 4'  'List 4 Cont.'  'List 4 End'  'List 4 Start'
  'List 5'  'List 5 Cont.'  'List 5 End'  'List 5 Start'
  'List Contents'  'List Heading'  'List Indent'  'Marginalia'
  'Numbering 1'  'Numbering 1 Cont.'  'Numbering 1 End'  'Numbering 1 Start'
  'Numbering 2'  'Numbering 2 Cont.'  'Numbering 2 End'  'Numbering 2 Start'
  'Numbering 3'  'Numbering 3 Cont.'  'Numbering 3 End'  'Numbering 3 Start'
  'Numbering 4'  'Numbering 4 Cont.'  'Numbering 4 End'  'Numbering 4 Start'
  'Numbering 5'  'Numbering 5 Cont.'  'Numbering 5 End'  'Numbering 5 Start'
  'Object index 1'  'Object index heading'  'Preformatted Text'  'Quotations'
  'Salutation'  'Sender'  'Signature'  'Standard'
  'Subtitle'  'Table'  'Table Contents'  'Table Heading'
  'Table index 1'  'Table index heading'  'Text'  'Text body'
  'Text body indent'  'Title'  'User Index 1'  'User Index 10'
  'User Index 2'  'User Index 3'  'User Index 4'  'User Index 5'
  'User Index 6'  'User Index 7'  'User Index 8'  'User Index 9'
  'User Index Heading'

6 "TableStyles" Style Family contains containers:
No. of names: 11
  'Academic'  'Box List Blue'  'Box List Green'  'Box List Red'
  'Box List Yellow'  'Default Style'  'Elegant'  'Financial'
  'Simple Grid Columns'  'Simple Grid Rows'  'Simple List Shaded'

Info.get_style_names() retrieves the XNameContainer object for each style family, and extracts its style (property set) names using getElementNames():

@classmethod
def get_style_names(cls, doc: object, family_style_name: str) -> List[str]:
    try:
        style_container = cls.get_style_container(doc=doc, family_style_name=family_style_name)
        names = style_container.getElementNames()
        lst = list(names)
        lst.sort()
        return lst
    except Exception as e:
        raise Exception("Could not access style names") from e

The last part of Styles Info lists the properties for a specific property set. Info.get_style_props() does that:

@classmethod
def get_style_props(
    cls, doc: object, family_style_name: str, prop_set_nm: str
) -> XPropertySet:
    style_container = cls.get_style_container(doc, family_style_name)
    name_props = Lo.qi(XPropertySet, style_container.getByName(prop_set_nm), True)
    return name_props

Its arguments are the document, the style family name, and style (property set) name.

A reference to the property set is returned. Accessing the “Standard” style (property set) of the “ParagraphStyle” family would require:

props = Info.get_style_props(doc.component, "ParagraphStyles", "Standard")

The property set can be nicely printed by calling Props.show_props():

Props.show_props('ParagraphStyles "Standard"', props)

The output is long, but begins and ends like so:

ParagraphStyles "Standard" Properties
    BorderDistance: 0
    BottomBorder: (com.sun.star.table.BorderLine2){ (com.sun.star.table.BorderLine){ Color = (long)0x0, InnerLineWidth = (short)0x0, OuterLineWidth = (short)0x0, LineDistance = (short)0x0 }, LineStyle = (short)0x0, LineWidth = (unsigned long)0x0 }
    BottomBorderDistance: 0
    BreakType: <Enum instance com.sun.star.style.BreakType ('NONE')>
    Category: 4
    CharAutoKerning: True
    CharBackColor: -1
    CharBackTransparent: True
        :
    Rsid: 0
    SnapToGrid: True
    StyleInteropGrabBag: ()
    TopBorder: (com.sun.star.table.BorderLine2){ (com.sun.star.table.BorderLine){ Color = (long)0x0, InnerLineWidth = (short)0x0, OuterLineWidth = (short)0x0, LineDistance = (short)0x0 }, LineStyle = (short)0x0, LineWidth = (unsigned long)0x0 }
    TopBorderDistance: 0
    WritingMode: 4

This listing, and in fact any listing of a style from “ParagraphStyles”, shows that the properties are a mixture of those defined in the Style, ParagraphProperties, and CharacterProperties services.

6.3 Creating a New Style

The Story Creator example adds a new style to the paragraph style family using create_style_para() of Write, and uses it to format the document’s paragraphs.

def create_para_style(doc: WriteDoc, style_name: str) -> bool:
    try:
        # font 12 pt
        font = Font(name=Info.get_font_general_name(), size=12.0)

        # spacing below paragraphs
        spc = Spacing(below=UnitMM(4))

        # paragraph line spacing
        ln_spc = LineSpacing(mode=ModeKind.FIXED, value=UnitMM(6))

        _ = doc.create_style_para(style_name=style_name, styles=[font, spc, ln_spc])

        return True

    except Exception as e:
        print("Could not set paragraph style")
        print(f"  {e}")
    return False

Three style format are created, Font, Spacing, and LineSpacing. These formats are passed to create_style_para() which results in the style formats being applied to the style as it is being created.

UnitMM are imported from units and is a convenient way to pass values into methods that accept Class UnitT arguments.

In Story Creator, create_para_style() is called like so:

doc = WriteDoc(Write.create_doc(loader=loader))
# ...
if not create_para_style(doc, "adParagraph"):
    raise RuntimeError("Could not create new paragraph style")

# ...

A new style called adParagraph is added to the paragraph style family. It uses os dependent font determined by Info.get_font_general_name() such as “Liberation Serif” 12pt font, and leaves a 4mm space between paragraphs.

6.4 Applying Styles to Paragraphs (and Characters)

An adParagraph style is added to the paragraph style family, but how to apply that style to some paragraphs in the document? The easiest way is through the document’s XTextRange interface. XTextRange is supported by the TextRange service, which inherits ParagraphProperties and CharacterProperties (and several other property classes), as illustrated in Fig. 42.

Diagrom of The TextRange Service.

Fig. 42 :The TextRange Service.

XTextRange can be cast to XPropertySet to make the properties in ParagraphProperties and CharacterProperties accessible. An existing (or new) paragraph style can be applied to a text range by setting its ParaStyleName property:

However OooDev can simplify this process by using Para class.

from ooodev.format.writer.style.para import Para as StylePara
# ...

xtext_range = doc.component.getText().getStart()
para_style = StylePara("adParagraph")
para_style.apply(xtext_range)

The code above obtains the text range at the start of the document, and set its paragraph style to adParagraph. Any text added from this position onward will use that style.

This approach is used in Story Creator: the style is set first, then text is added.

6.5 Cursors and Text Ranges

Another technique for applying styles uses a cursor to select a text range. Then the text’s properties are accessed through the cursor.

All the different kinds of model and view cursor belong to the TextCursor service, and this inherits TextRange. This allows us to extend Fig. 42 to become Fig. 43.

Diagrom of Cursor Access to Text Properties

Fig. 43 :Cursor Access to Text Properties.

This hierarchy means that a cursor can access the TextRange service and its text properties. The following code fragment demonstrates the idea:

cursor = Write.get_cursor(doc)
cursor.gotoEnd(True) # select the entire document

props = Lo.qi(XPropertySet, cursor)
props.setProperty("ParaStyleName", "adParagraph")

Using Props.set_property(), simplifies this to:

cursor = Write.get_cursor(doc)
cursor.gotoEnd(True)
Props.set_property(cursor, "ParaStyleName", "adParagraph")

Since 0.15.0 it is possible to use Class WriteDoc to simplify the code even further:

from ooodev.write import Write, WriteDoc
# ...
doc = WriteDoc(Write.create_doc(loader=loader))
# ...
cursor = WriteDoc.get_cursor()
cursor.set_property(ParaStyleName = "adParagraph")

6.6 Building a Story Document

Story Creator example starts by setting the adParagraph style, then employs read_text() to read text from a file and add it to the document:

text_range = doc.component.getText().getStart()
para_style = StylePara("adParagraph")
para_style.apply(xtext_range)

# ...

read_text(fnm=fnm, cursor=cursor)
cursor.end_paragraph()

read_text() assumes the text file has a certain format. For example, scandal.txt begins like so:

A paragraph is a series of text lines followed by a blank line. But there are exceptions: lines that starts with “Title: “, “Author: ” or “Part ” are treated as headings, and styled differently. When the text above is processed, the resulting document looks like Fig. 44.

Screen Shot of The Output of Story Creator Example

Fig. 44 :The Output of Story Creator Example.

read_text() is implemented using python’s with open(fnm, 'r') as file: context manager:

def read_text(fnm: Path, cursor: WriteTextCursor) -> None:
    sb: List[str] = []
    with open(fnm, "r") as file:
        i = 0
        for ln in file:
            line = ln.rstrip()  # remove new line \n
            if len(line) == 0:
                if len(sb) > 0:
                    cursor.append_para(" ".join(sb))
                sb.clear()
            elif line.startswith("Title: "):
                cursor.append_para(line[7:], styles=[StylePara(StyleParaKind.TITLE)])
            elif line.startswith("Author: "):
                cursor.append_para(line[8:], styles=[StylePara(StyleParaKind.SUBTITLE)])
            elif line.startswith("Part "):
                cursor.append_para(line, styles=[StylePara(StyleParaKind.HEADING_1)])
            else:
                sb.append(line)
            i += 1
            # if i > 20:
            #     break
        if len(sb) > 0:
            cursor.append_para(" ".join(sb))

The interesting bits are the calls to cursor.append_para() which add a paragraph to the document and apply a style to it. For instance:

elif line.startswith("Author: "):
    cursor.append_para(line[8:], styles=[StylePara(StyleParaKind.SUBTITLE)])

Para (imported as StylePara) is used to access Subtitle style. StyleParaKind is an enum contain built in style values.

WriteTextCursor.append_para() writes the string into the document as a paragraph and applies the style (the input line without the “Author: ” substring).

6.7 Style Changes to Words and Phrases

Aside from changing paragraph styles, it’s useful to apply style changes to words or strings inside a paragraph. For example, to highlight a word in bold, or write several words in red italics for emphasis.

This is implemented by WriteTextCursor.style_left() using a similar approach to WriteTextCursor.style_prev_paragraph(). style_left() is passed an integer position which lies to the left of the current cursor position. Character style changes are applied to the text range defined by that distance this in turn calls style_left() which is shown below:

def style_left(cursor: XTextCursor, pos: int, prop_name: str, prop_val: object) -> None:
    old_val = Props.get_property(cursor, prop_name)

    curr_pos = Selection.get_position(cursor)
    cursor.goLeft(curr_pos - pos, True)
    Props.set_property(prop_set=cursor, name=prop_name, value=prop_val)

    cursor.goRight(curr_pos - pos, False)
    Props.set_property(prop_set=cursor, name=prop_name, value=old_val)

A XTextCursor is used to select the range, and the new style is set. Then the cursor is moved back to its old position, and the previous style reapplied.

There is also an overload (shown below) of Write.style_left() that works in s similar manor but takes one or more StyleT styles.

def style_left(cls, cursor: XTextCursor, pos: int, styles: Iterable[StyleT]) -> None:
    ...

Class Write and Class WriteTextCursor contain a few support functions that set common styles using style_left():

@classmethod
def style_left_bold(cls, cursor: XTextCursor, pos: int) -> None:
    cls.style_left(cursor, pos, "CharWeight", FontWeight.BOLD)

@classmethod
def style_left_italic(cls, cursor: XTextCursor, pos: int) -> None:
    cls.style_left(cursor, pos, "CharPosture", FontSlant.ITALIC)

@classmethod
def style_left_color(cls, cursor: XTextCursor, pos: int, color: Color) -> None:
    cls.style_left(cursor, pos, "CharColor", color)

@classmethod
def style_left_code(cls, cursor: XTextCursor, pos: int) -> None:
    cls.style_left(cursor, pos, "CharFontName", Info.get_font_mono_name())
    cls.style_left(cursor, pos, "CharHeight", 10)

The position (the pos value) passed to style_left() can be obtained from Write.get_position() and WriteTextCursor.get_position().

The Build Doc example contains several examples of how to use formatting styles from format module.

doc = WriteDoc(Write.create_doc(loader=loader))
cursor = doc.get_cursor()
cursor.append("Some examples of simple text ")
cursor.append_line(text="styles.", styles=[Font(b=True)])
cursor.append_para(
    text="This line is written in red italics.",
    styles=[Font(color=CommonColor.DARK_RED).bold.italic],
)
cursor.append_para("Back to old style")
cursor.append_line()

The resulting text in the document looks like Fig. 45.

Screen Shot of Styled Text

Fig. 45 :Styled Text.

The following fragment from Build Doc applies a ‘code’ styling to several lines:

cursor.append_para("Here's some code:")

code_font = Font(name=Info.get_font_mono_name(), size=10)
code_font.apply(cursor.component)

cursor.append_line("public class Hello")
cursor.append_line("{")
cursor.append_line("  public static void main(String args[]")
cursor.append_line('  {  System.out.println("Hello World");  }')
cursor.append_para("}  // end of Hello class")

# reset the cursor formatting
ParaStyle.default.apply(cursor.component)

Fig. 46 shows the generated document text.

Screen Shot of Text with Code Styling

Fig. 46 :Text with Code Styling.

ooodev.format.writer.direct.char.font.Font is imported and used to create mono font values and applied directly to the cursor.

from ooodev.format.writer.direct.char.font import Font
# ...

code_font = Font(name=Info.get_font_mono_name(), size=10)
code_font.apply(cursor.component)
# ...

Once the code as been written, ParaStyle.default is applied to the cursor to reset its values.

from ooodev.format.writer.style.para import Para as ParaStyle
# ...

# reset the cursor formatting
ParaStyle.default.apply(cursor.component)
# ...

6.9 Text Numbering

It’s straightforward to number paragraphs by using ListStyle, StyleListKind and BulletList.

The following code from Build Doc , numbers three paragraphs:

from ooodev.format.writer.direct.para.outline_list import ListStyle, StyleListKind
# ...

cursor.append_para("The following points are important:")

list_style = ListStyle(list_style=StyleListKind.NUM_123, num_start=-2)
list_style.apply(cursor.component)

cursor.append_para("Have a good breakfast")
cursor.append_para("Have a good lunch")
cursor.append_para("Have a good dinner")

# Reset to default which set cursor to No List Style
list_style.default.apply(cursor.component)

The result is shown in Fig. 48.

Screen Shot of Numbered Paragraphs.

Fig. 48 :Numbered Paragraphs.

Letters are drawn instead of numbers by changing the style name to “Numbering abc” (see Fig. 49).

from ooodev.format.writer.direct.para.outline_list import ListStyle, StyleListKind
from ooodev.format.writer.style.bullet_list import BulletList
# ...

cursor.append_para("Breakfast should include:")

# set cursor style to Number abc
list_style = ListStyle(list_style=StyleListKind.NUM_abc, num_start=-2)
list_style.apply(cursor.component)

cursor.append_para("Porridge")
cursor.append_para("Orange Juice")
cursor.append_para("A Cup of Tea")
# reset cursor number style
list_style.default.apply(cursor.component)
Screen Shot of Lettered Paragraphs.

Fig. 49 :Letter Paragraphs.

One issue with numbered paragraphs is that their default behavior retains the current count when numbering another group of text. For example, a second group of numbered paragraphs appearing in the document after Fig. 48 would start at 4. This is fixed by setting num_start=-2 when creating an instance of ListStyle, this forces a numbering reset.

list_style = ListStyle(list_style=StyleListKind.NUM_123, num_start=-2)

One large topic that is not covered in this document is numbering. This includes the numbering of chapter headings and lines. Chapter and line numbering are dealt with differently from most document styles. Instead of being accessed via XStyleFamiliesSupplier, they employ XChapterNumberingSupplier and XNumberFormatsSupplier.

For more details, see the development guide: https://wiki.openoffice.org/wiki/Documentation/DevGuide/Text/Line_Numbering_and_Outline_Numbering

6.10 Other Style Changes

Story Creator example illustrates three other styling effects: the creation of a header, setting the page to A4 format, and employing page numbers in the footer. The relevant calls are:

# fragment from story creator
from ooodev.format.writer.direct.char.font import Font
from ooodev.format.writer.direct.page.header import Header
from ooodev.format.writer.direct.page.header.area import Img, PresetImageKind
# ...

# header formatting
# create a header font style with a size of 9 pt, italic and dark green.
header_font = Font(
    name=Info.get_font_general_name(),
    size=9.0,
    i=True,
    color=CommonColor.DARK_GREEN
)
header_format = Header(
    on=True,
    shared_first=True,
    shared=True,
    height=13.0,
    spacing=3.0,
    spacing_dyn=True,
    margin_left=1.5,
    margin_right=2.0,
)
# create a header image from a preset
header_img = Img.from_preset(PresetImageKind.MARBLE)
# Set header can be passed a list of styles to format the header.
doc.set_header(
    text=f"From: {fnm.name}",
    styles=[header_font, header_format, header_img],
)

# page format A4
_ = doc.set_a4_page_format()
_ = doc.set_page_numbers()

Alternatively, the page format can be set via the PaperFormat class.

from ooodev.format.writer.modify.page.page import PaperFormat, PaperFormatKind
# ...

page_size_style = PaperFormat.from_preset(preset=PaperFormatKind.A4)
page_size_style.apply(doc.component)

Write.set_page_numbers() utilizes text fields, which is examined in the “Text Fields” section in Chapter 7. Text Content Other than Strings.

Changing the header in Write.set_header() requires the setting of the HeaderIsOn boolean in the Standard page style. Adding text to the header is done via an XText reference. A simpler version of OooDev’s Write.set_header() is shown below.

@staticmethod
def set_header(text_doc: XTextDocument, text: str) -> None:
    props = Info.get_style_props(
        doc=text_doc, family_style_name="PageStyles", prop_set_nm="Standard"
    )
    if props is None:
        raise PropertiesError("Could not access the standard page style container")
    try:
        props.setPropertyValue("HeaderIsOn", True)
        # header must be turned on in the document
        # props.setPropertyValue("TopMargin", 2200)
        header_text = Lo.qi(XText, props.getPropertyValue("HeaderText"))
        header_cursor = header_text.createTextCursor()
        header_cursor.gotoEnd(False)

        header_props = Lo.qi(XPropertySet, header_cursor, True)
        header_props.setPropertyValue("CharFontName", Info.get_font_general_name())
        header_props.setPropertyValue("CharHeight", 10)
        header_props.setPropertyValue("ParaAdjust", ParagraphAdjust.RIGHT)

        header_text.setString(f"{text}\n")
    except Exception as e:
        raise Exception("Unable to set header text") from e

There also is a Write.set_footer() method in OooDev that allows for the passing of a list of styles to format the footer.

The header’s XText reference is retrieved via the page style’s HeaderText property, and a cursor is created local to the header:

header_cursor = header_text.createTextCursor()

This cursor can only move around inside the header not the entire document.

The properties of the header’s XText are changed using the cursor, and then the text is added.