LibreOffice Developer's Guide: Chapter 1 - First Steps
This chapter shows you the first steps when using the LibreOffice API. Following these steps is essential to understand and use the chapters about LibreOffice documents such as Text Documents, Spreadsheet Documents, Drawing Documents and Presentation Documents. After you have successfully done the first steps, you can go directly to the other chapters of this manual.
The focus of the first steps will be Java, but other languages are covered as well. If you want to use LibreOffice Basic afterwards, please refer to the chapters First Steps with LibreOffice Basic and UNO Language Bindings. The usage of C++ is described in C++ Language Binding.
Programming With UNO
UNO (pronounced [ˈjuːnou]) stands for Universal Network Objects and is the base component technology for LibreOffice. You can utilize and write components that interact across languages, component technologies, computer platforms, and networks. Currently, UNO is available on Linux, Solaris, Windows, FreeBSD and macOS and other platforms that LibreOffice supports. The supported programming languages are C++, Java and LibreOffice Basic. In addition, UNO is available through the component technology Microsoft COM for many other languages. On LibreOffice there is also a language binding for Python available.
UNO is also programmable with .NET languages using the Common Language Infrastructure binding, but support for the latest cross platform .NET (formerly .NET Core) is not available yet. In addition, the new scripting framework offers the use of the API through several scripting languages, such as JavaScript, Beanshell or Python. See Scripting Framework for more details.
UNO is used to access LibreOffice, using its Application Programming Interface (API). The LibreOffice API is the comprehensive specification that describes the programmable features of LibreOffice.
Fields of Application for UNO
You can connect to a local or remote instance of LibreOffice from C++, Java and COM/DCOM. C++ and Java Desktop applications, Java servlets, Java Server Pages, JScript and VBScript, and languages, such as Delphi, Visual Basic and many others can use LibreOffice to work with Office documents.
It is possible to develop UNO Components in C++ or Java that can be instantiated by the office process and add new capabilities to LibreOffice. For example, you can write Chart Add-ins or Calc Add-ins, Add-ons, linguistic extensions, new file filters, database drivers. You can even write complete applications, such as a groupware client.
UNO components, as JavaBeans, integrate with Java IDEs (Integrated Development Environment) to give easy access to LibreOffice. Using OfficeBean, editing LibreOffice documents in Java Frames is possible. With this component, LibreOffice’s API is completely exposed so that all office components can be fully controlled.
LibreOffice Basic cooperates with UNO, so that UNO programs can be directly written in LibreOffice. With this method, you supply your own office solutions and wizards based on an event-driven dialog environment.
The LibreOffice database engine and the data aware forms open another wide area of opportunities for database driven solutions.
It should be noted that there is a new API for LibreOffice called LibreOfficeKit which can be used for accessing LibreOffice functionality through C/C++, without any need to use UNO. LibreOfficeKit uses tiled rendering, and it is used in LibreOffice Online and LibreOffice for Android.
Getting Started
A number of files and installation sets are required before beginning with the LibreOffice API.
Required Files
These files are required for any of the languages you use.
LibreOffice Installation
Install a copy of LibreOffice.
You can download LibreOffice from libreoffice.org/download.
Note: This book focuses on the current version.
API Reference
The LibreOffice API reference is part of the Software Development Kit and provides detailed information about LibreOffice objects. The latest version can always be found online on api.libreoffice.org/.
Installation Sets
The following installation sets are useful to develop LibreOffice API applications with Java.
JDK
Java applications for LibreOffice require the Java Development Kit 17 or later. Download and install a JDK from adoptopenjdk.net,oracle.com (Oracle requires login to download), or microsoft.com (Microsoft does not require login).
You should install a 32-bit JDK if you are using 32-bit OS, and a 64-bit JDK if you are using 64-bit OS. The recommendation is to always use the latest official Java version because of important bug fixes. JDK versions prior to 11 are not supported anymore (although companies other than Oracle provide some support).
LibreOffice Software Development Kit (SDK)
Obtain the LibreOffice Software Development Kit (SDK) from libreoffice.org/download. It contains the build environment for the examples mentioned in this manual and reference documentation for the LibreOffice API, for the Java UNO runtime, and the C++ API. It also offers more example sources. By means of the SDK you can use GNU make
to build and run the examples we mention here.
Install the SDK on your system. The index.html file inside the SDK folder gives an overview of the SDK. For detailed instructions, which compilers to use and how to set up your development environment, please refer to the SDK installation guide.
Configuration
Enable Java in LibreOffice
LibreOffice uses a Java Virtual Machine to instantiate components written in Java. Java should be found automatically during startup (or later when Java functionality is required). If you prefer to preselect a JRE or JDK, or if no Java is found, you can configure it using the
▸ dialog in LibreOffice, in the section ▸ ▸ . In the same section, make sure "Use a Java runtime environment" is checked.First Contact
Getting Started
It is relatively simple to get a working environment that offers a transparent use of UNO functionality and of office functionality. The following demonstrates how to write and build a small program that initializes UNO, which means that it internally connects to an office or starts a new office process if necessary and tells you if it was able to get the office component context that provides the office service manager object.
The FirstUnoContact example
The FirstUnoContact example that connects to the office:
For editing and running the example in Java, start a Java IDE or source editor, and enter the source code for the FirstUnoContact
class inside the sdk/examples/DevelopersGuide/FirstSteps
folder. In order to automate the build process, you can set up sdk environment in setsdkenv_unix
, and then use make
inside sdk/examples/DevelopersGuide/FirstSteps
folder, and then make FirstUnoContact.run
.
To compile the C++ example, you should refer to the LibreOffice SDK.
To run the Python example, you should use the Python bundled with LibreOffice:
"C:\Program Files\LibreOffice\program\python" FirstUnoContact.py
/opt/libreoffice24.8/program/python FirstUnoContact.py
/Applications/LibreOffice.App/Contents/Resources/python FirstUnoContact.py
The code is as follows:
public class FirstUnoContact {
public static void main(String[] args) {
try {
// get the remote office component context
com.sun.star.uno.XComponentContext xContext =
com.sun.star.comp.helper.Bootstrap.bootstrap();
System.out.println("Connected to a running office ...");
com.sun.star.lang.XMultiComponentFactory xMCF =
xContext.getServiceManager();
String available = (xMCF != null ? "available" : "not available");
System.out.println( "remote ServiceManager is " + available );
}
catch (java.lang.Exception e){
e.printStackTrace();
}
finally {
System.exit(0);
}
}
}
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <cppuhelper/bootstrap.hxx>
#include <iostream>
#include <sal/main.h>
SAL_IMPLEMENT_MAIN() {
try {
css::uno::Reference<css::uno::XComponentContext> xContext(
cppu::bootstrap());
std::cout << "Connected to a running office ..." << std::endl;
css::uno::Reference<css::lang::XMultiComponentFactory> xMCF =
xContext->getServiceManager();
std::string available = xMCF != nullptr ? "available" : "not available";
std::cout << "remote ServiceManager is " + available << std::endl;
} catch (css::uno::Exception &e) {
std::cout << e.Message << std::endl;
return 1;
}
return 0;
}
import officehelper
try:
xContext = officehelper.bootstrap()
print("Connected to a running office ...")
xMCF = xContext.getServiceManager()
available = "not available" if xMCF is None else "available"
print("remote ServiceManager is " + available)
except Exception as e:
print(e)
Sub Main
Set oContext = GetDefaultContext()
MsgBox "Connected to a running office..."
Set oServiceManager=oContext.GetServiceManager()
REM GetProcessServiceManager() built-in Basic function can be used in place of the above function call
If IsNull(oServiceManager) Then
bAvailable = "not "
End If
MsgBox "ServiceManager is " + bAvailable + "available"
End Sub
If the example runs successfully, you should see this output, after the debug output from the program:
Connected to a running office ...
remote ServiceManager is available
Otherwise, you may see the output that says: remote ServiceManager is not available
.
For an example that connects to the office with C++, see chapter C++ Language Binding. Accessing the office with LibreOffice Basic is described in First Steps with LibreOffice Basic. Service manager component COM/DCOM automation bridge examples are available from UNO Language Bindings.
The next section describes what happens during the connection between a Java program and LibreOffice.
Service Managers
UNO introduces the concept of service managers, which can be considered as “factories” that create services. For now, it is sufficient to see services as UNO objects that can be used to perform specific tasks. Later on we will give a more precise definition for the term service. For example, the following services are available:
- 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 LibreOffice configuration, for instance the settings in the Tools > Options dialog
com.sun.star.sdb.DatabaseContext
- holds databases registered with LibreOffice
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
A service always has a component context, which consists of the service manager that created the service and other data to be used by the service.
The FirstUnoContact class above is considered a client of the LibreOffice process, LibreOffice is the server in this respect. The server has its own component context and its own service manager, which can be accessed from client programs to use the office functionality. The client program initializes UNO and gets the component context from the LibreOffice process. Internally, this initialization process creates a local service manager, establishes a pipe connection to a running LibreOffice process (if necessary a new process is started) and returns the remote component context. In the first step this is the only thing you have to know. The com.sun.star.comp.helper.Bootstrap.bootstrap() method initializes UNO and returns the remote component context of a running LibreOffice process. You can find more details about bootstrapping UNO, the opportunities of different connection types and how to establish a connection to a UNO server process in the UNO Concepts.
After this first initialization step, you can use the method getServiceManager() from the component context to get the remote service manager from the LibreOffice process, which offers you access to the complete office functionality available through the API.
Failed Connections
A remote connection can fail under certain conditions:
- Client programs should be able to detect errors. For instance, sometimes the bridge might become unavailable. Simple clients that connect to the office, perform a certain task and exit afterwards should stop their work and inform the user if an error occurred.
- Clients that are supposed to run over a long period of time should not assume that a reference to an initial object will be valid over the whole runtime of the client. The client should resume even if the connection goes down for some reason and comes back later on. When the connection fails, a robust, long running client should stop the current work, inform the user that the connection is not available and release the references to the remote process. When the user tries to repeat the last action, the client should try to rebuild the connection. Do not force the user to restart your program just because the connection was temporarily unavailable.
When the bridge has become unavailable and access is tried, it throws a DisposedException. Whenever you access remote references in your program, catch this Exception in such a way that you set your remote references to null and inform the user accordingly. If your client is designed to run for a longer period of time, be prepared to get new remote references when you find that they are currently null.
A more sophisticated way to handle lost connections is to register a listener at the underlying bridge object. The chapter UNO Interprocess Connections shows how to write a connection-aware client.
How to get Objects in LibreOffice
An object in our context is a software artifact that has methods you can call. Objects are required to do something with LibreOffice. But where do you obtain them?
New objects
In general, new objects or objects which are necessary for a first access are created by service managers in LibreOffice. In the FirstLoadComponent
example, the remote service manager creates the remote Desktop
object, which handles application windows and loaded documents in LibreOffice:
Using remote service manager
Object desktop = xRemoteServiceManager.createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
Reference<XInterface> oDesktop = xMCF->createInstanceWithContext(
"com.sun.star.frame.Desktop", xContext);
desktop = self.remote_service_manager.createInstanceWithContext(
"com.sun.star.frame.Desktop", remote_context)
desktop = createUnoService("com.sun.star.frame.Desktop")
REM StarDesktop built-in Basic object can be used in place of the above function call
Document objects
Document objects represent the files that are opened with LibreOffice. They are created by the Desktop
object, which has a loadComponentFromURL()
method for this purpose.
Objects that are provided by other objects
Objects can hand out other objects. There are two cases:
- Integral parts: Features which are designed to be an integral part of the object that provides the feature can be obtained by get methods in the LibreOffice API. It is common to get an object from a get method. For instance,
getSheets()
is required for every Calc document,getText()
is essential for every Writer Document andgetDrawpages()
is an essential part of every Draw document. After loading a document, these methods are used to get the Sheets, Text and Drawpages object of the corresponding document. Object-specific get methods are an important technique to get objects.
- Non-integral parts: Features which are not considered integral for the architecture of an object are accessible through a set of universal methods. In the LibreOffice API, these features are called properties, and generic methods are used, such as
getPropertyValue(String propertyName)
to access them. In some cases such a non-integral feature is provided as an object, therefore the methodgetPropertyValue()
can be another source for objects. For instance, page styles for spreadsheets have the properties"RightPageHeaderContent"
and"LeftPageHeaderContent"
, that contain objects for the page header sections of a spreadsheet document. The genericgetPropertyValue()
method can sometimes provide an object you need.
Sets of objects
Objects can be elements in a set of similar objects. In sets, to access an object you need to know how to get a particular element from the set. The LibreOffice API allows four ways to provide an element in a set. The first three ways are objects with element access methods that allow access by name, index, or enumeration. The fourth way is a sequence of elements which has no access methods but can be used as an array directly. How these sets of elements are used will be discussed later.
The designer of an object decides which of those opportunities to offer, based on special conditions of the object, such as how it performs remotely or which access methods best work with implementation.
Working with Objects
Working with LibreOffice API objects involves the following:
- First we will learn the UNO concepts of objects, interfaces, services, attributes, and properties, and we will get acquainted with UNO's method of using them.
- After that, we will work with a LibreOffice document for the first time, and give some hints for the usage of the most common types in LibreOffice API.
- Finally we will introduce the common interfaces that allow you to work with text, tables and drawings across all LibreOffice document types.
Objects, Interfaces, and Services
Objects
In UNO, an object is a software artifact that has methods that you can call and attributes that you can get and set. Exactly which methods and attributes an object offers is specified by the set of interfaces it supports.
Interfaces
An interface specifies a set of attributes and methods that together define one single aspect of an object. For instance, the interface com.sun.star.lang.XLocalizable specifies no attributes and the methods getLocale()
and setLocale()
.
com.sun.star.lang.XLocalizable interface - IDL
|
---|
|
To allow for reuse of such interface specifications, an interface can inherit one or more other interfaces (as, for example, XLocalizable inherits all the attributes and methods of com.sun.star.uno.XInterface). Multiple inheritance, the ability to inherit more than one interface, was introduced in OpenOffice.org 2.x.
Strictly speaking, interface attributes are not needed in UNO. Each attribute could also be expressed as a combination of one method to get the attribute's value, and another method to set it (or just one method to get the value for a read-only attribute). However, there are at least two good reasons for the inclusion of interface attributes in UNO: First, the need for such combinations of getting and setting a value seems to be widespread enough to warrant extra support. Second, with attributes, a designer of an interface can better express nuances among the different features of an object. Attributes can be used for those features that are not considered integral or structural parts of an object, while explicit methods are reserved to access the core features.
Historically, a UNO object typically supported a set of many independent interfaces, corresponding to its many different aspects. With multiple-inheritance interfaces, there is less need for this, as an object may now support just one interface that inherits from all the other interfaces that make up the object’s various aspects.
Services
Historically, the term “service” has been used with an unclear meaning in UNO. Starting with OpenOffice.org 2.x, the underlying concepts have been made cleaner. Unfortunately, this leaves two different meanings for the term “service” within UNO. In the following, we will use the term "new-style service" to denote an entity that conforms to the clarified, OpenOffice.org-OpenOffice.org 2.x service concept, while we use "old-style service" to denote an entity that only conforms to the historical, more vague concept. To make matters even more complicated, the term “service” is often used with still different meanings in contexts outside UNO.
Although technically there should no longer be any need for old-style services, the LibreOffice API still uses them extensively to remain backwards compatible. Therefore, be prepared to encounter uses of both service concepts in parallel when working with the LibreOffice API.
A new-style service is of the form
com.sun.star.bridge.XUnoUrlResolver interface - IDL
|
---|
|
and specifies that objects that support a certain interface (for example, com.sun.star.bridge.XUnoUrlResolver) will be available under a certain service name (e.g., "com.sun.star.bridge.UnoUrlResolver") at a component context’s service manager. (Formally, new-style services are called “single-interface–based services.”)
The various UNO language bindings offer special constructs to easily obtain instances of such new-style services, given a suitable component context; see Java Language Binding and C++ Language Binding.
An old-style service (formally called an “accumulation-based service”) is of the form
com.sun.star.bridge.Desktop interface - IDL
|
---|
|
and is used to specify any of the following:
- The general contract is that, if an object is documented to support a certain old-style service, then you can expect that object to support all interfaces exported by the service itself and any inherited services. For example, the method queryFrames() returns a sequence of objects that should all support the old-style service com.sun.star.frame.Frame, and thus all the interfaces exported by Frame.
- Additionally, an old-style service may specify one or more properties, as in
com.sun.star.frame.Frame interface - IDL
|
---|
|
- Properties, which are explained in detail in the following section, are similar to interface attributes, in that they describe additional features of an object. The main difference is that interface attributes can be accessed directly, while the properties of an old-style service are typically accessed via generic interfaces like com.sun.star.beans.XPropertySet. Often, interface attributes are used to represent integral features of an object, while properties represent additional, more volatile features.
- Some old-style services are intended to be available at a component context’s service manager. For example, the service com.sun.star.frame.Desktop can be instantiated at a component context’s service manager under its service name "com.sun.star.frame.Desktop". (The problem is that you cannot tell whether a given old-style service is intended to be available at a component context; using a new-style service instead makes that intent explicit.)
- Other old-style services are designed as generic super-services that are inherited by other services. For example, the service com.sun.star.document.OfficeDocument serves as a generic base for all different sorts of concrete document services, like com.sun.star.text.TextDocument and com.sun.star.drawing.DrawingDocument. (Multiple-inheritance interfaces are now the preferred mechanism to express such generic base services.)
- Yet other old-style services only list properties, and do not export any interfaces at all. Instead of specifying the interfaces supported by certain objects, as the other kinds of old-style services do, such services are used to document a set of related properties. For example, the service com.sun.star.document.MediaDescriptor lists all the properties that can be passed to loadComponentFromURL.
A property is a feature of an object which is typically not considered an integral or structural part of the object and therefore is handled through generic getPropertyValue()
/setPropertyValue()
methods instead of specialized get methods, such as getPrinter()
. Old-style services offer a special syntax to list all the properties of an object. An object containing properties only has to support the com.sun.star.beans.XPropertySet interface to be prepared to handle all kinds of properties. Typical examples are properties for character or paragraph formatting. With properties, you can set multiple features of an object through a single call to setPropertyValues()
, which greatly improves the remote performance. For instance, paragraphs support the setPropertyValues()
method through their com.sun.star.beans.XMultiPropertySet interface.
Using Services
The concepts of interfaces and services were introduced for the following reasons:
Interfaces and services separate specification from implementation
- The specification of an interface or service is abstract, that is, it does not define how objects supporting a certain functionality do this internally. Through the abstract specification of the LibreOffice API, it is possible to pull the implementation out from under the API and install a different implementation if required.
Service names allow to create instances by specification name, not by class names
- In Java or C++ you use the new operator to create a class instance. This approach is restricted: the class you get is hard-coded. You cannot later on exchange it by another class without editing the code. The concept of services solves this. The central object factory in LibreOffice, the global service manager, is asked to create an object that can be used for a certain purpose without defining its internal implementation. This is possible because a service can be ordered from the factory by its service name and the factory decides which service implementation it returns. Which implementation you get makes no difference, you only use the well-defined interface of the service.
Interfaces
Abstract interfaces are more reusable if they are fine-grained, i.e., if they are small and describe only a single aspect of an object. To describe the many aspects of an object, objects can implement more than one of these fine-grained interfaces. Being able to implement multiple interfaces allows similar aspects of similar objects to be accessed with the same code. For example, many objects support text: text may be found in the body of a document, in text frames, in headers and footers, footnotes, table cells, and in drawing shapes. These objects all support the same interface, so a procedure can use, for example, getText() to retrieve text from any of these objects.
Services, interfaces, and methods are illustrated in the figure below for the old-style service com.sun.star.text.TextDocument, shown using UML notation. In this figure, services are shown on the left side. The arrow between services indicates that one service provided by the upper (arrowhead) service are inherited by the lower service. Interfaces exported by these services are shown on the right. All interface names in the LibreOffice API start with an X, so as to be distinguishable from the names of other entities. Each interface contains methods, which are listed beneath the interface.
A TextDocument
object provides the com.sun.star.text.TextDocument service, which implements the interfaces, XTextDocument
, XSearchable
, and XRefreshable
. These interfaces provide, for example, the methods getText()
, for adding text to a document, and findAll()
, for searching the document.
As indicated by the arrow, the com.sun.star.text.TextDocument service also inherits all the interfaces provided by the com.sun.star.document.OfficeDocument service, so these interfaces are also provided to a TextDocument
object. These interfaces handle tasks common to the LibreOffice applications: printing, XPrintable
; storing, XStorable
; modifying, XModifiable
; and model handling, XModel
.
The interfaces shown in the figure are only the mandatory interfaces of a TextDocument
object. A TextDocument
has optional properties and interfaces, among them the properties CharacterCount
, ParagraphCount
and WordCount
, and the interface XPropertySet
, which must be supported if properties are present at all. The implementation of the TextDocument
service in LibreOffice supports both required and all optional interfaces as well. The usage of a TextDocument
is described thoroughly in Text Documents.
C++ and Java require that the interface name be provided when accessing a method. An old-style service may provide several interfaces to keep track of. New-style services are easier to use because, since they have just one interface, the multiple-inheritance interface, all the methods are accessed through the same interface.
Using Interfaces
The fact that every UNO object must be accessed through its interfaces has an effect in languages like Java and C++, where the compiler needs the correct type of an object reference before you can call a method from it. In Java or C++, you normally just cast an object before you access an interface it implements. When working with UNO objects this is different: You must ask the UNO environment to get the appropriate reference for you whenever you want to access methods of an interface which your object supports, but your compiler does not yet know about. Only then you can cast it safely.
The Java UNO environment has a method queryInterface()
for this purpose. It looks complicated at first sight, but once you understand that queryInterface()
is about safe casting of UNO types across process boundaries, you will soon get used to it. Take a look at the second example FirstLoadComponent.java (in the sample directory, if you have installed the SDK on your computer), where a new Desktop object is created and, afterwards, the queryInterface() method is used to get the XComponentLoader interface.
Object desktop = xRemoteServiceManager.createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
XComponentLoader xComponentLoader = (XComponentLoader)
UnoRuntime.queryInterface(XComponentLoader.class, desktop);
We asked the service manager to create a com.sun.star.frame.Desktop using its factory method createInstanceWithContext(). This method is defined to return a Java Object type, which should not surprise you—after all the factory must be able to return any type:
Object createInstanceWithContext(String serviceName, XComponentContext context)
The object we receive is a com.sun.star.frame.Desktop service. The point is, while we know that the object we ordered at the factory is a DesktopUnoUrlResolver and exports among other interfaces the interface XComponentLoader
, the compiler does not. Therefore, we have to use the UNO runtime environment to ask or query for the interface XComponentLoader
, since we want to use the loadComponentFromURL()
method on this interface. The method queryInterface()
makes sure we get a reference that can be cast to the needed interface type, no matter if the target object is a local or a remote object. There are two queryInterface
definitions in the Java UNO language binding:
Object UnoRuntime.queryInterface(java.lang.Class targetInterface, Object sourceObject)
Object UnoRuntime.queryInterface(com.sun.star.uno.Type targetInterface, Object sourceObject)
Since UnoRuntime.queryInterface()
is specified to return a java.lang.Object just like the factory method createInstanceWithContext()
, we still must explicitly cast our interface reference to the needed type. The difference is that after queryInterface()
we can safely cast the object to our interface type and, most important, that the reference will now work even with an object in another process. Here is the queryInterface()
call, explained step by step:
XComponentLoader xComponentLoader = (XComponentLoader)
UnoRuntime.queryInterface(XComponentLoader.class, desktop);
XComponentLoader
is the interface we want to use, so we define a XComponentLoader
variable named xComponentLoader
(lower x) to store the interface we expect from queryInterface
.
Then we query our desktop object for the XComponentLoader
interface, passing in XComponentLoader.class
as target interface and desktop as source object. Finally we cast the outcome to XComponentLoader
and assign the resulting reference to our variable xComponentLoader
.
If the source object does not support the interface we are querying for, queryInterface()
will return null.
In Java, this call to queryInterface()
is necessary whenever you have a reference to an object which is known to support an interface that you need, but you do not have the proper reference type yet. Fortunately, you are not only allowed to queryInterface()
from java.lang.Object
source types, but you may also query an interface from another interface reference, like this:
// loading a blank spreadsheet document gives us its XComponent interface:
XComponent xComponent = xComponentLoader.loadComponentFromURL(
"private:factory/scalc", "_blank", 0, loadProps);
// now we query the interface XSpreadsheetDocument from xComponent
XSpreadsheetDocument xSpreadsheetDocument = UnoRuntime.queryInterface(
XSpreadsheetDocument.class, xComponent);
Furthermore, if a method is defined in such a way that it already returns an interface type, you do not need to query the interface, but you can use its methods right away. In the snippet above, the method loadComponentFromURL
is specified to return an com.sun.star.lang.XComponent interface, so you may call the XComponent
methods addEventListener()
and removeEventListener()
directly at the xComponent
variable, if you want to be notified that the document is being closed.
The corresponding step in C++ is done by a Reference<>
template that takes the source instance as parameter:
// instantiate a sample service with the servicemanager.
Reference< XInterface > rInstance =
rServiceManager->createInstanceWithContext(
OUString::createFromAscii("com.sun.star.frame.Desktop" ),
rComponentContext );
// Query for the XComponentLoader interface
Reference< XComponentLoader > rComponentLoader( rInstance, UNO_QUERY );
In LibreOffice Basic or with Python, querying for interfaces is not necessary; the Basic runtime engine or the embedded Python interpreter take care of that internally.
With the proliferation of multiple-inheritance interfaces in the LibreOffice API, there will be less of a demand to explicitly query for specific interfaces in Java or C++. For example, with the hypothetical interfaces
interface XBase1 {
void fun1();
};
interface XBase2 {
void fun2();
};
interface XBoth { // inherits from both XBase1 and XBase2
interface XBase1;
interface XBase2;
};
interface XFactory {
XBoth getBoth();
};
you can directly call both fun1()
and fun2()
on a reference obtained through XFactory.getBoth()
, without querying for either XBase1
or XBase2
.
Using Properties
An object must offer its properties through interfaces that allow you to work with properties. The most basic form of these interfaces is the interface com.sun.star.beans.XPropertySet. There are other interfaces for properties, such as com.sun.star.beans.XMultiPropertySet, that gets and sets a multitude of properties with a single method call. The XPropertySet
is always supported when properties are present in a service.
In XPropertySet
, two methods carry out the property access, which are defined as follows:
void setPropertyValue(String propertyName, Object propertyValue)
Object getPropertyValue(String propertyName)
void SAL_CALL setPropertyValue( const OUString& aPropertyName, const Any& aValue );
Any SAL_CALL getPropertyValue( const OUString& PropertyName );
def setPropertyValue(propertyName: str, propertyValue: any):
def getPropertyValue(propertyName: str) -> any:
Sub setPropertyValue(propertyName As String, propertyValue As Variant)
Function getPropertyValue(propertyName As String) As Variant
In the FirstLoadComponent example, the XPropertySet
interface was used to set the CellStyle property at a cell object. The cell object was a com.sun.star.sheet.SheetCell
and therefore supports also the com.sun.star.table.CellProperties
service which had a property CellStyle
. The following code explains how this property can be set:
// query the XPropertySet interface from cell object
XPropertySet xCellProps = UnoRuntime.queryInterface(XPropertySet.class, xCell);
// set the CellStyle property
xCellProps.setPropertyValue("CellStyle", "Result");
// query the XPropertySet interface from cell object
Reference<XPropertySet> xCellProps(xCell, UNO_QUERY_THROW);
// set the CellStyle property
xCellProps->setPropertyValue("CellStyle", Any(OUString("Result")));
# set the CellStyle property
xCell.setPropertyValue('CellStyle','Result')
REM set the CellStyle property
xCell.setPropertyValue("CellStyle","Result")
You are now ready to start working with an LibreOffice document.
Example: Working with a Spreadsheet Document
In this example, we will use the simple bootstrap mechanism (com.sun.star.comp.helper.Bootstrap.bootstrap()) to get the remote office context of a running office instance and ask the remote service manager to give us the remote Desktop object and use its loadComponentFromURL() method to create a new spreadsheet document. From the document we get its sheets container where we insert and access a new sheet by name. In the new sheet, we enter values into A1 and A2 and summarize them in A3. The cell style of the summarizing cell gets the cell style Result, so that it appears in italics, bold and underlined. Finally, we make our new sheet the active sheet, so that the user can see it.
The FirstLoadComponent example
This is the FirstLoadComponent
example, available inside the sdk/examples/DevelopersGuide/FirstSteps
folder. Again, you should set up sdk environment in setsdkenv_unix
, and then use make
inside sdk/examples/DevelopersGuide/FirstSteps
folder, and then make FirstLoadComponent.run
.
To run the Python example, you may use the Python bundled with LibreOffice:
/opt/libreoffice7.6/program/python FirstLoadComponent.py
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.uno.XComponentContext;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.container.XEnumeration;
import com.sun.star.container.XEnumerationAccess;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XController;
import com.sun.star.frame.XModel;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.sheet.XCellAddressable;
import com.sun.star.sheet.XCellRangesQuery;
import com.sun.star.sheet.XSheetCellRanges;
import com.sun.star.sheet.XSpreadsheet;
import com.sun.star.sheet.XSpreadsheetDocument;
import com.sun.star.sheet.XSpreadsheetView;
import com.sun.star.sheet.XSpreadsheets;
import com.sun.star.table.XCell;
import com.sun.star.uno.UnoRuntime;
public class FirstLoadComponent {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
// get the remote office component context
XComponentContext xRemoteContext = Bootstrap.bootstrap();
if (xRemoteContext == null) {
System.err.println("ERROR: Could not bootstrap default Office.");
}
XMultiComponentFactory xRemoteServiceManager = xRemoteContext.getServiceManager();
Object desktop = xRemoteServiceManager.createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
XComponentLoader xComponentLoader = UnoRuntime.queryInterface(XComponentLoader.class, desktop);
PropertyValue[] loadProps = new PropertyValue[0];
XComponent xSpreadsheetComponent = xComponentLoader.loadComponentFromURL("private:factory/scalc", "_blank", 0, loadProps);
XSpreadsheetDocument xSpreadsheetDocument = UnoRuntime.queryInterface(XSpreadsheetDocument.class,
xSpreadsheetComponent);
XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();
xSpreadsheets.insertNewByName("MySheet", (short)0);
com.sun.star.uno.Type elemType = xSpreadsheets.getElementType();
System.out.println(elemType.getTypeName());
Object sheet = xSpreadsheets.getByName("MySheet");
XSpreadsheet xSpreadsheet = UnoRuntime.queryInterface(
XSpreadsheet.class, sheet);
XCell xCell = xSpreadsheet.getCellByPosition(0, 0);
xCell.setValue(21);
xCell = xSpreadsheet.getCellByPosition(0, 1);
xCell.setValue(21);
xCell = xSpreadsheet.getCellByPosition(0, 2);
xCell.setFormula("=sum(A1:A2)");
XPropertySet xCellProps = UnoRuntime.queryInterface(
XPropertySet.class, xCell);
xCellProps.setPropertyValue("CellStyle", "Result");
XModel xSpreadsheetModel = UnoRuntime.queryInterface(
XModel.class, xSpreadsheetComponent);
XController xSpreadsheetController = xSpreadsheetModel.getCurrentController();
XSpreadsheetView xSpreadsheetView = UnoRuntime.queryInterface(XSpreadsheetView.class,
xSpreadsheetController);
xSpreadsheetView.setActiveSheet(xSpreadsheet);
// *********************************************************
// example for use of enum types
xCellProps.setPropertyValue("VertJustify",
com.sun.star.table.CellVertJustify.TOP);
// *********************************************************
// example for a sequence of PropertyValue structs
// create an array with one PropertyValue struct, it contains
// references only
loadProps = new PropertyValue[1];
// instantiate PropertyValue struct and set its member fields
PropertyValue asTemplate = new PropertyValue();
asTemplate.Name = "AsTemplate";
asTemplate.Value = Boolean.TRUE;
// assign PropertyValue struct to array of references for PropertyValue
// structs
loadProps[0] = asTemplate;
// load calc file as template
//xSpreadsheetComponent = xComponentLoader.loadComponentFromURL(
// "file:///c:/temp/DataAnalysys.ods", "_blank", 0, loadProps);
// *********************************************************
// example for use of XEnumerationAccess
XCellRangesQuery xCellQuery = UnoRuntime.queryInterface(XCellRangesQuery.class, sheet);
XSheetCellRanges xFormulaCells = xCellQuery.queryContentCells(
(short)com.sun.star.sheet.CellFlags.FORMULA);
XEnumerationAccess xFormulas = xFormulaCells.getCells();
XEnumeration xFormulaEnum = xFormulas.createEnumeration();
while (xFormulaEnum.hasMoreElements()) {
Object formulaCell = xFormulaEnum.nextElement();
xCell = UnoRuntime.queryInterface(XCell.class, formulaCell);
XCellAddressable xCellAddress = UnoRuntime.queryInterface(XCellAddressable.class, xCell);
System.out.println("Formula cell in column " +
xCellAddress.getCellAddress().Column
+ ", row " + xCellAddress.getCellAddress().Row
+ " contains " + xCell.getFormula());
}
}
catch (java.lang.Exception e){
e.printStackTrace();
}
finally {
System.exit( 0 );
}
}
}
import officehelper
import sys
import traceback
from com.sun.star.sheet.CellFlags import FORMULA
def main():
try:
remote_context = officehelper.bootstrap()
if remote_context is None:
print("ERROR: Could not bootstrap default Office.")
sys.exit(1)
srv_mgr = remote_context.getServiceManager()
desktop = srv_mgr.createInstanceWithContext("com.sun.star.frame.Desktop", remote_context)
spreadsheet_component = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, tuple())
spreadsheets = spreadsheet_component.getSheets()
spreadsheets.insertNewByName("MySheet", 0)
elem_type = spreadsheets.getElementType()
print(elem_type)
sheet = spreadsheets.getByName("MySheet")
cell = sheet.getCellByPosition(0, 0)
cell.setValue(21)
cell = sheet.getCellByPosition(0, 1)
cell.setValue(21)
cell = sheet.getCellByPosition(0, 2)
cell.setFormula("=sum(A1:A2)")
cell.setPropertyValue("CellStyle", "Result")
spreadsheet_controller = spreadsheet_component.getCurrentController()
spreadsheet_controller.setActiveSheet(sheet)
cell.setPropertyValue("VertJustify", "com.sun.star.table.CellVertJustify.TOP")
formula_cells = sheet.queryContentCells(FORMULA)
formulas = formula_cells.getCells()
formula_enum = formulas.createEnumeration()
while formula_enum.hasMoreElements():
formula_cell = formula_enum.nextElement()
print("Formula cell in column " + str(formula_cell.getCellAddress().Column)
+ ", row " + str(formula_cell.getCellAddress().Row)
+ " contains " + cell.getFormula())
except Exception as e:
print(e)
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()
#include <iostream>
#include <sal/main.h>
#include <cppuhelper/bootstrap.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/frame/XComponentLoader.hpp>
#include <com/sun/star/frame/XController.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/sheet/CellFlags.hpp>
#include <com/sun/star/sheet/XCellAddressable.hpp>
#include <com/sun/star/sheet/XCellRangesQuery.hpp>
#include <com/sun/star/sheet/XSheetCellRanges.hpp>
#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
#include <com/sun/star/sheet/XSpreadsheet.hpp>
#include <com/sun/star/sheet/XSpreadsheets.hpp>
#include <com/sun/star/sheet/XSpreadsheetView.hpp>
#include <com/sun/star/table/CellVertJustify.hpp>
#include <com/sun/star/table/XCell.hpp>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/Type.hxx>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/uno/XInterface.hpp>
using namespace rtl;
using namespace cppu;
using namespace css::uno;
using namespace css::lang;
using namespace css::frame;
using namespace css::container;
using namespace css::sheet;
using namespace css::beans;
using namespace css::table;
SAL_IMPLEMENT_MAIN()
{
try
{
Reference<XComponentContext> xRemoteContext = bootstrap();
if (!xRemoteContext.is())
{
std::cerr << "ERROR: Could not bootstrap default Office.\n";
return 1;
}
Reference<XMultiComponentFactory> xRemoteServiceManager
= xRemoteContext->getServiceManager();
Reference<XInterface> desktop = xRemoteServiceManager->createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
Reference<XComponentLoader> xComponentLoader
= Reference<XComponentLoader>(desktop, UNO_QUERY);
Sequence<PropertyValue> loadProps(0);
Reference<XComponent> xSpreadsheetComponent = xComponentLoader->loadComponentFromURL(
"private:factory/scalc", "_blank", 0, loadProps);
Reference<XSpreadsheetDocument> xSpreadsheetDocument(xSpreadsheetComponent, UNO_QUERY);
Reference<XSpreadsheets> xSpreadsheets = xSpreadsheetDocument->getSheets();
xSpreadsheets->insertNewByName("MySheet", (sal_Int16)0);
Type elemType = xSpreadsheets->getElementType();
std::cout << elemType.getTypeName() << std::endl;
Any sheet = xSpreadsheets->getByName("MySheet");
Reference<XSpreadsheet> xSpreadsheet(sheet, UNO_QUERY);
Reference<XCell> xCell = xSpreadsheet->getCellByPosition(0, 0);
xCell->setValue(21);
xCell = xSpreadsheet->getCellByPosition(0, 1);
xCell->setValue(21);
xCell = xSpreadsheet->getCellByPosition(0, 2);
xCell->setFormula("=sum(A1:A2)");
Reference<XPropertySet> xCellProps(xCell, UNO_QUERY);
xCellProps->setPropertyValue("CellStyle", Any(OUString("Result")));
Reference<XModel> xSpreadsheetModel(xSpreadsheetComponent, UNO_QUERY);
Reference<XController> xSpreadsheetController = xSpreadsheetModel->getCurrentController();
Reference<XSpreadsheetView> xSpreadsheetView(xSpreadsheetController, UNO_QUERY);
xSpreadsheetView->setActiveSheet(xSpreadsheet);
// *********************************************************
// example for use of enum types
xCellProps->setPropertyValue("VertJustify", Any(CellVertJustify_TOP));
// *********************************************************
// example for a sequence of PropertyValue structs
// create an array with one PropertyValue struct, it contains
// references only
loadProps.realloc(1);
// instantiate PropertyValue struct and set its member fields
PropertyValue asTemplate;
asTemplate.Name = "AsTemplate";
asTemplate.Value = makeAny(true);
// assign PropertyValue struct to array of references for PropertyValue
// structs
loadProps[0] = asTemplate;
// load calc file as a template
// xSpreadsheetComponent = xComponentLoader->loadComponentFromURL(
// "file:///c:/temp/DataAnalysys.ods", "_blank", 0, loadProps);
// *********************************************************
// example for use of XEnumerationAccess
Reference<XCellRangesQuery> xCellQuery(sheet, UNO_QUERY);
Reference<XSheetCellRanges> xFormulaCells
= xCellQuery->queryContentCells((sal_Int16)CellFlags::FORMULA);
Reference<XEnumerationAccess> xFormulas = xFormulaCells->getCells();
Reference<XEnumeration> xFormulaEnum = xFormulas->createEnumeration();
while (xFormulaEnum->hasMoreElements())
{
Reference<XCell> formulaCell(xFormulaEnum->nextElement(), UNO_QUERY);
Reference<XCellAddressable> xCellAddress(formulaCell, UNO_QUERY);
if (xCellAddress.is())
{
std::cout << "Formula cell in column " << xCellAddress->getCellAddress().Column
<< ", row " << xCellAddress->getCellAddress().Row << " contains "
<< formulaCell->getFormula() << std::endl;
}
}
}
catch (RuntimeException& e)
{
std::cerr << e.Message << "\n";
return 1;
}
return 0;
}
Sub Main
desktop = createUnoService("com.sun.star.frame.Desktop")
Dim args()
spreadsheet_component = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, args())
spreadsheets = spreadsheet_component.getSheets()
spreadsheets.insertNewByName("MySheet", 0)
elem_type = spreadsheets.getElementType()
Msgbox(elem_type.Name)
sheet = spreadsheets.getByName("MySheet")
cell = sheet.getCellByPosition(0, 0)
cell.setValue(21)
cell = sheet.getCellByPosition(0, 1)
cell.setValue(21)
cell = sheet.getCellByPosition(0, 2)
cell.setFormula("=sum(A1:A2)")
cell.setPropertyValue("CellStyle", "Result")
spreadsheet_controller = spreadsheet_component.getCurrentController()
spreadsheet_controller.setActiveSheet(sheet)
cell.setPropertyValue("VertJustify", "com.sun.star.table.CellVertJustify.TOP")
formula_cells = sheet.queryContentCells(com.sun.star.sheet.CellFlags.FORMULA)
formulas = formula_cells.getCells()
formula_enum = formulas.createEnumeration()
Do while formula_enum.hasMoreElements()
formula_cell = formula_enum.nextElement()
Msgbox("Formula cell in column " + formula_cell.getCellAddress().Column + _
", row " + formula_cell.getCellAddress().Row + _
" contains " + cell.getFormula())
Loop
End Sub
Execution from LibreOffice suite
XSCRIPTCONTEXT builtin object provides a proper execution context for macros that use Python, JavaScript or BeanShell languages. uno.py module comes as an alternative to XSCRIPTCONTEXT for Python scripts.
Python macros can be written in order to be called either using Python embedded interpreter, either using development features such as code completion, syntax highliting, debugging, version control to name a few. DualComponentLoader
extends from FirtLoadComponent
to illustrate this:
REM Same as above FirstLoadComponent example
import uno
import sys
import traceback
from com.sun.star.sheet.CellFlags import FORMULA
def main(ctx=None):
try:
if ctx is None: # Execution triggered via Office client
ctx = uno.getComponentContext()
if ctx is None:
print("ERROR: Python UNO runtime is absent.")
sys.exit(1)
srv_mgr = ctx.getServiceManager()
desktop = srv_mgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
spreadsheet_component = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, tuple())
spreadsheets = spreadsheet_component.getSheets()
spreadsheets.insertNewByName("MySheet", 0)
elem_type = spreadsheets.getElementType()
print(elem_type)
sheet = spreadsheets.getByName("MySheet")
cell = sheet.getCellByPosition(0, 0)
cell.setValue(21)
cell = sheet.getCellByPosition(0, 1)
cell.setValue(21)
cell = sheet.getCellByPosition(0, 2)
cell.setFormula("=sum(A1:A2)")
cell.setPropertyValue("CellStyle", "Result")
spreadsheet_controller = spreadsheet_component.getCurrentController()
spreadsheet_controller.setActiveSheet(sheet)
cell.setPropertyValue("VertJustify", "com.sun.star.table.CellVertJustify.TOP")
formula_cells = sheet.queryContentCells(FORMULA)
formulas = formula_cells.getCells()
formula_enum = formulas.createEnumeration()
while formula_enum.hasMoreElements():
formula_cell = formula_enum.nextElement()
print("Formula cell in column " + str(formula_cell.getCellAddress().Column)
+ ", row " + str(formula_cell.getCellAddress().Row)
+ " contains " + cell.getFormula())
except Exception as e:
print(e)
traceback.print_exc()
sys.exit(1)
# Entry point for LibreOffice client via
# Tools - Macros - Run macro... menu
g_exportedScripts = (main,)
# Entry point for Python console or Python IDEs
if __name__ == "__main__":
import officehelper
remote_context = officehelper.bootstrap()
if remote_context is None:
print("ERROR: Could not bootstrap default Office.")
sys.exit(1)
main(remote_context)
/* TBD
*/
/* TBD
*/
Complete code decoupling when running Python scripts from an IDE of from LibreOffice can be achieved using IDE_utils guidelines.
Python console is inhibited Under Windows, therefore DualComponentLoader
print()
outputs require to follow Windows' console recommendation.
Common Types
Until now, literals and common Java types for method parameters and return values have been used as if the LibreOffice API was made for Java. However, it is important to understand that UNO is designed to be language independent and therefore has its own set of types which have to be mapped to the proper types for your language binding. The type mappings are briefly described in this section. Refer to Documentation/DevGuide/Professional UNO for detailed information about type mappings.
Basic Types
The basic UNO types (where the term “basic” has nothing to do with LibreOffice Basic) occur as members of structs, as method return types or method parameters. The following table shows the basic UNO types and, if available, their exact mappings to Java, C++, and LibreOffice Basic types.
UNO | Type description | Java | C++ | Basic |
---|---|---|---|---|
void | empty type, used only as method return type and in any |
void | void | - |
boolean | Boolean type; true and false | boolean | bool (sal_Bool is deprecated) |
Boolean |
byte | signed 8-bit integer type | byte | sal_Int8 | Integer |
short | signed 16-bit integer type | short | sal_Int16 | Integer |
unsigned short | unsigned 16-bit integer type (deprecated) | - | sal_uInt16 | - |
long | signed 32-bit integer type | int | sal_Int32 | Long |
unsigned long | unsigned 32-bit integer type (deprecated) | - | sal_uInt32 | - |
hyper | signed 64-bit integer type | long | sal_Int64 | - |
unsigned hyper | unsigned 64-bit integer type (deprecated) | - | sal_uInt64 | - |
float | IEC 60559 single precision floating point type | float | float (if appropriate) | Single |
double | IEC 60559 double precision floating point type | double | double (if appropriate) | Double |
char | 16-bit Unicode character type (more precisely: UTF-16 code units)- | char | sal_Unicode | - |
There are special conditions for types that do not have an exact mapping in this table. Check for details about these types in the corresponding sections about type mappings in UNO Language Bindings.
Strings
UNO considers strings to be simple types, but since they need special treatment in some environments, we discuss them separately here.
UNO | Description | Java | C++ | Basic |
---|---|---|---|---|
string | Unicode string type (more precisely: strings of Unicode scalar values) | java.lang.String | rtl::OUString | String |
In Java, use UNO strings as if they were native java.lang.String
objects.
In C++, native char
string literals need to be converted to UNO Unicode strings by using _ustr
postfix.
In Basic, Basic strings are mapped to UNO strings transparently.
String aStr = "abcd";
OUString aStr = u"abcd"_ustr;
aStr = "abcd"
aStr = "abcd"
Enum Types and Groups of Constants
The LibreOffice API uses many enumeration types (called enums) and groups of constants (called constant groups). Enums are used to list every plausible value in a certain context. The constant groups define possible values for properties, parameters, return values and struct members.
For example, there is an enum com.sun.star.table.CellVertJustify that describes the possible values for the vertical adjustment of table cell content. The vertical adjustment of table cells is determined by their property VertJustify. The possible values for this property are, according to CellVertJustify
, the values STANDARD
, TOP
, CENTER
and BOTTOM
.
// adjust a cell content to the upper cell border
// The service com.sun.star.table.Cell includes the service com.sun.star.table.CellProperties
// and therefore has a property VertJustify that controls the vertical cell adjustment
// we have to use the XPropertySet interface of our Cell to set it
xCellProps.setPropertyValue("VertJustify", com.sun.star.table.CellVertJustify.TOP);
LibreOffice Basic understands enumeration types and constant groups. Their usage is straightforward:
'LibreOffice Basic
oCellProps.VertJustify = com.sun.star.table.CellVertJustify.TOP
In C++ enums and constant groups are used with the scope operator ::
//C++
rCellProps->setPropertyValue(OUString::createFromAscii( "VertJustify"),
::com::sun::star::table::CellVertJustify.TOP);
Struct
Structs in the LibreOffice API are used to create compounds of other UNO types. They correspond to C structs or Java classes consisting of public member variables only.
While structs do not encapsulate data, they are easier to transport as a whole, instead of marshaling get()
and set()
calls back and forth. In particular, this has advantages for remote communication.
You gain access to struct members through the . (dot) operator as in
aProperty.Name = "ReadOnly";
In Java, C++ and LibreOffice Basic, the keyword new
instantiates structs. In OLE automation, use com.sun.star.reflection.CoreReflection to get a UNO struct. Do not use the service manager to create structs.
//In Java:
com.sun.star.beans.PropertyValue aProperty
= new com.sun.star.beans.PropertyValue();
'In LibreOffice Basic
Dim aProperty as new com.sun.star.beans.PropertyValue
Any
The LibreOffice API frequently uses an any
type, which is the counterpart of the Variant
type known from other environments. The any
type holds one arbitrary UNO type. The any
type is especially used in generic UNO interfaces.
Examples for the occurrence of any are the method parameters and return values of the following, frequently used methods:
Interface | returning an any type | taking an any type | |
---|---|---|---|
XPropertySet | any getPropertyValue(string propertyName) | void setPropertyValue(any value) | |
XNameContainer | any getByName(string name) | void replaceByName(string name, any element) | void insertByName(string name, any element) |
XIndexContainer | any getByIndex(long index) | void replaceByIndex(long index, any element) | void insertByIndex(long index, any element) |
XEnumeration | any nextElement() | - |
The any
type also occurs in the com.sun.star.beans.PropertyValue struct.
This struct
has two member variables, Name
and Value
, and is ubiquitous in sets of PropertyValue
structs, where every PropertyValue
is a name-value pair that describes a property by name and value. If you need to set the value of such a PropertyValue struct
, you must assign an any
type, and you must be able to interpret the contained any
, if you are reading from a PropertyValue
. How this is done depends on your language.
In Java, the any
type is mapped to java.lang.Object
, but there is also a special Java class com.sun.star.uno.Any
, mainly used in those cases where a plain Object
would be ambiguous. There are two simple rules of thumb to follow:
- 1. When you are supposed to pass in an
any
value, always pass in ajava.lang.Object
or a Java UNO object.
For instance, if you use setPropertyValue()
to set a property that has a non-interface type in the target object, you must pass in a java.lang.Object
for the new value. If the new value is of a primitive type in Java, use the corresponding Object
type for the primitive type:
xCellProps.setPropertyValue("CharWeight", new Double(200.0));
Another example would be a PropertyValue
struct you want to use for loadComponentFromURL
:
com.sun.star.beans.PropertyValue aProperty = new com.sun.star.beans.PropertyValue();
aProperty.Name = "ReadOnly";
aProperty.Value = Boolean.TRUE;
- 2. When you receive an
any
instance, always use thecom.sun.star.uno.AnyConverter
to retrieve its value.
The AnyConverter
requires a closer look. For instance, if you want to get a property which contains a primitive Java type, you must be aware that getPropertyValue()
returns a java.lang.Object
containing your primitive type wrapped in an any value. The com.sun.star.uno.AnyConverter
is a converter for such objects. Actually it can do more than just conversion, you can find its specification in the Java UNO reference. The following list sums up the conversion functions in the AnyConverter
:
static Object toArray(Object object)
static boolean toBoolean(Object object)
static byte toByte(Object object)
static char toChar(Object object)
static double toDouble(Object object)
static float toFloat(Object object)
static int toInt(Object object)
static long toLong(Object object)
static Object toObject(Class clazz, Object object)
static Object toObject(Type type, Object object)
static short toShort(Object object)
static java.lang.String toString(Object object)
static Type toType(Object object)
static int toUnsignedInt(Object object)
static long toUnsignedLong(Object object)
static short toUnsignedShort(Object object)
Its usage is straightforward:
import com.sun.star.uno.AnyConverter;
long cellColor = AnyConverter.toLong(xCellProps.getPropertyValue("CharColor"));
For convenience, for interface types you can directly use UnoRuntime.queryInterface()
without first calling AnyConverter.getObject()
:
import com.sun.star.uno.AnyConverter;import com.sun.star.uno.UnoRuntime;
Object ranges = xSpreadsheet.getPropertyValue("NamedRanges");
XNamedRanges ranges1 = UnoRuntime.queryInterface(
XNamedRanges.class, AnyConverter.toObject(XNamedRanges.class, r));
XNamedRanges ranges2 = UnoRuntime.queryInterface(XNamedRanges.class, r);
In LibreOffice Basic, the any
type becomes a Variant:
'LibreOffice Basic
Dim cellColor as Variant
cellColor = oCellProps.CharColor
In C++, there are special operators for the any
type:
//C++ has >>= and <<= for Any (the pointed brackets are always left)
Any any = rCellProps->getPropertyValue(OUString::createFromAscii( "CharColor" ));
// extract the value from any
sal_Int32 cellColor;
if (any >>= cellColor)
{
// extracted successfully (it would fail if e.g. the any contained a struct, not a long)
}
Sequence
A sequence is a homogeneous collection of values of one UNO type with a variable number of elements. Sequences map to arrays in most current language bindings. Although such collections are sometimes implemented as objects with element access methods in UNO (e.g., via the com.sun.star.container.XEnumeration interface), there is also the sequence type, to be used where remote performance matters. Sequences are always written with pointed brackets in the API reference:
// a sequence of strings is notated as follows in the API reference
sequence< string > aStringSequence;
In Java, you treat sequences as arrays. (But do not use null
for empty sequences, use arrays created via new
and with a length of zero instead.) Furthermore, keep in mind that you only create an array of references when creating an array of Java objects, the actual objects are not allocated. Therefore, you must use new
to create the array itself, then you must again use new
for every single object and assign the new objects to the array.
An empty sequence of PropertyValue
structs is frequently needed for loadComponentFromURL
:
// create an empty array of PropertyValue structs for loadComponentFromURL
PropertyValue[] emptyProps = new PropertyValue[0];
A sequence of PropertyValue
structs is needed to use loading parameters with loadComponentFromURL()
. The possible parameter values for loadComponentFromURL()
and the com.sun.star.frame.XStorable interface can be found in the service com.sun.star.document.MediaDescriptor.
// create an array with one PropertyValue struct for loadComponentFromURL, it contains references only
PropertyValue[] loadProps = new PropertyValue[1];
// instantiate PropertyValue struct and set its member fields
PropertyValue asTemplate = new PropertyValue();
asTemplate.Name = "AsTemplate";
asTemplate.Value = Boolean.TRUE;
// assign PropertyValue struct to first element in our array of references to PropertyValue structs
loadProps[0] = asTemplate;
// load calc file as template
XComponent xSpreadsheetComponent = xComponentLoader.loadComponentFromURL(
"file:///X:/share/samples/english/spreadsheets/OfficeSharingAssoc.sxc",
"_blank", 0, loadProps);
In LibreOffice Basic, a simple Dim
creates an empty array.
'LibreOffice Basic
Dim loadProps() 'empty array
A sequence of structs is created using new together with Dim
.
'LibreOffice Basic
Dim loadProps(0) as new com.sun.star.beans.PropertyValue 'one PropertyValue
In C++, there is a class template for sequences. An empty sequence can be created by omitting the number of elements required.
//C++
Sequence< ::com::sun::star::beans::PropertyValue > loadProps; // empty sequence
If you pass a number of elements, you get an array of the requested length.
//C++
Sequence< ::com::sun::star::beans::PropertyValue > loadProps( 1 );
// the structs are default constructed
loadProps[0].Name = OUString::createFromAscii( "AsTemplate" );
loadProps[0].Handle <<= true;
Reference< XComponent > rComponent = rComponentLoader->loadComponentFromURL(
OUString::createFromAscii("private:factory/swriter"),
OUString::createFromAscii("_blank"),
0,
loadProps);
Element Access
We have already seen in the section How to get Objects in LibreOffice that sets of objects can also be provided through element access methods. The three most important kinds of element access interfaces are com.sun.star.container.XNameContainer, com.sun.star.container.XIndexContainer and com.sun.star.container.XEnumeration.
The three element access interfaces are examples of how the fine-grained interfaces of the LibreOffice API allow consistent object design.
All three interfaces inherit from XElementAccess
; therefore, they include the methods
type getElementType()
boolean hasElements()
for finding out basic information about a set of elements. The method hasElements()
tells whether or not a set contains any elements at all; the method getElementType()
tells which type a set contains. In Java and C++, you can get information about a UNO type through com.sun.star.uno.Type
, cf, the Java UNO and the C++ UNO reference.
The com.sun.star.container.XIndexContainer and com.sun.star.container.XNameContainer interface have a parallel design. Consider both interfaces in UML notation.
The XIndexAccess/XNameAccess
interfaces are about getting an element. The XIndexReplace/XNameReplace
interfaces allow you to replace existing elements without changing the number of elements in the set, whereas the XIndexContainer/XNameContainer
interfaces allow you to increase and decrease the number of elements by inserting and removing elements.
Many sets of named or indexed objects do not support the whole inheritance hierarchy of XIndexContainer
or XNameContainer
, because the capabilities added by every subclass are not always logical for any set of elements.
The XEumerationAccess
interface works differently from named and indexed containers below the XElementAccess
interface. XEnumerationAccess
does not provide single elements like XNameAccess
and XIndexAccess
, but it creates an enumeration of objects which has methods to go to the next element as long as there are more elements.
Sets of objects sometimes support all element access methods, some also support only name, index, or enumeration access. Always look up the various types in the API reference to see which access methods are available.
For instance, the method getSheets()
at the interface com.sun.star.sheet.XSpreadsheetDocument is specified to return a com.sun.star.sheet.XSpreadsheets interface inherited from XNameContainer
. In addition, the API reference tells you that the provided object supports the com.sun.star.sheet.Spreadsheets service, which defines additional element access interfaces besides XSpreadsheets
.
Examples that show how to work with XNameAccess
, XIndexAccess
, and XEnumerationAccess
are provided below.
Name Access
The basic interface which hands out elements by name is the com.sun.star.container.XNameAccess interface. It has three methods:
any getByName( [in] string name)
sequence< string > getElementNames()
boolean hasByName( [in] string name)
In the FirstLoadComponent.java example above, the method getSheets()
returned a com.sun.star.sheet.XSpreadsheets interface, which inherits from XNameAccess
. Therefore, you could use getByName()
to obtain the sheet "MySheet" by name from the XSpreadsheets
container:
XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();
Object sheet = xSpreadsheets.getByName("MySheet");
XSpreadsheet xSpreadsheet = UnoRuntime.queryInterface(XSpreadsheet.class, sheet);
// use XSpreadsheet interface to get the cell A1 at position 0,0 and enter 42 as value
XCell xCell = xSpreadsheet.getCellByPosition(0, 0);
Since getByName()
returns an any, you have to use AnyConverter.toObject()
and/or UnoRuntime.queryInterface()
before you can call methods at the spreadsheet object.
Index Access
The interface which hands out elements by index is the com.sun.star.container.XIndexAccess interface. It has two methods:
any getByIndex( [in] long index)
long getCount()
The FirstLoadComponent example allows to demonstrate XIndexAccess
. The API reference tells us that the service returned by getSheets()
is a com.sun.star.sheet.Spreadsheet service and supports not only the interface com.sun.star.sheet.XSpreadsheets, but XIndexAccess
as well. Therefore, the sheets could have been accessed by index and not just by name by performing a query for the XIndexAccess
interface from our xSpreadsheets
variable:
XIndexAccess xSheetIndexAccess = UnoRuntime.queryInterface(XIndexAccess.class, xSpreadsheets);
Object sheet = XSheetIndexAccess.getByIndex(0);
Enumeration Access
The interface com.sun.star.container.XEnumerationAccess creates enumerations that allow traveling across a set of objects. It has one method: com.sun.star.container.XEnumeration createEnumeration()
.
The enumeration object gained from createEnumeration()
supports the interface com.sun.star.container.XEnumeration. With this interface we can keep pulling elements out of the enumeration as long as it has more elements. XEnumeration
supplies the methods boolean hasMoreElements()
and any nextElement()
which are meant to build loops such as:
while (xCells.hasMoreElements()) {
Object cell = xCells.nextElement();
// do something with cell
}
For example, in spreadsheets you have the opportunity to find out which cells contain formulas. The resulting set of cells is provided as XEnumerationAccess
.
The interface that queries for cells with formulas is com.sun.star.sheet.XCellRangesQuery, it defines (among others) a method XSheetCellRanges queryContentCells(short cellFlags)
which queries for cells having content as defined in the constants group com.sun.star.sheet.CellFlags. One of these cell flags is FORMULA
. From queryContentCells()
we receive an object with an com.sun.star.sheet.XSheetCellRanges interface, which has these methods: XEnumerationAccess getCells()
, String getRangeAddressesAsString()
and sequence< com.sun.star.table.CellRangeAddress > getRangeAddresses()
.
The method getCells()
can be used to list all formula cells and the containing formulas in the spreadsheet document from our FirstLoadComponent example, utilizing XEnumerationAccess
.
XCellRangesQuery xCellQuery = UnoRuntime.queryInterface(XCellRangesQuery.class, sheet);
XSheetCellRanges xFormulaCells = xCellQuery.queryContentCells(
(short)com.sun.star.sheet.CellFlags.FORMULA);
XEnumerationAccess xFormulas = xFormulaCells.getCells();
XEnumeration xFormulaEnum = xFormulas.createEnumeration();
while (xFormulaEnum.hasMoreElements()) {
Object formulaCell = xFormulaEnum.nextElement();
// do something with formulaCell
xCell = UnoRuntime.queryInterface(XCell.class, formulaCell);
XCellAddressable xCellAddress = UnoRuntime.queryInterface(
XCellAddressable.class, xCell);
System.out.print("Formula cell in column " + xCellAddress.getCellAddress().Column
+ ", row " + xCellAddress.getCellAddress().Row
+ " contains " + xCell.getFormula());
}
How Do I Know Which Type I Have?
A common problem is deciding what capabilities an object really has, after you receive it from a method. By observing the code completion in Java IDE, you can discover the base interface of an object returned from a method. You will notice that loadComponentFromURL() returns a com.sun.star.lang.XComponent.
By pressing Alt + F1 in the NetBeans IDE you can read specifications about the interfaces and services you are using.
However, methods can only be specified to return one interface type. The interface you get from a method very often supports more interfaces than the one that is returned by the method (especially when the design of those interfaces predates the availability of multiple-inheritance interface types in UNO). Furthermore, the interface does not tell anything about the properties the object contains.
Therefore you should use this manual to get an idea how things work. Then start writing code, using the code completion and the API reference.
In addition, you can try the InstanceInspector, a Java tool which is part of the LibreOffice SDK examples. It is a Java component that can be registered with the office and shows interfaces and properties of the object you are currently working with.
In LibreOffice Basic, you can inspect objects using the following Basic properties.
sub main
oDocument = thiscomponent
msgBox(oDocument.dbg_methods)
msgBox(oDocument.dbg_properties)
msgBox(oDocument.dbg_supportedInterfaces)
end sub
For a complex object, these msgBox calls will run right off the screen. Try the following, instead:
sub main
oDocument = thiscomponent
GlobalScope.BasicLibraries.LoadLibrary( "Tools" )
Call Tools.WritedbgInfo(oDocument)
end sub
This produces a new Writer document, containing the retrieved information.
Using these DBG properties is a very crude method to discover the contents of an API objects. Use instead Xray tool or MRI tool.
Example: Hello Text, Hello Table, Hello Shape
The goal of this section is to give a brief overview of those mechanisms in the LibreOffice API that are common to all document types. The three main application areas of LibreOffice are text, tables and drawing shapes. The point is: texts, tables and drawing shapes can occur in all three document types, no matter if you are dealing with a Writer, Calc or Draw/Impress file, but they are treated in the same manner everywhere. When you master the common mechanisms, you will be able to insert and use texts, tables and drawings in all document types.
Common Mechanisms for Text, Tables and Drawings
We want to stress the common ground, therefore we start with the common interfaces and properties that allow to manipulate existing texts, tables and drawings. Afterwards we will demonstrate the different techniques to create text, table and drawings in each document type.
The key interfaces and properties to work with existing texts, tables and drawings are the following:
For text the interface com.sun.star.text.XText contains the methods that change the actual text and other text contents (examples for text contents besides conventional text paragraphs are text tables, text fields, graphic objects and similar things, but such contents are not available in all contexts). When we talk about text here, we mean any text - text in text documents, text frames, page headers and footers, table cells or in drawing shapes. XText
is the key for text everywhere in LibreOffice.
The interface com.sun.star.text.XText has the ability to set or get the text as a single string, and to locate the beginning and the end of a text. Furthermore, XText
can insert strings at an arbitrary position in the text and create text cursors to select and format text. Finally, XText
handles text contents through the methods insertTextContent
and removeTextContent
, although not all texts accept text contents other than conventional text. In fact, XText
covers all this by inheriting from com.sun.star.text.XSimpleText that is inherited from com.sun.star.text.XTextRange.
Text formatting happens through the properties which are described in the services com.sun.star.style.ParagraphProperties and com.sun.star.style.CharacterProperties.
The following example method manipulateText()
adds text, then it uses a text cursor to select and format a few words using CharacterProperties
, afterwards it inserts more text. The method manipulateText()
only contains the most basic methods of XText
so that it works with every text object. In particular, it avoids insertTextContent()
, since there are no text contents except for conventional text that can be inserted in all text objects.
protected void manipulateText(XText xText) throws com.sun.star.uno.Exception {
// simply set whole text as one string
xText.setString("He lay flat on the brown, pine-needled floor of the forest, "
+ "his chin on his folded arms, and high overhead the wind blew in the tops "
+ "of the pine trees.");
// create text cursor for selecting and formatting
XTextCursor xTextCursor = xText.createTextCursor();
XPropertySet xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, xTextCursor);
// use cursor to select "He lay" and apply bold italic
xTextCursor.gotoStart(false);
xTextCursor.goRight((short)6, true);
// from CharacterProperties
xCursorProps.setPropertyValue("CharPosture",
com.sun.star.awt.FontSlant.ITALIC);
xCursorProps.setPropertyValue("CharWeight",
new Float(com.sun.star.awt.FontWeight.BOLD));
// add more text at the end of the text using insertString
xTextCursor.gotoEnd(false);
xText.insertString(xTextCursor, " The mountainside sloped gently where he lay; "
+ "but below it was steep and he could see the dark of the oiled road "
+ "winding through the pass. There was a stream alongside the road "
+ "and far down the pass he saw a mill beside the stream and the falling water "
+ "of the dam, white in the summer sunlight.", false);
// after insertString the cursor is behind the inserted text, insert more text
xText.insertString(xTextCursor, "\n \"Is that the mill?\" he asked.", false);
}
def manipulateText(self, xtext) -> None:
"""Insert text content
:param xtext: object that implements com.sun.star.text.XText interface.
"""
# simply set whole text as one string
xtext.String = (
"He lay flat on the brown, pine-needled floor of the forest, "
"his chin on his folded arms, and high overhead the wind blew "
"in the tops of the pine trees."
)
# create text cursor for selecting and formatting
text_cursor = xtext.createTextCursor()
# use cursor to select "He lay" and apply bold italic
text_cursor.gotoStart(False)
text_cursor.goRight(6, True)
# from CharacterProperties
text_cursor.CharPosture = ITALIC
text_cursor.CharWeight = BOLD
# add more text at the end of the text using insertString
text_cursor.gotoEnd(False)
content = (
" The mountainside sloped gently where he lay; "
"but below it was steep and he could see the dark of the oiled "
"road winding through the pass. There was a stream alongside the "
"road and far down the pass he saw a mill beside the stream and "
"the falling water of the dam, white in the summer sunlight."
)
xtext.insertString(text_cursor, content, False)
# after insertString the cursor is behind the inserted text,
# insert more text
content = "\n \"Is that the mill?\" he asked."
xtext.insertString(text_cursor, content, False)
Sub manipulateText(xtext As Object)
' Insert text content
'param xtext: object that implements com.sun.star.text.XText interface.
' simply set whole text as one string
xtext.String = "He lay flat on the brown, pine-needled floor of the forest, " +_
"his chin on his folded arms, and high overhead the wind blew " +_
"in the tops of the pine trees."
' create text cursor for selecting and formatting
text_cursor = xtext.createTextCursor()
' use cursor to select "He lay" and apply bold italic
text_cursor.gotoStart(False)
text_cursor.goRight(6, True)
' from CharacterProperties
text_cursor.CharPosture = com.sun.star.awt.FontSlant.ITALIC
text_cursor.CharWeight = 150
' add more text at the end of the text using insertString
text_cursor.gotoEnd(False)
content = _
" The mountainside sloped gently where he lay; " +_
"but below it was steep and he could see the dark of the oiled " +_
"road winding through the pass. There was a stream alongside the " +_
"road and far down the pass he saw a mill beside the stream and " +_
"the falling water of the dam, white in the summer sunlight."
xtext.insertString(text_cursor, content, False)
' after insertString the cursor is behind the inserted text,
' insert more text
content = CHR$(10) & " ""Is that the mill?"" he asked."
xtext.insertString(text_cursor, content, False)
End Sub
void manipulateText(const Reference<XText>& xText)
{
// simply set whole text as one string
xText->setString("He lay flat on the brown, pine-needled floor of the forest, "
"his chin on his folded arms, and high overhead the wind blew in the tops "
"of the pine trees.");
// create text cursor for selecting and formatting
Reference<XTextCursor> xTextCursor = xText->createTextCursor();
Reference<XPropertySet> xCursorProps(xTextCursor, UNO_QUERY_THROW);
// use cursor to select "He lay" and apply bold italic
xTextCursor->gotoStart(false);
xTextCursor->goRight(6, true);
// from CharacterProperties
xCursorProps->setPropertyValue("CharPosture", Any(FontSlant_ITALIC));
xCursorProps->setPropertyValue("CharWeight", Any(FontWeight::BOLD));
xTextCursor->gotoEnd(false);
xText->insertString(
xTextCursor->getStart(),
" The mountainside sloped gently where he lay; "
"but below it was steep and he could see the dark of the oiled road "
"winding through the pass. There was a stream alongside the road "
"and far down the pass he saw a mill beside the stream and the falling water "
"of the dam, white in the summer sunlight.",
false);
xText->insertString(xTextCursor->getStart(), "\n \"Is that the mill?\" he asked.", false);
}
In tables and table cells, the interface com.sun.star.table.XCellRange allows you to retrieve single cells and subranges of cells. Once you have a cell, you can work with its formula or numeric value through the interface com.sun.star.table.XCell.
Table formatting is partially different in text tables and spreadsheet tables. Text tables use the properties specified in com.sun.star.text.TextTable, whereas spreadsheet tables use com.sun.star.table.CellProperties. Furthermore there are table cursors that allow to select and format cell ranges and the contained text. But since a com.sun.star.text.TextTableCursor works quite differently from a com.sun.star.sheet.SheetCellCursor, we will discuss them in the chapters about text and spreadsheet documents.
protected void manipulateTable(XCellRange xCellRange) throws com.sun.star.uno.Exception {
String backColorPropertyName = "";
XPropertySet xTableProps = null;
// enter column titles and a cell value
// Enter "Quotation" in A1, "Year" in B1. We use setString because we want to change the whole
// cell text at once
XCell xCell = xCellRange.getCellByPosition(0,0);
XText xCellText = UnoRuntime.queryInterface(XText.class, xCell);
xCellText.setString("Quotation");
xCell = xCellRange.getCellByPosition(1,0);
xCellText = UnoRuntime.queryInterface(XText.class, xCell);
xCellText.setString("Year");
// cell value
xCell = xCellRange.getCellByPosition(1,1);
xCell.setValue(1940);
// select the table headers and get the cell properties
XCellRange xSelectedCells = xCellRange.getCellRangeByName("A1:B1");
XPropertySet xCellProps = UnoRuntime.queryInterface(
XPropertySet.class, xSelectedCells);
// format the color of the table headers and table borders
// we need to distinguish text and spreadsheet tables:
// - the property name for cell colors is different in text and sheet cells
// - the common property for table borders is com.sun.star.table.TableBorder, but
// we must apply the property TableBorder to the whole text table,
// whereas we only want borders for spreadsheet cells with content.
// XServiceInfo allows to distinguish text tables from spreadsheets
XServiceInfo xServiceInfo = UnoRuntime.queryInterface(XServiceInfo.class, xCellRange);
// determine the correct property name for background color and the XPropertySet interface
// for the cells that should get colored border lines
if (xServiceInfo.supportsService("com.sun.star.sheet.Spreadsheet")) {
backColorPropertyName = "CellBackColor";
// select cells
xSelectedCells = xCellRange.getCellRangeByName("A1:B2");
// table properties only for selected cells
xTableProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xSelectedCells);
}
else if (xServiceInfo.supportsService("com.sun.star.text.TextTable")) {
backColorPropertyName = "BackColor";
// table properties for whole table
xTableProps = UnoRuntime.queryInterface(XPropertySet.class, xCellRange);
}
// set cell background color
xCellProps.setPropertyValue(backColorPropertyName, Integer.valueOf(0x99CCFF));
// set table borders
// create description for blue line, width 10
// colors are given in ARGB, comprised of four bytes for alpha-red-green-blue as in 0xAARRGGBB
BorderLine theLine = new BorderLine();
theLine.Color = 0x000099;
theLine.OuterLineWidth = 10;
// apply line description to all border lines and make them valid
TableBorder bord = new TableBorder();
bord.VerticalLine = bord.HorizontalLine =
bord.LeftLine = bord.RightLine =
bord.TopLine = bord.BottomLine =
theLine;
bord.IsVerticalLineValid = bord.IsHorizontalLineValid =
bord.IsLeftLineValid = bord.IsRightLineValid =
bord.IsTopLineValid = bord.IsBottomLineValid =
true;
xTableProps.setPropertyValue("TableBorder", bord);
}
def manipulateTable(self, xcellrange) -> None:
"""Format a table area
:param xcellrange: object that implements com.sun.star.table.XCellRange interface.
"""
# enter column titles and a cell value
xcellrange[0, 0].String = "Quotation"
xcellrange[0, 1].String = "Year"
xcellrange[1, 1].Value = 1940
# format table headers and table borders
# we need to distinguish text and sheet tables:
# property name for cell colors is different in text and sheet cells
# we want to apply TableBorder to whole text table, but only to sheet
# cells with content
background_color = 0x99CCFF
# create description for blue line, width 10
line = BorderLine()
line.Color = 0x000099
line.OuterLineWidth = 10
# apply line description to all border lines and make them valid
border = TableBorder()
border.VerticalLine = border.HorizontalLine = line
border.LeftLine = border.RightLine = line
border.TopLine = border.BottomLine = line
border.IsVerticalLineValid = border.IsHorizontalLineValid = True
border.IsLeftLineValid = border.IsRightLineValid = True
border.IsTopLineValid = border.IsBottomLineValid = True
if xcellrange.supportsService("com.sun.star.sheet.Spreadsheet"):
selected_cells = xcellrange["A1:B2"]
selected_cells.CellBackColor = background_color
selected_cells.TableBorder = border
print(selected_cells.TableBorder.TopLine.Color)
elif xcellrange.supportsService("com.sun.star.text.TextTable"):
selected_cells = xcellrange["A1:B1"]
selected_cells.BackColor = background_color
xcellrange.TableBorder = border
print(xcellrange.TableBorder.TopLine.Color)
Sub manipulateTable(xcellrange As Object)
'Format a table area
':param xcellrange: object that implements com.sun.star.table.XCellRange interface.
' enter column titles and a cell value
xcellrange.getCellByPosition(0, 0).SetString("Quotation")
xcellrange.getCellByPosition(0, 1).SetString("Year")
xcellrange.getCellByPosition(1, 1).SetValue(1940)
' format table headers and table borders
' we need to distinguish text and sheet tables:
' property name for cell colors is different in text and sheet cells
' we want to apply TableBorder to whole text table, but only to sheet
' cells with content
background_color = &H99CCFF
' create description for blue line, width 10
Dim border_line As New com.sun.star.table.BorderLine
border_line.Color = &H000099
border_line.OuterLineWidth = 10
' apply line description to all border lines and make them valid
Dim border As New com.sun.star.table.TableBorder
border.VerticalLine = border_line
border.HorizontalLine = border_line
border.LeftLine = border_line
border.RightLine = border_line
border.TopLine = border_line
border.BottomLine = border_line
border.IsVerticalLineValid = True
border.IsHorizontalLineValid = True
border.IsLeftLineValid = True
border.IsRightLineValid = True
border.IsTopLineValid = True
border.IsBottomLineValid = True
If xcellrange.supportsService("com.sun.star.sheet.Spreadsheet") Then
selected_cells = xcellrange.getCellRangeByName("A1:B2")
selected_cells.CellBackColor = background_color
selected_cells.TableBorder = border
' Print selected_cells.TableBorder.TopLine.Color
ElseIf xcellrange.supportsService("com.sun.star.text.TextTable") Then
selected_cells = xcellrange.getCellRangeByName("A1:B1")
selected_cells.BackColor = background_color
xcellrange.TableBorder = border
' Print xcellrange.TableBorder.TopLine.Color
End If
End Sub
void manipulateTable(const Reference<XCellRange>& xCellRange)
{
OUString backColorPropertyName;
Reference<XPropertySet> xTableProps = nullptr;
// enter column titles and a cell value
Reference<XCell> xCell = xCellRange->getCellByPosition(0, 0);
Reference<XText> xCellText = Reference<XText>(xCell, UNO_QUERY_THROW);
xCellText->setString("Quotation");
xCell = xCellRange->getCellByPosition(1, 0);
xCellText = Reference<XText>(xCell, UNO_QUERY_THROW);
xCellText->setString("Year");
xCell = xCellRange->getCellByPosition(1, 1);
xCell->setValue(1940);
Reference<XCellRange> xSelectedCells = xCellRange->getCellRangeByName("A1:B1");
Reference<XPropertySet> xCellProps(xSelectedCells, UNO_QUERY_THROW);
// format table headers and table borders
// we need to distinguish text and sheet tables:
// property name for cell colors is different in text and sheet cells
// we want to apply TableBorder to whole text table, but only to sheet cells with content
Reference<XServiceInfo> xServiceInfo(xCellRange, UNO_QUERY_THROW);
if (xServiceInfo->supportsService("com.sun.star.sheet.Spreadsheet"))
{
backColorPropertyName = "CellBackColor";
xSelectedCells = xCellRange->getCellRangeByName("A1:B2");
xTableProps = Reference<XPropertySet>(xSelectedCells, UNO_QUERY_THROW);
}
else if (xServiceInfo->supportsService("com.sun.star.text.TextTable"))
{
backColorPropertyName = "BackColor";
xTableProps = Reference<XPropertySet>(xCellRange, UNO_QUERY_THROW);
}
// set cell background color
xCellProps->setPropertyValue(backColorPropertyName, Any(0x99CCFF));
// set table borders
// create description for blue line, width 10
BorderLine theLine;
theLine.Color = 0x000099;
theLine.OuterLineWidth = 10;
// apply line description to all border lines and make them valid
TableBorder bord;
bord.VerticalLine = bord.HorizontalLine = bord.LeftLine = bord.RightLine = bord.TopLine
= bord.BottomLine = theLine;
bord.IsVerticalLineValid = bord.IsHorizontalLineValid = bord.IsLeftLineValid
= bord.IsRightLineValid = bord.IsTopLineValid = bord.IsBottomLineValid = true;
xTableProps->setPropertyValue("TableBorder", Any(bord));
xTableProps->getPropertyValue("TableBorder") >>= bord;
theLine = bord.TopLine;
int col = theLine.Color;
std::cout << col << "\n";
}
On drawing shapes, the interface com.sun.star.drawing.XShape is used to determine the position and size of a shape.
Everything else is a matter of property-based formatting and there is a multitude of properties to use. LibreOffice comes with eleven different shapes that are the basis for the drawing tools in the GUI (graphical user interface). Six of the shapes have individual properties that reflect their characteristics. The six shapes are:
- com.sun.star.drawing.EllipseShape for circles and ellipses.
- com.sun.star.drawing.RectangleShape for boxes.
- com.sun.star.drawing.TextShape for text boxes.
- com.sun.star.drawing.CaptionShape for labeling.
- com.sun.star.drawing.MeasureShape for metering.
- com.sun.star.drawing.ConnectorShape for lines that can be "glued" to other shapes to draw connecting lines between them.
Five shapes have no individual properties, rather they share the properties defined in the service com.sun.star.drawing.PolyPolygonBezierDescriptor:
- com.sun.star.drawing.LineShape is for lines and arrows.
- com.sun.star.drawing.PolyLineShape is for open shapes formed by straight lines.
- com.sun.star.drawing.PolyPolygonShape is for shapes formed by one or more polygons.
- com.sun.star.drawing.ClosedBezierShape is for closed bezier shapes.
- com.sun.star.drawing.PolyPolygonBezierShape is for combinations of multiple polygon and Bezier shapes.
All of these eleven shapes use the properties from the following services:
- com.sun.star.drawing.Shape describes basic properties of all shapes such as the layer a shape belongs to, protection from moving and sizing, style name, 3D transformation and name.
- com.sun.star.drawing.LineProperties determines how the lines of a shape look.
- com.sun.star.drawing.Text has no properties of its own, but includes:
- com.sun.star.drawing.TextProperties that affects numbering, shape growth and text alignment in the cell, text animation and writing direction.
- com.sun.star.style.ParagraphProperties is concerned with paragraph formatting.
- com.sun.star.style.CharacterProperties formats characters.
- com.sun.star.drawing.ShadowProperties deals with the shadow of a shape.
- com.sun.star.drawing.RotationDescriptor sets rotation and shearing of a shape.
- com.sun.star.drawing.FillProperties is only for closed shapes and describes how the shape is filled.
- com.sun.star.presentation.Shape adds presentation effects to shapes in presentation documents.
Consider the following example showing how these properties work:
protected void manipulateShape(XShape xShape) throws com.sun.star.uno.Exception {
// for usage of setSize and setPosition in interface XShape see method useDraw() below
XPropertySet xShapeProps = UnoRuntime.queryInterface(XPropertySet.class, xShape);
// colors are given in ARGB, comprised of four bytes for alpha-red-green-blue as in 0xAARRGGBB
xShapeProps.setPropertyValue("FillColor", Integer.valueOf(0x99CCFF));
xShapeProps.setPropertyValue("LineColor", Integer.valueOf(0x000099));
// angles are given in hundredth degrees, rotate by 30 degrees
xShapeProps.setPropertyValue("RotateAngle", Integer.valueOf(3000));
}
def manipulateShape(self, xshape) -> None:
"""Format a shape
:param xshape: object that implements com.sun.star.drawing.XShape interface.
"""
xshape.FillColor = 0x99CCFF
xshape.LineColor = 0x000099
xshape.RotateAngle = 3000
xshape.TextLeftDistance = 0
xshape.TextRightDistance = 0
xshape.TextUpperDistance = 0
xshape.TextLowerDistance = 0
Sub manipulateShape(xshape As Object)
'Format a shape
'param xshape: object that implements com.sun.star.drawing.XShape interface.
xshape.FillColor = &H99CCFF
xshape.LineColor = &H000099
xshape.RotateAngle = 3000
xshape.TextLeftDistance = 0
xshape.TextRightDistance = 0
xshape.TextUpperDistance = 0
xshape.TextLowerDistance = 0
End Sub
void manipulateShape(const Reference<XShape>& xShape)
{
Reference<XPropertySet> xShapeProps = Reference<XPropertySet>(xShape, UNO_QUERY_THROW);
xShapeProps->setPropertyValue("FillColor", Any(0x99CCFF));
xShapeProps->setPropertyValue("LineColor", Any(0x000099));
xShapeProps->setPropertyValue("RotateAngle", Any(3000));
xShapeProps->setPropertyValue("TextLeftDistance", Any(0));
xShapeProps->setPropertyValue("TextRightDistance", Any(0));
xShapeProps->setPropertyValue("TextUpperDistance", Any(0));
xShapeProps->setPropertyValue("TextLowerDistance", Any(0));
}
Creating Text, Tables and Drawing Shapes
The three manipulateXXX
methods above took text, table and shape objects as parameters and altered them. The following methods show how to create such objects in the various document types. Note that all documents have their own service factory to create objects to be inserted into the document. Aside from that it depends very much on the document type how you proceed. This section only demonstrates the different procedures, the explanation can be found in the chapters about Text, Spreadsheet and Drawing Documents.
First, a small convenience method is used to create new documents.
protected XComponent newDocComponent(String docType) throws java.lang.Exception {
String loadUrl = "private:factory/" + docType;
xRemoteServiceManager = this.getRemoteServiceManager(unoUrl);
Object desktop = xRemoteServiceManager.createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
XComponentLoader xComponentLoader = UnoRuntime.queryInterface(
XComponentLoader.class, desktop);
PropertyValue[] loadProps = new PropertyValue[0];
return xComponentLoader.loadComponentFromURL(loadUrl, "_blank", 0, loadProps);
}
def new_doc_component(self, doc_type: str) -> None:
load_url = "private:factory/" + doc_type
self.remote_service_manager = self.get_remote_service_manager()
desktop = self.remote_service_manager.createInstanceWithContext(
"com.sun.star.frame.Desktop", self.remote_context
)
return desktop.loadComponentFromURL(load_url, "_blank", 0, tuple([]))
Function new_doc_component(doc_type As String)
load_url = "private:factory/" & doc_type
desktop = createUnoService("com.sun.star.frame.Desktop")
Set new_doc_component = desktop.loadComponentFromURL(load_url, "_blank", 0, Array())
End Function
Reference<XComponent> newDocComponent(const OUString docType)
{
OUString loadUrl = "private:factory/" + docType;
Reference<XMultiComponentFactory> xRemoteServiceManager = getRemoteServiceManager();
Reference<XInterface> desktop = xRemoteServiceManager->createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
Reference<XComponentLoader> xComponentLoader
= Reference<XComponentLoader>(desktop, UNO_QUERY_THROW);
Sequence<PropertyValue> loadProps(0);
Reference<XComponent> xDocument
= xComponentLoader->loadComponentFromURL(loadUrl, "_blank", 0, loadProps);
return xDocument;
}
Text, Tables and Drawings in Writer
The method useWriter
creates a writer document and manipulates its text, then uses the document's internal service manager to instantiate a text table and a shape, inserts them and manipulates the table and shape. Refer to Text Documents for more detailed information.
protected void useWriter() throws java.lang.Exception {
try {
// create new writer document and get text, then manipulate text
XComponent xWriterComponent = newDocComponent("swriter");
XTextDocument xTextDocument = UnoRuntime.queryInterface(
XTextDocument.class, xWriterComponent);
XText xText = xTextDocument.getText();
manipulateText(xText);
// get internal service factory of the document
XMultiServiceFactory xWriterFactory = UnoRuntime.queryInterface(
XMultiServiceFactory.class, xWriterComponent);
// insert TextTable and get cell text, then manipulate text in cell
Object table = xWriterFactory.createInstance("com.sun.star.text.TextTable");
XTextContent xTextContentTable = UnoRuntime.queryInterface(
XTextContent.class, table);
xText.insertTextContent(xText.getEnd(), xTextContentTable, false);
XCellRange xCellRange = UnoRuntime.queryInterface(
XCellRange.class, table);
XCell xCell = xCellRange.getCellByPosition(0, 1);
XText xCellText = UnoRuntime.queryInterface(XText.class, xCell);
manipulateText(xCellText);
manipulateTable(xCellRange);
// insert RectangleShape and get shape text, then manipulate text
Object writerShape = xWriterFactory.createInstance(
"com.sun.star.drawing.RectangleShape");
XShape xWriterShape = UnoRuntime.queryInterface(
XShape.class, writerShape);
xWriterShape.setSize(new Size(10000, 10000));
XTextContent xTextContentShape = UnoRuntime.queryInterface(
XTextContent.class, writerShape);
xText.insertTextContent(xText.getEnd(), xTextContentShape, false);
XPropertySet xShapeProps = UnoRuntime.queryInterface(
XPropertySet.class, writerShape);
// wrap text inside shape
xShapeProps.setPropertyValue("TextContourFrame", Boolean.TRUE);
XText xShapeText = UnoRuntime.queryInterface(XText.class, writerShape);
manipulateText(xShapeText);
manipulateShape(xWriterShape);
}
catch ( com.sun.star.lang.DisposedException e ) { //works from Patch 1
xRemoteContext = null;
throw e;
}
}
def use_writer(self) -> None:
try:
doc = self.new_doc_component("swriter")
xtext = doc.Text
self.manipulateText(xtext)
# insert TextTable and get cell text, then manipulate text in cell
table = doc.createInstance("com.sun.star.text.TextTable")
xtext.insertTextContent(xtext.End, table, False)
xcell = table[1, 0]
self.manipulateText(xcell)
self.manipulateTable(table)
# insert RectangleShape and get shape text, then manipulate text
writer_shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
writer_shape.setSize(Size(10000, 10000))
xtext.insertTextContent(xtext.End, writer_shape, False)
# wrap text inside shape
writer_shape.TextContourFrame = True
self.manipulateText(writer_shape)
self.manipulateShape(writer_shape)
bookmark = doc.createInstance("com.sun.star.text.Bookmark")
bookmark.Name = "MyUniqueBookmarkName"
# insert the bookmark at the end of the document
xtext.insertTextContent(xtext.End, bookmark, False)
# Query the added bookmark and set a string
found_bookmark = doc.Bookmarks.getByName("MyUniqueBookmarkName")
found_bookmark.Anchor.String = (
"The throat mike, glued to her neck, "
"looked as much as possible like an analgesic dermadisk."
)
for text_table in doc.TextTables:
text_table.BackColor = 0xC8FFB9
except DisposedException:
self.remote_context = None
raise
Sub use_draw
doc = new_doc_component("sdraw")
Dim Point As New com.sun.star.awt.Point
Dim Size As New com.sun.star.awt.Size
draw_shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
Point.X = 5000
Point.Y = 5000
Size.Width= 10000
Size.Height= 10000
draw_shape.setSize(Size)
draw_shape.setPosition(Point)
doc.DrawPages(0).add(draw_shape)
' wrap text inside shape
draw_shape.TextContourFrame = True
manipulateText(draw_shape)
manipulateShape(draw_shape)
End Sub
void useWriter()
{
// create new writer document and get text, then manipulate text
Reference<XComponent> xWriterComponent = newDocComponent("swriter");
Reference<XTextDocument> xTextDocument(xWriterComponent, UNO_QUERY_THROW);
Reference<XText> xText = xTextDocument->getText();
manipulateText(xText);
// get internal service factory of the document
Reference<lang::XMultiServiceFactory> xWriterFactory
= Reference<XMultiServiceFactory>(xTextDocument, UNO_QUERY_THROW);
// insert TextTable and get cell text, then manipulate text in cell
Reference<XTextContent> xTextContentTable = Reference<XTextContent>(
xWriterFactory->createInstance("com.sun.star.text.TextTable"), UNO_QUERY_THROW);
xText->insertTextContent(xText->getEnd(), xTextContentTable, false);
Reference<XCellRange> xCellRange = Reference<XCellRange>(xTextContentTable, UNO_QUERY_THROW);
Reference<XCell> xCell = xCellRange->getCellByPosition(0, 1);
Reference<XText> xCellText = Reference<XText>(xCell, UNO_QUERY_THROW);
manipulateText(xCellText);
manipulateTable(xCellRange);
Reference<XShape> xWriterShape = Reference<XShape>(
xWriterFactory->createInstance("com.sun.star.drawing.RectangleShape"), UNO_QUERY_THROW);
xWriterShape->setSize(Size(10000, 10000));
Reference<XTextContent> xTextContentShape(xWriterShape, UNO_QUERY_THROW);
xText->insertTextContent(xText->getEnd(), xTextContentShape, false);
Reference<XPropertySet> xShapeProps(xWriterShape, UNO_QUERY_THROW);
// wrap text inside shape
xShapeProps->setPropertyValue("TextContourFrame", Any(true));
Reference<XText> xShapeText = Reference<XText>(xWriterShape, UNO_QUERY_THROW);
manipulateText(xShapeText);
manipulateShape(xWriterShape);
// more code snippets used in the manual:
Reference<XInterface> bookmark = xWriterFactory->createInstance("com.sun.star.text.Bookmark");
// name the bookmark
Reference<XNamed> xNamed(bookmark, UNO_QUERY_THROW);
xNamed->setName("MyUniqueBookmarkName");
// get XTextContent interface and insert it at the end of the document
Reference<XTextContent> xTextContent(bookmark, UNO_QUERY_THROW);
xText->insertTextContent(xText->getEnd(), xTextContent, false);
//query BookmarksSupplier
Reference<XBookmarksSupplier> xBookmarksSupplier(xWriterComponent, UNO_QUERY_THROW);
Reference<XNameAccess> xNamedBookmarks = xBookmarksSupplier->getBookmarks();
auto foundBookmark = xNamedBookmarks->getByName("MyUniqueBookmarkName");
Reference<XTextContent> xFoundBookmark(foundBookmark, UNO_QUERY_THROW);
Reference<XTextRange> xFound = xFoundBookmark->getAnchor();
xFound->setString(" The throat mike, glued to her neck, "
"looked as much as possible like an analgesic dermadisk.");
// first query the XTextTablesSupplier interface from our document
Reference<XTextTablesSupplier> xTablesSupplier(xWriterComponent, UNO_QUERY_THROW);
// get the tables collection
Reference<XNameAccess> xNamedTables = xTablesSupplier->getTextTables();
// now query the XIndexAccess from the tables collection
Reference<XIndexAccess> xIndexedTables(xNamedTables, UNO_QUERY_THROW);
// we need properties
Reference<XPropertySet> xTableProps = nullptr;
// get the tables
for (int i = 0; i < xIndexedTables->getCount(); i++)
{
//Object table = xIndexedTables.getByIndex(i);
auto table = xIndexedTables->getByIndex(i);
xTableProps = Reference<XPropertySet>(table, UNO_QUERY_THROW);
xTableProps->setPropertyValue("BackColor", Any(0xC8FFB9));
}
}
Text, Tables and Drawings in Calc
The method useCalc
creates a calc document, uses its document factory to create a shape and manipulates the cell text, table and shape. The chapter Spreadsheet Documents treats all aspects of spreadsheets.
protected void useCalc() throws java.lang.Exception {
try {
// create new calc document and manipulate cell text
XComponent xCalcComponent = newDocComponent("scalc");
XSpreadsheetDocument xSpreadsheetDocument =
UnoRuntime.queryInterface(
XSpreadsheetDocument .class, xCalcComponent);
Object sheets = xSpreadsheetDocument.getSheets();
XIndexAccess xIndexedSheets = UnoRuntime.queryInterface(
XIndexAccess.class, sheets);
Object sheet = xIndexedSheets.getByIndex(0);
//get cell A2 in first sheet
XCellRange xSpreadsheetCells = UnoRuntime.queryInterface(
XCellRange.class, sheet);
XCell xCell = xSpreadsheetCells.getCellByPosition(0,1);
XPropertySet xCellProps = UnoRuntime.queryInterface(
XPropertySet.class, xCell);
xCellProps.setPropertyValue("IsTextWrapped", Boolean.TRUE);
XText xCellText = UnoRuntime.queryInterface(XText.class, xCell);
manipulateText(xCellText);
manipulateTable(xSpreadsheetCells);
// get internal service factory of the document
XMultiServiceFactory xCalcFactory = UnoRuntime.queryInterface(
XMultiServiceFactory.class, xCalcComponent);
// get Drawpage
XDrawPageSupplier xDrawPageSupplier =
UnoRuntime.queryInterface(XDrawPageSupplier.class, sheet);
XDrawPage xDrawPage = xDrawPageSupplier.getDrawPage();
// create and insert RectangleShape and get shape text, then manipulate text
Object calcShape = xCalcFactory.createInstance(
"com.sun.star.drawing.RectangleShape");
XShape xCalcShape = UnoRuntime.queryInterface(
XShape.class, calcShape);
xCalcShape.setSize(new Size(10000, 10000));
xCalcShape.setPosition(new Point(7000, 3000));
xDrawPage.add(xCalcShape);
XPropertySet xShapeProps = UnoRuntime.queryInterface(
XPropertySet.class, calcShape);
// wrap text inside shape
xShapeProps.setPropertyValue("TextContourFrame", Boolean.TRUE);
XText xShapeText = UnoRuntime.queryInterface(XText.class, calcShape);
manipulateText(xShapeText);
manipulateShape(xCalcShape);
}
catch ( com.sun.star.lang.DisposedException e ) { //works from Patch 1
xRemoteContext = null;
throw e;
}
}
def use_calc(self) -> None:
try:
doc = self.new_doc_component("scalc")
sheet = doc.Sheets[0]
# get cell A2 in first sheet
cell = sheet[1, 0]
cell.IsTextWrapped = True
self.manipulateText(cell)
self.manipulateTable(sheet)
# create and insert RectangleShape and get shape text,
# then manipulate text
shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
shape.Size = Size(10000, 10000)
shape.Position = Point(7000, 3000)
shape.TextContourFrame = True
sheet.DrawPage.add(shape)
self.manipulateText(shape)
self.manipulateShape(shape)
except DisposedException:
self.remote_context = None
raise
Sub use_calc
doc = new_doc_component("scalc")
sheet = doc.Sheets(0)
' get cell A2 in first sheet
cell = sheet.getCellByPosition(1, 0)
cell.IsTextWrapped = True
manipulateText(cell.getText())
manipulateTable(sheet)
' create and insert RectangleShape and get shape text,
' then manipulate text
shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
Dim Point As New com.sun.star.awt.Point
Dim Size As New com.sun.star.awt.Size
shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
Point.X = 7000
Point.Y = 3000
Size.Width= 10000
Size.Height= 10000
shape.setSize(Size)
shape.setPosition(Point)
shape.TextContourFrame = True
sheet.DrawPage.add(shape)
manipulateText(shape)
manipulateShape(shape)
End Sub
void useCalc()
{
// create new calc document and manipulate cell text
Reference<XComponent> xCalcComponent = newDocComponent("scalc");
Reference<XSpreadsheetDocument> xCalcDocument(xCalcComponent, UNO_QUERY_THROW);
Reference<XSpreadsheetDocument> xSpreadsheetDocument
= Reference<XSpreadsheetDocument>(xCalcDocument, UNO_QUERY);
Reference<XIndexAccess> xIndexedSheets(xSpreadsheetDocument->getSheets(), UNO_QUERY);
Reference<XCellRange> xSpreadsheetCells(xIndexedSheets->getByIndex(0), UNO_QUERY_THROW);
//get cell A2 in first sheet
Reference<XCell> xCell = xSpreadsheetCells->getCellByPosition(0, 1);
Reference<XText> xCellText = Reference<XText>(xCell, UNO_QUERY_THROW);
Reference<XPropertySet> xCellProps(xCell, UNO_QUERY_THROW);
xCellProps->setPropertyValue("IsTextWrapped", Any(true));
manipulateText(xCellText);
manipulateTable(xSpreadsheetCells);
// get internal service factory of the document
Reference<XMultiServiceFactory> xCalcFactory
= Reference<XMultiServiceFactory>(xCalcDocument, UNO_QUERY_THROW);
// get Drawpage
Reference<XDrawPageSupplier> xDrawPageSupplier(xIndexedSheets->getByIndex(0), UNO_QUERY_THROW);
Reference<XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_QUERY_THROW);
// create and insert RectangleShape and get shape text, then manipulate text
Reference<XShape> xCalcShape(
xCalcFactory->createInstance("com.sun.star.drawing.RectangleShape"), UNO_QUERY_THROW);
xCalcShape->setSize(Size(10000, 10000));
xCalcShape->setPosition(Point(7000, 3000));
xDrawPage->add(xCalcShape);
Reference<XPropertySet> xShapeProps(xCalcShape, UNO_QUERY_THROW);
// wrap text inside shape
xShapeProps->setPropertyValue("TextContourFrame", Any(true));
Reference<XText> xShapeText = Reference<XText>(xCalcShape, UNO_QUERY_THROW);
manipulateText(xShapeText);
manipulateShape(xCalcShape);
}
Drawings and Text in Draw
The method useDraw
creates a draw document and uses its document factory to instantiate and add a shape, then it manipulates the shape. The chapter Drawing Documents and Presentation Documents casts more light on drawings and presentations.
protected void useDraw() throws java.lang.Exception {
try {
//create new draw document and insert rectangle shape
XComponent xDrawComponent = newDocComponent("sdraw");
XDrawPagesSupplier xDrawPagesSupplier =
UnoRuntime.queryInterface(
XDrawPagesSupplier.class, xDrawComponent);
Object drawPages = xDrawPagesSupplier.getDrawPages();
XIndexAccess xIndexedDrawPages = UnoRuntime.queryInterface(
XIndexAccess.class, drawPages);
Object drawPage = xIndexedDrawPages.getByIndex(0);
XDrawPage xDrawPage = UnoRuntime.queryInterface(XDrawPage.class, drawPage);
// get internal service factory of the document
XMultiServiceFactory xDrawFactory =
UnoRuntime.queryInterface(
XMultiServiceFactory.class, xDrawComponent);
Object drawShape = xDrawFactory.createInstance(
"com.sun.star.drawing.RectangleShape");
XShape xDrawShape = UnoRuntime.queryInterface(XShape.class, drawShape);
xDrawShape.setSize(new Size(10000, 20000));
xDrawShape.setPosition(new Point(5000, 5000));
xDrawPage.add(xDrawShape);
XText xShapeText = UnoRuntime.queryInterface(XText.class, drawShape);
XPropertySet xShapeProps = UnoRuntime.queryInterface(
XPropertySet.class, drawShape);
// wrap text inside shape
xShapeProps.setPropertyValue("TextContourFrame", Boolean.TRUE);
manipulateText(xShapeText);
manipulateShape(xDrawShape);
}
catch ( com.sun.star.lang.DisposedException e ) { //works from Patch 1
xRemoteContext = null;
throw e;
}
}
def use_draw(self) -> None:
try:
doc = self.new_doc_component("sdraw")
draw_shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
draw_shape.setSize(Size(10000, 20000))
draw_shape.setPosition(Point(5000, 5000))
doc.DrawPages[0].add(draw_shape)
# wrap text inside shape
draw_shape.TextContourFrame = True
self.manipulateText(draw_shape)
self.manipulateShape(draw_shape)
except DisposedException:
self.remote_context = None
raise
Sub use_draw
doc = new_doc_component("sdraw")
Dim Point As New com.sun.star.awt.Point
Dim Size As New com.sun.star.awt.Size
draw_shape = doc.createInstance("com.sun.star.drawing.RectangleShape")
Point.X = 5000
Point.Y = 5000
Size.Width= 10000
Size.Height= 10000
draw_shape.setSize(Size)
draw_shape.setPosition(Point)
doc.DrawPages(0).add(draw_shape)
' wrap text inside shape
draw_shape.TextContourFrame = True
manipulateText(draw_shape)
manipulateShape(draw_shape)
End Sub
void useDraw()
{
//create new draw document and insert ractangle shape
Reference<XComponent> xDrawComponent = newDocComponent("sdraw");
Reference<XDrawPagesSupplier> xDrawPagesSupplier(xDrawComponent, UNO_QUERY_THROW);
Reference<XIndexAccess> xIndexedDrawPages(xDrawPagesSupplier->getDrawPages(), UNO_QUERY_THROW);
Reference<XDrawPage> xDrawPage(xIndexedDrawPages->getByIndex(0), UNO_QUERY_THROW);
// get internal service factory of the document
Reference<lang::XMultiServiceFactory> xDrawFactory
= Reference<lang::XMultiServiceFactory>(xDrawComponent, UNO_QUERY_THROW);
Reference<XShape> xDrawShape(
xDrawFactory->createInstance("com.sun.star.drawing.RectangleShape"), UNO_QUERY_THROW);
xDrawShape->setSize(Size(10000, 20000));
xDrawShape->setPosition(Point(5000, 5000));
xDrawPage->add(xDrawShape);
Reference<XPropertySet> xShapeProps(xDrawShape, UNO_QUERY_THROW);
// wrap text inside shape
xShapeProps->setPropertyValue("TextContourFrame", Any(true));
Reference<XText> xShapeText = Reference<XText>(xDrawShape, UNO_QUERY_THROW);
manipulateText(xShapeText);
manipulateShape(xDrawShape);
}