Automating LibreOffice in Python is complicated as all get out.
It’s gotten easier with the ooodev
wrapper library that allows IntelliSense to do its thing, but it only covers a portion of UNO features (albeit the most commonly used portion).
Still, there are multiple different ways to perform automation tasks in Python, whether going through the wrapper or not:
-
Navigating the model via a Text Cursor and accessing the embedded constructs and properties directly.
-
Navigating the formatted text via a View Cursor and indirectly accessing the model’s constructs and properties.
-
Accessing the model’s embedded constructs (graphics, frames, tables) via enumerated lists and working backwards to navigate from their anchor points.
-
Dispatching a command (https://wiki.documentfoundation.org/Development/DispatchCommands) — as if the user interacting with the LibreOffice UI — in conjuction with the View Cursor.
In all of these cases, you might need to switch back and forth between taking advantage of the wrapper (code written in snake_case) and calling the original UNO code directly (code written in TitleCase).
Usually, the latter is done by breaking out of the wrapper code by referencing the component
property.
You’ll recognize this by code that looks like snake_case.component.TitleCase
where the IDE won’t be able to provide IntelliSense help on the TitleCase portion.
-
Zaz-Pip is a LibreOffice extension that allows pip installation of Python packages from within LibreOffice.
-
LibreOffice is version 24.2.1.2 as of 3/25/2024
-
The embedded Python interpreter is at:
C:\Program Files\LibreOffice\program\python-core-3.8.18\bin\python.exe
-
With the corresponding:
C:\Program Files\LibreOffice\program\python-core-3.8.18\lib
C:\proj\LibreOffice.code-workspace
includes:
-
C:\forks\LibreWriter_Macros\python-ooouno-ex
— The ooodev samples project -
C:\Program Files\LibreOffice\share\Scripts\python\*.py
— Scripts to run within LibreOffice -
C:\proj\proj_gruntwurk\gwpycore
— The GruntWurk core python library -
C:\proj\proj_gruntwurk\self-publish
— The GruntWurk library for a self-publishing tool chain (AsciiDoc → LibreOffice → KindleGen, etc.)
-
Python Scripts Organization and Location
-
Running Python Interactive Console
-
Programming with Python Scripts
-
Python programming examples
Alternative Python Script Organizer — Allows for Py script editing within LibreOffice
A “library” is a folder.
A “module” is a file.
Depends on .py
being associated with an editor/IDE (not Python itself).
{sl} https://api.libreoffice.org/docs/idl/ref/annotated.html. {sl} https://extensions.libreoffice.org/en/extensions/show/5742 — Inline heading (BASIC) extension.
Macros may receive different args depending on what they are assigned to, so be sure to use a signature of (*args, **kwargs)
def dialog_example(): ctx = XSCRIPTCONTEXT.getComponentContext() smgr = ctx.getServiceManager() dp = smgr.createInstanceWithContext("com.sun.star.awt.DialogProvider", ctx) dialog = dp.createDialog("vnd.sun.star.script:Standard.Dialog1?location=user") dialog.execute() dialog.dispose()
C:\forks\LibreWriter_Macros\additional_examples\sdk-examples\TuesdayPython
Find |
Replace |
Libre |
MS Word |
String |
F |
^1 or ^g |
Picture (inline pictures only) |
||
F |
^f |
Auto-referenced footnotes |
||
F |
^e |
Auto-referenced endnotes |
||
F |
^2 |
Auto-referenced footnotes or endnotes |
||
F |
^5 or ^a |
Annotation/comment mark |
||
F |
^19 or |
Opening field brace (Use only when you are viewing field codes.) (Selects whole field, not just opening brace.) |
||
F |
^21 or |
Closing field brace (Use only when you are viewing field codes.) (Selects whole field, not just closing brace.) |
||
F |
. |
^? |
Any single character |
|
F |
\d |
^# |
Any digit |
|
F |
\a |
^$ |
Any letter |
|
F |
\u8195 |
^u8195 |
Em space Unicode character value search |
|
F |
\u8194 |
^u8194 |
En space Unicode character value search |
|
F |
^b |
Section break |
||
F |
^w |
White space (space, nonbreaking space, tab) |
||
F |
^unnnn |
Word 2000 Unicode character search, where "n" is a decimal number corresponding to the Unicode character value |
||
R |
& or $0 |
^& |
Contents of the "Find what" box |
|
R |
^c |
Replace with the Clipboard contents |
||
F |
R |
\t |
^9 or ^t |
Tab |
F |
R |
^11 or |
New line |
|
F |
R |
\u2014 |
^+ |
Em dash |
F |
R |
\u2013 |
^= |
En dash |
F |
R |
^12 |
Page or section break (Replaces a section break with a page break) |
|
F |
R |
^13 or |
Carriage return/paragraph mark |
|
F |
R |
^14 or |
Column break |
|
F |
R |
? |
Question mark |
|
F |
R |
^- |
Optional hyphen |
|
F |
R |
^~ |
Nonbreaking hyphen |
|
F |
R |
^^ |
Caret character |
|
F |
R |
^m |
Manual page break |
|
F |
R |
^s |
Nonbreaking space |
|
F |
R |
^nnn |
Where "n" is an ASCII character number |
|
F |
R |
^0nnn |
Where "n" is an ANSI character number |
Set g_exportedScripts to a tuple of the functions you want to be able to be called directly by the user.
def func_a(): pass def func_b(): pass def func_hidden(): pass # not shown in the UI g_exportedScripts = func_a, func_b
In Python, you can import some modules that can be found in sys.path
list.
If you want to import your own module placed inside Scripts/python directory (see Script Locations), put your module in pythonpath
directory nearby your script file.
- Scripts/ - python/ - macro.py - pythonpath/ # this directory is added automatically before your macro executed - your_module.py # this module can be found
When you execute the macro from your script file, the internal executor adds the pythonpath/
directory to sys.path
list to be used as one of lookup location.
{s} Assigning Macros
-
The LibreOffice macro recorder sucks (besides only working in Basic, not Python). When you try to record, for example, changing the after-spacing on a paragraph Style, it only records the fact that you’re opening the definition dialog box. It does nothing about recording the actual changes that you’re making.
-
In any event, the macro recorder is not available in the ribbon bar. You have to pull down Tools > Macros > RecordMacro.
- User-specific
-
C:\Users\<user>\AppData\Roaming\LibreOffice\4\user\Scripts\python
- All users
-
%APPDATA%\LibreOffice\4\user\Scripts\python
{s} Find and Replace
Service managers are factories that create services. Examples:
-
com.sun.star.frame.Desktop
— maintains loaded documents: is used to load documents, to get the current document, and access all loaded documents -
com.sun.star.configuration.ConfigurationProvider
— yields access to the Apache OpenOffice configuration, for instance the settings in the Tools > Options dialog -
com.sun.star.sdb.DatabaseContext
— holds databases registered with Apache OpenOffice -
com.sun.star.system.SystemShellExecute
— executes system commands or documents registered for an application on the current platform -
com.sun.star.text.GlobalSettings
— manages global view and print settings for text documents
For a Writer Document…
Default styles in the CellStyles family: (none)
Default styles in the CharacterStyles family: Bullet Symbols
, Caption characters
, Citation
, Definition
, Drop Caps
, Emphasis
, Endnote Symbol
, Endnote anchor
, Example
, Footnote Symbol
, Footnote anchor
, 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
Default styles in the FrameStyles family: Formula
, Frame
, Graphics
, Labels
, Marginalia
, OLE
, Watermark
Default styles in the NumberingStyles family: List 1
, List 2
, List 3
, List 4
, List 5
, No List
, Numbering 123
, Numbering ABC
, Numbering IVX
, Numbering abc
, Numbering ivx
Default styles in the PageStyles family: Endnote
, Envelope
, First Page
, Footnote
, HTML
, Index
, Landscape
, Left Page
, Right Page
, Standard
Default styles in the ParagraphStyles family: Addressee
, Appendix
, Bibliography 1
, Bibliography Heading
, Caption
, Comment
, Contents 1
thru Contents 10
, 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
thru Heading 10
, Horizontal Line
, Illustration
, Index
, Index 1
thru Index 3
, Index Heading
, Index Separator
, List
, List 1
, List 1 Cont.
, List 1 End
, List 1 Start
thru 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
thru 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
thru User Index 10
, User Index Heading
Default styles in the TableStyles family: 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
,
-
BreakType:
com.sun.star.style.BreakType
-
Category: 4
-
CharBackColor: -1
-
CharBackTransparent: True
-
CharColor: -1
-
CharCrossedOut: False
-
CharEmphasis: 0
-
CharEscapement: 0
-
CharEscapementHeight: 100
-
CharFontFamily: 3
-
CharFontName: Liberation Serif
-
CharFontPitch: 2
-
CharFontStyleName:
-
CharHeight: 12.0
-
CharHidden: False
-
CharHighlight: -1
-
CharNoHyphenation: True
-
CharOverline: 0
-
CharOverlineColor: -1
-
CharOverlineHasColor: False
-
CharPosture: <Enum instance com.sun.star.awt.FontSlant ('NONE')>
-
CharPropHeight: 100
-
CharRotation: 0
-
CharRotationIsFitToLine: False
-
CharScaleWidth: 100
-
CharShadingValue: 0
-
CharShadowed: False
-
CharStrikeout: 0
-
CharTransparence: 100
-
CharUnderline: 0
-
CharUnderlineColor: -1
-
CharUnderlineHasColor: False
-
CharWeight: 100.0
-
CharWordMode: False
-
DisplayName: Header
-
DropCapCharStyleName:
-
DropCapFormat: (com.sun.star.style.DropCapFormat){ Lines = (byte)0x0, Count = (byte)0x0, Distance = (short)0x0 }
-
DropCapWholeWord: False
-
FollowStyle: Header
-
Hidden: False
-
IsAutoUpdate: False
-
IsPhysical: True
-
LinkStyle:
-
NumberingLevel: 0
-
NumberingStyleName:
-
OutlineLevel: 0
-
PageDescName: None
-
PageNumberOffset: None
-
ParaAdjust: 0
-
ParaExpandSingleWord: False
-
ParaFirstLineIndent: 0
-
ParaFirstLineIndentRelative: 100
-
ParaHyphenationMaxHyphens: 0
-
ParaHyphenationMaxLeadingChars: 2
-
ParaHyphenationMaxTrailingChars: 2
-
ParaHyphenationNoCaps: False
-
ParaInteropGrabBag: ()
-
ParaIsAutoFirstLineIndent: False
-
ParaIsCharacterDistance: True
-
ParaIsForbiddenRules: True
-
ParaIsHangingPunctuation: True
-
ParaIsHyphenation: False
-
ParaKeepTogether: False
-
ParaLastLineAdjust: 0
-
ParaLineNumberCount: False
-
ParaLineNumberStartValue: 0
-
ParaLineSpacing: (com.sun.star.style.LineSpacing){ Mode = (short)0x0, Height = (short)0x64 }
-
ParaOrphans: 2
-
ParaRegisterModeActive: False
-
ParaShadowFormat: (com.sun.star.table.ShadowFormat){ Location = (com.sun.star.table.ShadowLocation)NONE, ShadowWidth = (short)0xb0, IsTransparent = (boolean)false, Color = (long)0x808080 }
-
ParaSplit: True
-
ParaTabStops: ((com.sun.star.style.TabStop){ Position = (long)0x225b, Alignment = (com.sun.star.style.TabAlign)CENTER, DecimalChar = (char)'.', FillChar = (char)' ' }, (com.sun.star.style.TabStop){ Position = (long)0x44b6, Alignment = (com.sun.star.style.TabAlign)RIGHT, DecimalChar = (char)'.', FillChar = (char)' ' })
-
ParaUserDefinedAttributes: pyuno object (com.sun.star.container.XNameContainer)0x2599f4aa498{implementationName=SvUnoAttributeContainer, supportedServices={com.sun.star.xml.AttributeContainer}, supportedInterfaces={com.sun.star.lang.XServiceInfo,com.sun.star.lang.XUnoTunnel,com.sun.star.container.XNameContainer,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak,com.sun.star.uno.XAggregation}}
-
ParaVertAlignment: 0
-
ParaWidows: 2
-
Rsid: 0
-
SnapToGrid: True
-
StyleInteropGrabBag: ()
-
WritingMode: 4
CharWeight constants:
DONTKNOW = 0.0 THIN = 50.0 ULTRALIGHT = 60.0 LIGHT = 75.0 SEMILIGHT = 90.0 NORMAL = 100.0 SEMIBOLD = 110.0 BOLD = 150.0 ULTRABOLD = 175.0 BLACK = 200.0
ParaLineSpacing Modes:
PROP = 0 # The height value is a proportional value. MINIMUM = 1 # The height is the minimum line height. LEADING = 2 # The height value is the distance to the previous line. FIX = 3 # The height value is a fixed line height.
ParaVertAlignment Modes:
AUTOMATIC = 0 # In automatic mode, horizontal text is aligned to the baseline. The same applies to text that is rotated 90°. Text that is rotated 270° is aligned to the center. BASELINE = 1 # The text is aligned to the baseline. TOP = 2 # The text is aligned to the top. CENTER = 3 # The text is aligned to the center. BOTTOM = 4 # The text is aligned to bottom.
Borders:
-
CharBorderDistance: 0
-
CharBottomBorder: (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 }
-
CharBottomBorderDistance: 0
-
CharLeftBorder: (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 }
-
CharLeftBorderDistance: 0
-
CharRightBorder: (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 }
-
CharRightBorderDistance: 0
-
CharTopBorder: (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 }
-
CharTopBorderDistance: 0
-
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
-
LeftBorder: (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 }
-
LeftBorderDistance: 0
-
RightBorder: (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 }
-
RightBorderDistance: 0
-
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
-
ParaIsConnectBorder: True
Margins:
-
ParaContextMargin: False
-
ParaBottomMargin: 0
-
ParaBottomMarginRelative: 100
-
ParaLeftMargin: 0
-
ParaLeftMarginRelative: 100
-
ParaRightMargin: 0
-
ParaRightMarginRelative: 100
-
ParaTopMargin: 0
-
ParaTopMarginRelative: 100
In a stand-alone Python script, use the XSCRIPTCONTEXT
object.
In a Python module, use import uno
.
import uno
def HelloWorldPythonCalc():
oDoc = XSCRIPTCONTEXT.getDocument()
# -- or --
ctx = uno.getComponentContext()
smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
oDoc = desktop.getCurrentComponent()
oSheet =oDoc.getSheets().getByIndex(0)
oCell = oSheet.getCellByPosition(0,0)
oCell.String = 'Hello World via Python'
return None
There are two ways to create a virtual environment for working with LibreOffice. One is to go through poetry (see below). The other is to manually install pip, as follows:
-
Capture the version of python that is installed with LibreOfice:
"C:\Program Files\LibreOffice\program\python.exe" --version
-
Create a rough virtual environment using your regular copy of python
cd <project folder> py -38 -m venv --without-pip .venv
-
Edit
.venv\pyvenv.cfg
, and change it to look like the following excerpt.
home = C:\Program Files\LibreOffice\program implementation = CPython version_info = 3.8.16.final.0 virtualenv = 20.17.1 include-system-site-packages = false base-prefix = C:\Program Files\LibreOffice\program\python-core-3.8.16 base-exec-prefix = C:\Program Files\LibreOffice\program\python-core-3.8.16 base-executable = C:\Program Files\LibreOffice\program\python.exe prompt = myproject_3.8.16
-
Make sure
home =
points to the python that’s installed in LibreOffice. -
Change
version_info =
to specify the version found above (e.g.version_info = 3.10.5.final.0
). -
Change the two references to the
python-core-nnn
folder to correspond. -
Change
prompt =
to correspond (optional, no impact). -
Activate the venv and Install PIP:
.\.venv\Scripts\Activate.ps1 Invoke-WebRequest -Uri https://bootstrap.pypa.io/get-pip.py -UseBasicParsing).Content | python.exe - python -m pip --version
-
Use pip to install whatever packages are needed (including editable local packages) NOTE: It is import that pip be run with
python -m pip
within the venv to ensure the correct pip is being used.
Optionally, link LibreOffice user python into virtual environment.
-
Deactivate current virtual environment.
deactivate
-
Determine the path that pip has been installed in (e.g.
C:\Users\<user>\AppData\Roaming\Python\Python38\site-packages
). -
Create a file in
.venv\Lib\site-packages
calledlibre_office_user_pkg.pth
(the name is not important as long as it ends with.pth
). Open the file in a text editor and paste in the determined path. Save and close the file. Now, when the virtual environment is activated the user python packages will be included in python’s sys.path. -
Reactivate Virtual Environment:
.\.venv\Scripts\Activate.ps1
There are two ways to create a virtual environment for working with LibreOffice. One is to manually install pip (see above). The other is to go through poetry, as follows:
-
Install poetry. See [poetry] in PYTHON_CHEAT.adoc.
-
Capture the version of customized python that is installed with LibreOfice:
"C:\Program Files\LibreOffice\program\python.exe" --version
-
Make sure that a corresponding, non-customized version of python is installed (matching the major and minor release numbers, at least, if not the build number as well). See https://github.com/pyenv-win/pyenv-win for a tool that can help with this. If using
pyenv
, then the install placement will be something likeC:\Users\<username>\.pyenv\pyenv-win\versions\3.8.10\
-
Use that non-customized python to create a virtual environment.
&"C:\Users\<username>\.pyenv\pyenv-win\versions\3.8.10\python.exe" -m venv --without-pip .venv .\.venv\Scripts\Activate.ps1 python --version
-
Use poetry to initialize a project. We will add packages later as there are some additional steps that need to be done.
cd D:\tmp\project poetry init
-
Install
oooenv
in the virtual environment:
poetry add oooenv --group=dev oooenv --version
-
Use the following command to toggle back and forth between the virtual environment using the python interpreter that is included with LiobreOffice vs. the non-customized one. (When issuing poetry commands, it need to be the non-customized one.)
oooenv env -t poetry add ooo-dev-tools
import uno from ooodev.loader.lo import Lo from ooodev.calc import CalcDoc def say_hello(cell_name): doc = CalcDoc.from_current_doc() sheet = doc.sheets[0] sheet[cell_name].value="Hello World!" _ = Lo.load_office(Lo.ConnectSocket()) doc = CalcDoc.create_doc(visible=True) say_hello("A1") doc.close() Lo.close_office()
Zaz-Pip is a LibreOffice extension (*.oxt
) that allows pip installation of Python packages from within LibreOffice.
-
Download the .oxt file from https://git.cuates.net/elmau/zaz-pip/src/branch/master/extension
-
Within LibreOffice, Tools → Extension Manager
-
Click
Add
button -
Install the downloaded
ZAZPip_v1.0.0.oxt
file -
Restart LibreOffice.