We’re half-way through the event, so there’s still time to join us in Luxembourg (or you can watch recordings after the event).
We’re half-way through the event, so there’s still time to join us in Luxembourg (or you can watch recordings after the event).
The LibreOffice and Open Source Conference 2024 has started! We’ll be uploading videos from the talks as soon as possible, and in the meantime, here are a few photos from today’s talks and events…
The LibreOffice and Open Source Conference 2024 starts in just a few days in Luxembourg, and it’s supported by the country’s Digital Learning Hub, which offers short and hands-on training courses in the fields of computer science. Director Serge Linckels says:
We are delighted to announce our partnership with the LibreOffice Conference 2024, taking place in Luxembourg. This collaboration underscores our commitment to fostering innovation and supporting the open-source community.
As the Digital Learning Hub, we are dedicated to advancing digital education and empowering individuals with the skills needed for the future. Our involvement in the LibreOffice Conference 2024 reflects our mission to promote digital literacy and open-source solutions. We believe that by supporting events like this, we can help build a more inclusive and technologically adept society.
We look forward to engaging with the vibrant community at the conference, sharing insights, and exploring new opportunities.
Thank you to The Document Foundation for organizing this pivotal event. We are very excited to be part of this journey.
The SVG export in Impress now supports a per-paragraph setting to handle semi-transparent shape text, while previously this was only possible to control at a per-shape level.
This work is primarily for Collabora Online, but the feature is available in desktop Impress as well.
As described in a previous post, Impress already had the capability to have semi-transparent shape text, but the SVG export of this for the case when not all paragraphs have the same setting was broken.
Transparency in SVG can be described as a property of a group (<g style="opacity: 0.5">...</g>
)
and it can be also a property of the text (<tspan fill-opacity="0.5">...</tspan>
).
The SVG export works with the metafile of the shape, so when looking for meta actions, it tries to
guess if the transparency will be for text: if so, it needs to use the tspan
markup, otherwise
going with the g
markup is OK.
What happened here is that meta action for a normal text started, so the SVG export assumed the text is not semi-transparent, but later the second line was still transparent, so we started a group element, and this resulted in a not even well-formed XML output.
The relevant part of the test document is simple: just 3 paragraphs, the second one is semi-transparent (and also has a bullet, as an extra):
Once this was exported to SVG, this resulted in a non-well-formed XML, so you got this error in a web browser:
Once tweaking the transparency mask writer to check if text started already, we get the correct SVG render:
If you would like to know a bit more about how this works, continue reading... :-)
The bugfix commit was SVG export: fix handling of semi-transparent text inside a list.
The tracking bug was tdf#162782.
You can get a development edition of Collabora Online 24.04 and try it out yourself right now: try the development edition. Collabora intends to continue supporting and contributing to LibreOffice, the code is merged so we expect all of this work will be available in TDF's next release too (25.2).
On September 21, free and open source software (FOSS) enthusiasts celebrated the 21st worldwide Software Freedom Day. Our community members in Nepal were not behind with the celebrations either: they were active supporting small open source communities and connecting them for the greater good in the LibreOffice community. Here’s their report:
Suraj Bhattarai, LibreOffice liaison in Nepal, was available at the LibreOffice booth at the Software Freedom Day celebration by Open Source Klub (NOSK) at Nepal College of Information and Technology (NCIT), Lalitpur.
He described how the LibreOffice booth was so busy and engaging. In particular, the LibreOffice community supported the event with fun games, swag, candies, and engagement – all while advocating for the best free and open source office suite. The booth included amazing LibreOffice merchandise, such as T-shirts, tote-bags, water-bottles, round pin plastic badges, flyers, a variety of stickers, beer mats, candies, and so forth.
The booth had a LibreOffice crossword game, and showed LibreOffice 24.8 on a display for hands-on testing. There was also a presentation deck for newbies, and some verbal support/assistance to improve the LibreOffice experience and customization for easier navigation within the user interface. Suraj also mentioned that around three quarters of the people who appeared at the booth for a quick “hello” mentioned hearing about or knowing and using LibreOffice in their home or workspace.
The event was mainly joined by students across Kathmandu valley, open source contributors, club alumni, and veteran FOSS contributors/kickstarters in Nepal. Apart from the event itinerary and other activities, the LibreOffice “paper plane contest” received major attention and everyone seems to have enjoyed their paper plane flight to software freedom!
The winner was awarded a 750ML aluminum water bottle, with the LibreOffice logo printed on it. Suraj concluded the competition with the 3R principle and the analogy of releasing paperwork and transitioning to digital open source office suites for document-related work pieces. The college administration expressed some interest in replacing Microsoft Office and migrating the campus computers to LibreOffice suite.
Similarly, Suraj also delivered a recorded talk named “Diversity, Inclusion and Community Model in Free Software Communities” at Birendra Multiple Campus, Chitwan. There, the Software Freedom Day celebration was hosted by the Birendra Open Source Club (BOSC) with support from the LibreOffice community.
The aim of the talk was to deepen and bridge the relationship of the club with the LibreOffice community and LibreOffice activities/contributions in the future. Previously, the club contributed greatly to the success of a local event: the LibreOffice Localization Sprint 2023. Achyut Koirala, the acting president of the on-campus club, represented the LibreOffice community there.
While Suraj himself couldn’t be present, Achyut Koirala together with Shreeram Lamichhane communicated the positive feedback from the recorded talk Suraj had shared. As a closing remark, Achyut thanked the LibreOffice community as a whole for the inclusive community model and for welcoming their community into the project.
And finally, Nirjal Bhurtel, representing LibreOffice’s local community, did the same at Kathmandu University where the celebration was hosted by their own on-campus community. Kathmandu Open Source Community has been the veteran contributor when it comes to the history of contributions from Nepal in the LibreOffice project.
TDF says: Many thanks to Suraj and the Nepalese community for the great work! And as a bonus, here’s a video from the aforementioned paper plane contest:
Please confirm that you want to play a YouTube video. By accepting, you will be accessing content from YouTube, a service provided by an external third party.
If you accept this notice, your choice will be saved and the page will refresh.
LibreOffice options page provides rich set of settings for everyone who wants to tune LibreOffice to match their needs. But, what if you as a developer, need setting dialogs that are needed elsewhere in the LibreOffice application? Here I discuss some of such use cases, which are handled by defining UNO commands.
The code for providing “Tools > Options” is not in a single module, but main part resides in cui
module, which contains code which is used across different modules. Looking into cui/source/options/
folder from LibreOffice core source code, you can see various different source files related to the options. The biggest file there is cui/source/options/treeopt.cxx
, which is the actual implementation of the tree-based dialog that you see when you open Tools > Options dialog. There are other C++ files that handle .ui files related to options. You can find those UI files in cui/uiconfig/ui/
folder with a name like opt*.ui
:
$ ls cui/uiconfig/ui/opt*.ui
These files can be edited and they are used as described in the LibreOffice design blog:
Only some of the dialogs can be opened available via UNO dispatch commands. As an example, you may see “.uno:AdditionsDialog” is used both in cui/source/options/optgdlg.cxx
for creating a dialog in Tools > Options (when you click for “more icons”), and also in sfx2/source/appl/appserv.cxx
.
You can try running this UNO command in LibreOffice BASIC editor with this code snippet:
Sub Main() Set oDispatch = CreateUnoService("com.sun.star.frame.DispatchHelper") Dim args(0) As New com.sun.star.beans.PropertyValue Set oFrame = StarDesktop.Frames.getByIndex(0) oDispatch.executeDispatch(oFrame, ".uno:AdditionsDialog", "", 0, args) End Sub
The above command is defined specifically to help developers use the “Extensions” dialog, anywhere in LibreOffice UI, from top menus to context menus and toolbars and also in code, in a simple way.
There is another dialog titled “Security Options and Warnings”, which is opened through .uno:OptionsSecurityDialog
UNO command. In this way, it can be used easily in other modules of LibreOffice.
Adding a new UNO command was discussed before, in a separate blog post:
Adding a new UNO command for an options dialog is basically the same. There can be differences regarding the configurations and the data that is passed between the dialog and the caller.
When you create a dialog box directly like the code snippet below, you have access to the member functions defined for that specific dialog:
IMPL_LINK_NOARG( SwGlossaryDlg, PathHdl, weld::Button&, void ) { SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); ScopedVclPtr<AbstractSvxMultiPathDialog> pDlg(pFact->CreateSvxPathSelectDialog(m_xDialog.get())); SvtPathOptions aPathOpt; const OUString sGlosPath( aPathOpt.GetAutoTextPath() ); pDlg->SetPath(sGlosPath); if(RET_OK == pDlg->Execute()) { const OUString sTmp(pDlg->GetPath()); if(sTmp != sGlosPath) { aPathOpt.SetAutoTextPath( sTmp ); ::GetGlossaries()->UpdateGlosPath( true ); Init(); } } }
As you can see, pDlg->GetPath()
is accessible here, and you can use it to pass data. But when you are using UNO commands, those functions will not be available directly. Instead, you may pass values that denote the data that will be read from somewhere else, like the configuration.
For example, consider there are multiple paths that you may want to edit using this UNO command with the same dialog. In this case, you can pass a value that shows the associated path that you are changing. It can be passed as an enumeration, and then you set and/or get the value directly from the configuration.
In this way, the callers in the C++ code will have much easier task to do, as it is only calling the UNO command, and the rest is done in the implementation of the UNO command.
For AdditionsDialog, the calling code is as simple as this in cui/source/options/optgdlg.cxx
:
IMPL_STATIC_LINK_NOARG(OfaViewTabPage, OnMoreIconsClick, weld::Button&, void) { css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue( u"AdditionsTag"_ustr, u"Icons"_ustr) }; comphelper::dispatchCommand(u".uno:AdditionsDialog"_ustr, aArgs); }
UNO dispatch commands can take parameters. As an example, take a look at .uno:NewDoc
defined in sfx2/sdi/sfx.sdi
:
SfxStringItem NewDoc SID_NEWDOC (SfxStringItem Region SID_TEMPLATE_REGIONNAME,SfxStringItem Name SID_TEMPLATE_NAME) [ ... ]
As you can see, there are two parameters:
SfxStringItem Region SID_TEMPLATE_REGIONNAME SfxStringItem Name SID_TEMPLATE_NAME
The SfxStringItem
is the type, Region and Name are the names of the parameters, SID_TEMPLATE_REGIONNAME
and SID_TEMPLATE_NAME
are the constants used for passing parameters. The parameter type is not limited to numbers and strings, and it can be any defined class.
To set and get data for this parameters, you may use appropriate Set and Put functions. For example, this gets the parameter if set:
const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE_REGIONNAME, false)
Or, this sets the data:
OUString sVal = u"test"_ustr; rSet.Put( SfxStringItem( SID_TEMPLATE_REGIONNAME, sVal ) );
To get to know better how to implement more complex UNO dispatch commands, you can refer to the implementation of the existing UNO commands to get idea about how they are implemented. You can see a comprehensive list of UNO commands here:
Our LibreOffice and Open Source Conference 2024 is taking place next week in Luxembourg, and one of the sponsors is Passbolt S.A., which makes an open source password manager. Kevin Muller, the company’s CEO, says:
We are excited to participate in the LibreOffice and Open Source Conference, where I will be speaking about the pivotal role open source has played in Passbolt’s commercial success.
Passbolt has been 100% open source from day one—and it always will be. This approach has given us a significant competitive edge, driving market adoption and commercialization from the outset. Compared to many competitors Passbolt’s open source philosophy offers unmatched transparency and control.
In today’s world, where nations are increasingly focused on reclaiming their digital sovereignty, the transparency and trust fostered by open source software are more critical than ever.
Kevin is giving a talk on the opening day, so check it out!
In my last post on Libreoffice I promised to talk about Writer changes once in a while, but then ... nothing ever happened. However, given that I had an annoying motorcycle accident in the meantime that turned out much more persistently annoying than originally thought, I think I have a bit of an excuse.
So ... what did happen? For one, I fixed quite a few regressions with my name on them, but ... is there much to talk about here? Mostly not: If you look at the fixes, they are often oneliners fixing something that seems rather obvious in retrospect. The more tricky question is: how did these get in in the first place? Its hard for me to say that, as the introducing commits are from even longer ago.
One thing is certain though: Often a unittest would have caught them, so whenever possible, I tried to create a reproducer adding such a test with the fix. To anyone writing bug reports: Creating minimal reproduction test is hugely valuable in this -- not just for finding the issue, but also as a starting point for a regression test. So if a bug bugs you and it is missing a minimal reproduction scenario, adding one is a great way to move this forward. Oh, and maybe verifying a bugfix, if someone provided a fix and the friendly bot say affected users are "encouraged to test the fix and report feedback".
While doing these fixes, I stumbled over Noel suggesting to speed up bookmarks in writer which is of course great, but I noticed that the code could be optimized a bit more as the bookmarks of a document are now sorted by their starting position (which was one of the first changes I made back on OpenOffice.org about more than a decade ago). Thus we can use bisectional search on the bookmarks here, which should be even faster. Now, it would be great if the discussion on this between Noel and me would available for others to learn from, wouldnt it? The cool thing is: it is.
All discussion happened on gerrit in the comments so if you want to learn about bookmark in Writer and how to maybe speed them up for documents that have a lot of them, that is a great starting point! Is there anything to add? Well maybe the following: Currently the bookmarks starting at the same position are currently not sorted. If one would sort them by their end position, the bisectional search could maybe cover even more? This would also remove one extra loop of logic and make the code simpler and easier to read.
The performance improvement is likely irrelevant -- esp. since there will be not that many documents with lots of bookmarks starting at the same position. The simpler code might be worth it though. So why wasnt it done?
It still can be tried in a follow-up, but speaking about regressions earlier: This has some obscure regression risk, because if we change the order of bookmarks starting at the same position from undefined to something ordered by the end position it might impact a lot of code using bookmarks. The function in question might actually be faster, but other functions (e.g. the inserting of new bookmarks) might actually be slower. So ... this is left as an exercise to the reader.
Comments? Feedback? Additions? Most welcome here on the fediverse !
Kudos to Ilmari Lauhakangas for helping to elaborate this list.
436 bugs, 53 of which are enhancements, have been reported by 258 people.
424 bugs have been triaged by 67 people.
475 bugs have been set to RESOLVED.
Check the following sections for more information about bugs resolved as FIXED, WORKSFORME and DUPLICATE.
213 bugs have been fixed by 45 people.
List of critical bugs fixed
List of high severity bugs fixed
List of crashes fixed
Writer now has support for doing partial layout passes when LOK clients have pending events, which sometimes improves interactivity a lot.
This work is primarily for Collabora Online, but the feature is useful for any LOK clients.
I recently worked with a document that has relatively simple structure, but it has 300 pages, and most of the content is part of a numbered list. Pasting a simple string (like an URL) into the end of a paragraph resulted in a short, but annoying hang. It turns out we updated Writer's layout for all the 300 pages before the content was repainted on the single visible page. In theory, you could reorder events, so you first calculate the first page, you paint the first page, then you calculate the remaining 299 pages. Is this possible in practice? Let's try!
The relevant part of the test document is simple: just an empty numbered paragraph, so we can paste somewhere:
This is a good sample, because pasting into a numbered list requires invalidating all list items in that list, since possibly the paste operation created a new list item, and then the number portion has to be updated for all items in the rest of the list. So if you paste into a numbered list, you need to re-calculate the entire document if all the document is just a numbered list.
The first problem was that Writer tracks its visible area, but LOK needs two kinds of visible areas. The first kind decides if invalidations are interesting for part of the document area. LOK wants to get all invalidations, so in case we cache some document content in the client that is near the visible area, we need to know when to throw away that cache. On the other hand, we want to still track the actually visible viewport of the client, so we can prioritize visible vs hidden parts of the document. Writer in LOK mode thought that all parts of the document are a priority, but this could improved by taking the client's viewport into account.
The second problem was that even if Writer had two layout passes (first is synchronous, for the visible area; second is async, for the rest of the document), both passes were performed before allowing a LOK client to request tiles for the issued invalidations.
This is now solved by a new registerAnyInputCallback()
API, which allows the LOK client to signal if
it has pending events (e.g. unprocessed callbacks, tiles to be painted) or it's OK for Writer layout
to finish its idle job first.
The end result for pasting a URL into this 300 pages document, when measuring end-to-end (from sending the paste command to getting the first updated tile) is a decrease in response time, from 963 ms to 14 ms.
If you would like to know a bit more about how this works, continue reading... :-)
As usual, the high-level problem was addressed by a series of small changes:
The tracking issue for this problem was cool#9735.
You can get a development edition of Collabora Online 24.04 and try it out yourself right now: try the development edition. Collabora intends to continue supporting and contributing to LibreOffice, the code is merged so we expect all of this work will be available in TDF's next release too (25.2).
When I translated one book about Python to Russian which contained many examples of Python code I though quite long how to highlight them in the normal text. For book writing I used LibreOffice Writer (of course) but Writer has no a standard tool for code highlighting.
So after some searching I found the LibreOffice extension - Code Highlighter 2. It is also available on our extension site. This extension makes code highlighting using Pygments Python library. There is support for many programming languages and many color styles for highlighting there.
The extension worked fine, but I didn't like that for highlighting I should manually select every code example in the text, then press some shortcut, then select another code example, etc...
I wrote an issue on the extension github page and after some discussions the extension author Jean-Marc Zambon implemented a new feature that allows to highlight all code example in the book in only one action using Paragraph style!
So my workflow in this case will be as follows:
Above you can see examples of the Code Highlighter work with some light and some dark styles.
by Roman Kuznetsov (noreply@blogger.com) at August 26, 2024 11:18 AM
In Collabora Online (for the normal mode of operation) we have a single server process (coolwsd) that spawns a separate process (kit) to load and manage each individual document. Each of those per-document kit processes runs in its own isolated environment. See architecture for details.
Each environment contains a minimal file system (ideally bind mounted from a template dir for speed, but linked/copied if not possible) that each kit chroots into, limiting its access to that subtree.
That chroot requires the CAP_SYS_CHROOT capability (and the desirable mount requires the CAP_SYS_ADMIN capability), and granting those capabilities to the coolforkit and coolmount binaries is a root privilege that, for typical deb/rpm packages, is done automatically at install time.
But it would be far more convenient not to require these capabilities to be set to do this isolation. They grant online more ability to affect its host system than it uses, we only want to mount dirs and chroot into dirs that belong to online and have no need or desire to make them available to any other process or user, and it's awkward, especially during development. to require root privileges to set these capabilities.
This scenario is not unique, and Linux provides namespaces, typically used by container implementations, to support achieving this. So recent work in Collabora Online leverages these namespaces to do its own layer of per-document kit isolation. (There's a good series of articles by Steve Ovens on the various namespaces, with the mount namespaces the most relevant one here.)
In essence, a user level process can create its own namespace in which it is apparently root from its own perspective, but as the original uid from the outside perspective and limited to operating on resources that the original uid is limited to accessing. So for each forkit, instead of requiring initial system capabilities and creating a system level bind mount we instead have no specific initial capabilities, enter a new namespace, unique to each forkit, in which that forkit becomes king of its own castle with apparent full capabilities, and can create bind mounts and chroot into its minimal file system.
Which is pretty magical to me as the whole existence of namespaces passed me by entirely without notice despite debuting over a decade ago.
Nothing is ever simple however, so some hurdles along the way.
Entering the namespace "requires that the calling process is not threaded" (man 2 unshare) which is not a problem for the normal use case in each kit, but did pose a problem for the test coolwsd does in advance to probe if there are working namespaces on the system in determine if it should operate kits in namespace mode or not. There it turned out that the Poco::Logger we use backups existing logs when it creates a new one, and then by default spawns a thread to compress the old log.
I initially had the vague notion that I could treat a namespace as a sort pseudo-sudo and switch back and forth freely between them, but that's not the model, typically it's a one way journey. But namespaces can be stacked instead with a namespace where the original uid is mapped to (apparent) root then containing another namespace where the user is mapped back to the original uid again. So we do that, each forkit enters its initial namespace and is mapped to root, does the mounts, enters another nested namespace mapped back to the original uid, chroots and drops all of the capabilities gained on entering a namespace. Which aligns the namespace mode with the expectations of the non-namespaces mode as to what effective uid the kit appears to run as.
The mounts that each forkit does are private to that forkit, so while in the non-namespace case the mounts are visible system-wide, in the namespace case the mounts are not visible either to other forkits or to the parent coolwsd. So how the document is provided by coolwsd to a child kit had to be adapted for the new mode of even less potential leakage between components.
There was a glitch in mounting, because when we bind mounts dirs from our system template we want them to be readonly, which requires the typical Linux 2 step process of mount and remount with readonly flags. This worked for the non namespace case, but failed for namespaces even though the initial mount succeeded. Here we had an extra flag of MS_NOATIME when remounting to potentially shave a little time off use of the kit jail, but in namespaces removing that option from the underlying system mount isn't permitted.
Despite that mount flag change giving working namespace-using kits directly inside toplevel OS, one of our lxc-using ci systems still refused to allow a readonly remount in a namespace to work. The catch here was that lxc is bundled with default apparmor rules which additionally restrict a readonly remount call to a certain set of arguments which our remount effort didn't match, so that had to be adjusted. Specifically the rather obscure MS_SILENT use.
Performance-wise, an unexpected (to me at least) side effect of using namespaces is that the coolwsd measurement of the time to spawn a forkit on my hardware has reduced from an average of 39.63ms per spawn to an average of an average of 6.15ms per spawn, which wasn't the primary goal but is a nice benefit.
Surveying distros where namespaces are available by default suggests:
RHEL/CENTOS
Debian
Ubuntu
Ubuntu 24.04 however, while supporting namespaces out of the box, has restricted namespaces via apparmor rules, which complicates things again so Collabora Online .deb packages install an apparmor profile to enable it to use namespaces out of the box.
Kudos to Ilmari Lauhakangas for helping to elaborate this list.
430 bugs, 64 of which are enhancements, have been reported by 245 people.
440 bugs have been triaged by 65 people.
403 bugs have been set to RESOLVED.
Check the following sections for more information about bugs resolved as FIXED, WORKSFORME and DUPLICATE.
162 bugs have been fixed by 39 people.
List of critical bugs fixed
List of high severity bugs fixed
Various functionalities of the LibreOffice are available through its programming interface, the UNO API. Here I discuss how to extend it.
Many functionalities of the LibreOffice is available through UNO API. You can write extensions and external programs that use LibreOffice functionality without the need to change the LibreOffice core source code.
Extensions work seamlessly with the software, and external applications can connect to the LibreOffice process and use it. The ability to do that depends on the UNO API.
On the other hand, some functionalities may not be available through this API. For example, newer features of the decent versions of LibreOffice, or functionalities that are not useful and/or important for external applications. Sometimes, you may want to use such functionalities elsewhere. Then you have to modify the LibreOffice core source code, and expose those functionalities through the API make them available to the external applications.
Let’s refer to the LibreOffice Developer’s Guide, which is mostly around the LibreOffice UNO API. There, you can read:
“The goal of UNO (Universal Network Objects) is to provide an environment for network objects across programming language and platform boundaries. UNO objects run and communicate everywhere.”
As UNO objects should be usable across different languages and platforms, they are described in an abstract meta language called UNOIDL (UNO interface definition language). This is similar to the IDL definitions in many other technologies like CORBA.
The API that I discuss here, provides functionality to control full screen functionality for top level windows. Stephan, experienced LibreOffice developer, added that API in this commit:
commit af5c4092052c98853b88cf886adb11b4a1532fff Expose WorkWindow fullscreen mode via new XTopWindow3 ...deriving from the existing XTopWindow2. (Exposing this functionality via UNO is useful e.g. for some embedded LOWA example application.)
The changes in this commit are over these files:
offapi/UnoApi_offapi.mk offapi/com/sun/star/awt/XTopWindow3.idl toolkit/inc/awt/vclxtopwindow.hxx toolkit/source/awt/vclxtopwindow.cxx
First one, offapi/UnoApi_offapi.mk
is needed to introduce the IDL file, according to its module, in a proper location. XTopWindow3.idl
is added in com/sun/star/awt
, which corresponds to com.sun.star.awt module. The other two, vclxtopwindow.hxx
and vclxtopwindow.cxx
are the implementation of the API in C++.
Let’s look into XTopWindow3.idl
:
module com { module sun { module star { module awt { /** extends XTopWindow with additional functionality @since LibreOffice 25.2 */ interface XTopWindow3: XTopWindow2 { /** controls whether the window is currently shown full screen */ [attribute] boolean FullScreen; }; }; }; }; };
As you may see, it contains these important information:
1. It is an interface, called XTopWindow3
.
2.It has a boolean attribute, FullScreen
.
3. This functionality will be available in LibreOffice 25.2 and later.
4. This interface extends XTopWindow
interface. You may find the documentation for XTopWindow in api.libreoffice.org.
More information about XTopWindow
interface can be found in XWindow
section of the LibreOffice Developer’s Guide, chapter 2.
C++ implementation basically consists of two functions to set and get the FullScreen
property:
sal_Bool VCLXTopWindow::getFullScreen() { SolarMutexGuard g; if (auto const win = VCLXContainer::GetAsDynamic()) { return win->IsFullScreenMode(); } return false; } void VCLXTopWindow::setFullScreen(sal_Bool value) { SolarMutexGuard g; if (auto const win = VCLXContainer::GetAsDynamic()) { return win->ShowFullScreenMode(value); } }
Some APIs are much more complex. The one that was discussed here was only an example to show the required things to extend UNO API. You can browse the API documentation in api.libreoffice.org for more complex APIs:
Writer now has improved support for font fallback when you open a DOCX file that refers to fonts which are not available currently.
This work is primarily for Collabora Online, but the feature is fully available in desktop Writer as well.
Font embedding is meant to solve the problems around missing fonts, but you can also find documents with stub embedded fonts that are to be ignored and our code didn't have any sanity check on such fonts, leading to unexpected glyph-level fallbacks. Additionally, once font-level fallback happened, we didn't take the font style (e.g. sans vs serif) into account, which is expected to work when finding a good replacement for the missing font.
Here is how to the original rendering looked like:
Once the handler for the embedded fonts in ODT/DOCX was improved to ignore stub fonts where even basic glyphs were not available, the result was a bit more consistent, but still bad. Here is a different document to show the problem:
Note how now we used the same font, but the glyphs are always sans, not serif. So the final step was to import the font type from DOCX and consider that while deciding font fallback:
With this, we ignore stub embedded fonts from DOCX, we import the font type and in general font fallback on Linux takes the font type into account while deciding font fallback.
If you would like to know a bit more about how this works, continue reading... :-)
As usual, the high-level problem was addressed by a series of small changes:
You can get a development edition of Collabora Online 24.04 and try it out yourself right now: try the development edition. Collabora intends to continue supporting and contributing to LibreOffice, the code is merged so we expect all of this work will be available in TDF's next release too (24.8).
"Michael, as the Christian & Hacker at Collabora Ltd you know how hard choosing the right global employment and work payment partner can be."
LibreOffice 24.8 will be released as final at the end of August, 2024 ( Check the Release Plan ) being LibreOffice 24.8 Release Candidate 2 (RC2) the forth and last pre-release since the development of version 24.8 started at the beginning of December, 2023. Since the previous release, LibreOffice 24.8 RC1, 138 commits have been submitted to the code repository and 87 issues got fixed. Check the release notes to find the new features included in this version of LibreOffice.
LibreOffice 24.8 RC2 can be downloaded for Linux, macOS and Windows, and it will replace the standard installation.
In case you find any problem in this pre-release, please report it in Bugzilla ( You just need a legit email account in order to create a new account ).
For help, you can contact the QA Team directly in the QA IRC channel or via Matrix.
LibreOffice is a volunteer-driven community project, so please help us to test – we appreciate it!
Happy testing!!
IMPORTANT INFORMATION FOR WINDOWS 7 USERS
Internal python version has been upgraded to python 3.9 which no longer supports Windows 7. Be aware some LibreOffice functionalities written in Python may not work, like the wizards in File – Wizards. Please, do test this version and give us feedback.
Here I discuss what fuzz testing is, and how LibreOffice developers use it incrementally to maintain LibreOffice code quality.
LibreOffice developers use various different methods and tools to maintain LibreOffice code quality. These are some of them:
1. Code review: Every patch from contributors should pass code review on Gerrit, and after conforming to coding standards and conventions, it can become part of the LibreOffice source code.
2. Static code checking: “Coverity Scan” continuously scans LibreOffice source code to find the possible defects. An automated script reports these issues to the LibreOffice developers mailing list so that developers can fix them.
3. Continuous Testing: There are various C++ unit test and Python UI tests in LibreOffice core source code to make sure that the functionalities of the software remain working during the later changes. They are also helpful for making sure that the fixed regressions do not happen again. These test run continuously for each and every Gerrit submission on CI machines via Jenkins.
4. Crash testing: A good way to make sure that LibreOffice works fine is to batch open and convert a huge set of documents. This task is done regularly, and if some failure occurs developers are informed to fix the issue.
5. Crash reporting: LibreOffice uses crash testing to find out about the recurrent crashes, and fix them.
6. Tinderbox Platforms: Using dedicated machines with various different architectures, LibreOffice developers make sure that LibreOffice source code builds and runs without problem on different platforms. Here is the description of tinderbox (TB) from TDF Wiki:
Tinderbox is a script to run un-attended build on multiple repos, for multiple branches and for gerrit patch review system.
You can see the build status here:
https://tinderbox.libreoffice.org/
7. Fuzz testing: LibreOffice software is checked continuously using Fuzz testing. This is essentially giving various automated inputs to the program to find the possible places in the code where problem occurs. Then, developers will become aware of the those problematic places in the code, and can fix them.
Fuzz testing on LibreOffice source code is active since 2017, and since then there has been various bug fixes for the problems that the fuzz tester reported. You can see more than 1500 of such fixes in the git log until now:
$ git shortlog -s -n --grep=ofz#
This tool can find various different problems. These issues are then filed in a section of Chromium bug tracker, and after ~30 days, they are made public. When developers fix bugs of this kind, they refer to the issue number (for example 321) as ofz#321. A comprehensive list of all issues found is visible here:
Let’s look at one of the fixes. You can find commits related to fuzzing with:
$ git log --grep=ofz
This is a recent fix from Caolán, an experienced LibreOffice developer that provided most of the fixes found through oss-fuzz:
commit d30ecb5fb07f005ebd944e864f0a15678289a4ed ofz#69809 Integer-overflow --- a/filter/source/graphicfilter/icgm/cgm.cxx +++ b/filter/source/graphicfilter/icgm/cgm.cxx @@ -227,7 +227,7 @@ double CGM::ImplGetFloat( RealPrecision eRealPrecision, sal_uInt32 nRealSize ) else { sal_Int32* pLong = static_cast<sal_Int32*>(pPtr); - nRetValue = static_cast(abs( pLong[ nSwitch ] )); + nRetValue = fabs(static_cast(pLong[nSwitch])); nRetValue *= 65536; nVal = static_cast( pLong[ nSwitch ^ 1 ] ); nVal >>= 16;
As you can see, using abs()
first, and then casting to double
is changed in this commit to cast to double
first, and then using fabs()
. The reason of this change lies in the data type of some variables.
pLong
is an array of sal_Int32
, which is 32 bit signed integer. It can take values from -2,147,483,648
to 2,147,483,647
. As you can see, the smallest negative 32-bit signed integer can not be stored in the same 32-bit signed variable if abs()
is used to remove sign from that.
As the result is stored in nRetValue
, a varible of type double
, it is possible to first cast the array item to double
, and then use floating point version of absolute function, fabs()
over it. In this way, “integer overflow” will not happen anymore.
This patch was one of the smallest examples of what a fix can be. There are many bugs that are more complex, and require more careful examination to provide a fix.
You can read more in the TDF Wiki article around fuzz testing in LibreOffice. Also, OSS-Fuzz documentation is a good place to look into:
In the previous blog post, I provided a brief introduction to LibreOfficeKit API which one can use for accessing LibreOffice functionalities in an external application. Here I discuss in detail how to use LibreOfficeKit for converting an ODT to PDF, or from/to virtually any other format that LibreOffice supports.
For this example, you only need one header: LibreOfficeKit/LibreOfficeKit.hxx
. Include it, and that is enough. This header will be available in the future versions of LibreOffice community. But for now, you may need to download LibreOfficeKit headers folder from LibreOffice core source code, and put it alongside your source code.
The example is relatively short. Here it is:
#include <iostream> #include <LibreOfficeKit/LibreOfficeKit.hxx> int main(int argc, char *argv[]) { if(argc < 2) { std::cout << "Usage: lokconvert \n"; return 1; } const char *input = argv[1]; const char *output = argv[2]; lok::Office * llo = NULL; try { const char lo_path[] = LOROOT "/program/"; llo = lok::lok_cpp_init(lo_path); if (!llo) { std::cerr << "Error: could not initialize LibreOfficeKit\n"; return 1; } lok::Document * lodoc = llo->documentLoad(input, NULL /* options */); if (!lodoc) { std::cerr << "Error: could not load document: " << llo->getError() << "\n"; return 1; } if (!lodoc->saveAs(output, "pdf", NULL /* options */)) { std::cerr << "Error: could not export document: " << llo->getError() << "\n"; return 1; } } catch (const std::exception & e) { std::cerr << "Error: LibreOfficeKit exception: " << e.what() << "\n"; return 1; } std::cerr << "Success!\n"; return 0; }
The example is simple. Input and output file names are passed via command line arguments, and are received via argv[1]
and argv[2]
, if argument count, argc
, is at least 3. First one is the program name itself, which we don’t use here.
LibreOffice binary folder is needed here, so you should set OO_SDK_URE_BIN_DIR
environment variable, or you may even set that in your program itself.
This part of the code is for LibreOfficeKit initialization:
llo = lok::lok_cpp_init(lo_bin_dir);
And after that, it is the time to load the document:
lok::Document* lodoc = llo->documentLoad(input, NULL /* options */);
Having the lodoc
pointer is necessary for converting the document using saveAs
function:
lodoc->saveAs(output, "pdf", NULL /* options */)
As you can see, relevant code is inside a try/catch block. In case some exception occur, you will see relevant error information on the console. Also, in each step, from initializing LibreOfficeKit, to loading and in the end generating the output, there are error handling mechanisms to make sure that the work goes forward smoothly.
In the end, if everything is successful, you will see this message on the screen:
Success!
You can use cmake to build this example. Here is an example CMakeLists.txt which also provides LO_ROOT
and LO_SDK_ROOT
to be used inside C++ file, as an alternative to setting it via an environment variable, or putting it manually in the code.
cmake_minimum_required(VERSION 3.5) project(lokconv LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(lokconv main.cpp) include(GNUInstallDirs) set(LOVERSION 24.2) if(WIN32) set(LOROOT C:/Progra~1/LibreOffice) set(LOSDKROOT ${LOROOT}/sdk) add_definitions(-DWIN32) elseif(APPLE) set(LOROOT /Application/LibreOffice.app/Contents/Frameworks) set(LOSDKROOT /Application/LibreOffice${LOVERSION}_SDK) add_definitions(-DMACOSX) else() set(LOROOT /opt/libreoffice${LOVERSION}) set(LOSDKROOT ${LOROOT}/sdk) add_definitions(-DLINUX) endif() add_compile_definitions(LOK_USE_UNSTABLE_API) target_compile_definitions(lokconv PRIVATE LOROOT="${LOROOT}") include_directories(lokconv PRIVATE ${LOSDKROOT}/include )
There are many nice things that can be done with LibreOfficeKit, but you may have to enable LOK_USE_UNSTABLE_API
to be able to use those functionalities. This symbol is visible in the above cmake file. But, in the C++ example I did not use any of such functions. I will discuss about these functionalities in later blog posts.
A while ago, Simon Phipps, member of the Board of Directors at The Document Foundation, shared the idea to introduce a peer-to-peer collaboration built in to desktop LibreOffice without the requirement for a cloud provider. This idea has received a lot of attention inside the organization and the design team has started to outline the project now.…
LibreOffice 24.8 will be released as final at the end of August, 2024 ( Check the Release Plan ) being LibreOffice 24.8 Release Candidate 1 (RC1) the third pre-release since the development of version 24.8 started at the beginning of December, 2023. Since the previous release, LibreOffice 24.8 Beta1, 243 commits have been submitted to the code repository and 120 issues got fixed. Check the release notes to find the new features included in this version of LibreOffice.
LibreOffice 24.8 RC1 can be downloaded for Linux, macOS and Windows, and it will replace the standard installation.
In case you find any problem in this pre-release, please report it in Bugzilla ( You just need a legit email account in order to create a new account ).
For help, you can contact the QA Team directly in the QA IRC channel or via Matrix.
LibreOffice is a volunteer-driven community project, so please help us to test – we appreciate it!
Happy testing!!
IMPORTANT INFORMATION FOR WINDOWS 7 USERS
Internal python version has been upgraded to python 3.9 which no longer supports Windows 7. Be aware some LibreOffice functionalities written in Python may not work, like the wizards in File – Wizards. Please, do test this version and give us feedback.
Writer now has improved support for toplevel line shapes when you import those from DOCX.
This work is primarily for Collabora Online, but the feature is fully available in desktop Writer as well.
As described in a post from 2014, Writer reads the drawingML markup for shapes in DOCX files, including line shapes. While investigating a simple-looking problem around a horizontal vs vertical line, it turns out that there is a deeper issue here, and it looks like now have proper fix for this bug.
Imagine that your company template has a nice footer in two columns, and the content in the columns are separated by a vertical line shape, but when you open your DOCX in Writer, it crosses the text of that footer instead:
While researching how line shapes are represented in our document model and how ODT import works, it turned out that the proper way to create a line shape is to only consider size / scaling when it comes to the individual points of the line, everything else (e.g. position / translation) should go to the transform matrix of the shape, then the render result will be as expected:
It was also interesting to see that this also improved other, existing test documents, e.g. core.git
sw/qa/extras/ooxmlimport/data/line-rotation.docx
looked like this before:
And the same fix makes it perfect:
Just stick to the rule: scaling goes to the points -- translation, rotation and horizontal shear goes to the shape.
For now, this is only there for toplevel Writer lines, but in-groupshape and Calc/Impress lines could also follow this technique if there is a practical need.
The "after" screenshots show ~no red, which means there is ~no reference output, where the Writer output would be missing.
If you would like to know a bit more about how this works, continue reading... :-)
The bugfix commit was tdf#161779 DOCX import, drawingML: fix handling of translation for lines.
The tracking bug was tdf#161779.
You can get a development edition of Collabora Online 24.04 and try it out yourself right now: try the development edition. Collabora intends to continue supporting and contributing to LibreOffice, the code is merged so we expect all of this work will be available in TDF's next release too (24.8).
Kudos to Ilmari Lauhakangas for helping to elaborate this list.
432 bugs, 43 of which are enhancements, have been reported by 275 people.
421 bugs have been triaged by 52 people.
436 bugs have been set to RESOLVED.
Check the following sections for more information about bugs resolved as FIXED, WORKSFORME and DUPLICATE.
177 bugs have been fixed by 36 people.
List of critical bugs fixed
List of high severity bugs fixed
List of crashes fixed
List of performance issues fixed
If you want to use LibreOffice functionality in your applications, LibreOfficeKit API is one of the good ways to do that. Here I describe how, with some examples. If you want to add the capability of loading, displaying, editing, saving and/or converting LibreOffice/MS Office files to your application, you have come to a good place.
LibreOfficeKit is the (relatively) new API to access LibreOffice functionalities in C/C++ without the need of older UNO API and its dependencies. It is the underlying technology for LibreOffice Online, and also LibreOffice Viewer for Android.
LibreOffice functionality is usable via the older UNO API, which is several years old and provides many functionalities of LibreOffice through network or sockets. But UNO API is not helpful if you want to have the LibreOffice UI in your application. In comparison, LibreOfficeKit is capable of doing that.
LibreOfficeKit can create tiles from the LibreOffice, as a static library. It lets the user interact with LibreOffice from somewhere else, either different application, or a web browser.
In this way, it becomes much easier to create applications that incorporate the actual functionalities of LibreOffice in them, while having the LibreOffice UI in themselves.
As an example, gtktiledviewer provides a small editor that can load the LibreOffice/MS Office files, display them and the user can actually edit those files.
If you have a working build of LibreOffice, you can run thegtktiledviewer
application this way:
bin/run gtktiledviewer --lo-path=$PWD/instdir/program path/to/test.odt
Building such an application will be much easier compared to building and running LibreOffice UNO API applications which require many dependencies.
In order to compile LibreOfficeKit applications, you need LibreOfficeKit headers. These headers will be shipped with the next version of LibreOffice community distribution, LibreOffice 24.8.
Another interesting example is document conversion. For example, if you want to convert an ODT file to PDF or other formats with LibreOffice, you can use LibreOfficeKit API to do that.
One example code that does that is the lloconv project. You may find it on gitlab:
The conversion code happens inside convert.cc source file, and it is pretty straightforward, although there are some extra code which handles finding LibreOffice binary path, and parsing command line arguments.
The important functions that are used in loading and converting documents can be found in these lines:
Office * llo = lok_cpp_init(lo_path);
And also:
Document * lodoc = llo->documentLoad(input_url.c_str(), options);
And at last:
lodoc->saveAs(output_url.c_str(), format, options)
It essentially initializes the document vialok_cpp_init
, then loads the document using documentLoad function, and at last convert it to the final file format using saveAs() function.
The API for conversion is pretty straightforward, and it is enough to to know the path to the LibreOffice installation path to be able to compile and run the C++ example.
More interesting things are possible with LibreOfficeKit API, and I will write more about them.
Earlier this month, we were pleased to sponsor the Libreoffice Technology Hackfest in Budapest, Hungary, and enjoyed meeting up with some of our fellow LibreOffice Technology hackers. Over two days, a dozen developers from Collabora Productivity and the wider community met up in the Eco Community Space to work on the LibreOffice codebase, and reap the benefits of spending time together.
A hackfest is an event where developers from multiple organisations meet each other, work on what they want and also more freely exchange ideas while being together in person. While having an international community working remotely on the codebase is excellent, there are still many benefits to more directly seeing what problems are being tackled by other developer sitting next to you; and this friendly environment allows building relationships that can then help even more in the future (even remotely).
As one attendee Miklos Vajna shared with us after the event, “It was really great to spend a couple of days with the other developers. I found it very helpful seeing what other people are working on, sharing ideas about the future feature possibilities, and especially enjoyed going out for a dinner with everyone in Budapest after a hard day’s work!”
For this reason, we were very pleased to sponsor this most recent meet up. Many thanks to all who joined us in Budapest, we look forward to seeing you soon at the next meeting!
If you would like to find out more about joining the Collabora Online or LibreOffice community, we would encourage you to join the Collabora Online Community Forum or have a look at the Collabora Online Github to learn about how to get started.
For more information about our upcoming events, and to learn where you could meet us next, do have a look at our events page.
Shortcuts are a major topic for user experience. Novices are advised to learn basic shortcuts beyond the famous Ctrl+C/X/V like Ctrl+1/2/3.. to quickly change the paragraph style to heading 1/2/3… in Writer. Once you have learned those combinations you never want to unlearn and to change the muscle memory.…
Writer now has much better support for continuous / inline endnotes (not on a separate page) in Writer, enabled by default for DOCX files.
This work is primarily for Collabora Online, but the feature is fully available in desktop Writer as well.
As described in a previous post, Writer already had minimal support for not rendering endnotes on a separate endnote page, but it was not mature enough to enable is by default for DOCX files.
What changed from the previous "continuous endnotes" approach is that instead of trying to map endnotes to footnotes, we now create a special endnotes section, which only exists at a layout level (no section node is backing this one), and this hosts all endnotes at the end of the document. It turns out this is a much more scalable technique, for example a stress-test with 72 endnotes over several pages is now handled just fine.
Here are some screenshots:
As you can see, there were various differences for this document, but the most problematic one was that the entire endnote was missing from the (originally) last page, as it was rendered on a separate page.
Now it's not only on the correct page, but also its position is correct: the endnote is after the body text, while the footnote is at the bottom of the page, as expected. The second screenshot shows ~no red, which means there is ~no reference output, where the Writer output would be missing.
If you would like to know a bit more about how this works, continue reading... :-)
As usual, the high-level problem was addressed by a series of small changes:
CppunitTest_sw_layoutwriter3
<w:endnotePr>
pos == sectEnd<w:endnotePr>
pos == sectEndThe tracking bug was tdf#160984.
You can get a development edition of Collabora Online 24.04 and try it out yourself right now: try the development edition. Collabora intends to continue supporting and contributing to LibreOffice, the code is merged so we expect all of this work will be available in TDF's next release too (24.8).
After resigning from the Board of Directors of TDF over the weekend, I hope I will again find more time to look into the technical details of LibreOffice Writer. I will also try to do my best to write some good article here about the depth of that application. While a text processor in itself is not that interesting anymore these days, the challenges of migrating that big old legacy code might be fascinating quite often. Hope to have you as a reader for that soon!
Comments? Feedback? Additions? Most welcome here on the fediverse !
LOWA is LibreOffice built with Emscripten as a Wasm executable that runs in the browser. Controlling that LibreOffice through UNO with JavaScript looks like a natural fit. Enter Embind, a mechanism to generate the binding glue between JavaScript and Wasm/C++.
As we will see, the Embind vs. UNO match is not perfect, but it kind-of gets the job done, at least for a first iteration.
To dive straight into technical matters, the UNO type system is mapped to JavaScript as follows. (If you would like to see some example code first, jump ahead to the Starting Points and come back here later for reference.)
BOOLEAN
, depending on context and somewhat inconsistently maps to JavaScript Boolean
and to JavaScript Number
values 0 and 1. (The C/C++ representation of UNO BOOLEAN
is sal_Bool
, which is an alias for unsigned char
, which Embind maps to JavaScript Number
. So in places where we directly rely on Embind, like for the return value of a UNO interface method invocation, we get the Embind mapping to Number
. But in places where we have more control, like for the JavaScript get
method for a UNO ANY
, we can be a bit more fancy and use a mapping to Boolean
.)BYTE
, SHORT
, UNSIGNED SHORT
, LONG
, UNSIGNED LONG
, FLOAT
, and DOUBLE
all map to JavaScript Number
(with restricted value ranges for everything but UNO DOUBLE
).HYPER
and UNSIGNED HYPER
both map to JavaScript BigInt
(with restricted value ranges).CHAR
and STRING
both map to JavaScript String
(with single UTF-16 code unit strings for UNO CHAR
).TYPE
maps to JavaScript Module.uno_Type
objects. There are construction functions Module.uno_Type.Void
, Module.uno_Type.Boolean
, Module.uno_Type.Byte
, Module.uno_Type.Short
, Module.uno_Type.UnsignedShort
, Module.uno_Type.Long
, Module.uno_Type.UnsignedLong
, Module.uno_Type.Hyper
, Module.uno_Type.UnsignedHyper
, Module.uno_Type.Float
, Module.uno_Type.Double
, Module.uno_Type.Char
, Module.uno_Type.String
, Module.uno_Type.Type
, Module.uno_Type.Any
, Module.uno_Type.Sequence
, Module.uno_Type.Enum
, Module.uno_Type.Struct
, Module.uno_Type.Exception
, and Module.uno_Type.Interface
for representations of all the UNO TYPE
values. The Module.uno_Type.Sequence
construction function recursively takes a UNO TYPE
argument for the component type, while the Module.uno_Type.Enum
, Module.uno_Type.Struct
, Module.uno_Type.Exception
, and Module.uno_Type.Interface
construction functions each take a string argument denoting the given type’s name in dotted notation (e.g., Module.uno_Type.Interface('com.sun.star.uno.XInterface')
). Those JavaScript objects implement toString
, which is also used for equality checks (e.g., type === 'com.sun.star.uno.XInterface'
).ANY
maps to JavaScript Module.uno_Any
objects. There is a constructor taking a UNO TYPE
argument and a corresponding value (using an undefined
value for UNO type VOID
). Those JavaScript objects implement a method get
that returns the JavaScript representation of the contained UNO value.Module.uno_Sequence_...
objects. The problem is that Embind does not let us have a generic mapping to the C++ com::sun::star::uno::Sequence<T>
class template; we can only have individual Embind mappings to specific class template instantiations. As a hack, for every UNO sequence type that appears somewhere in the LibreOffice UNO API, we generate a specific JavaScript Module.uno_Sequence_...
. The naming is Module.uno_Sequence_boolean
, Module.uno_Sequence_byte
, Module.uno_Sequence_short
, Module.uno_Sequence_unsigned_short
, Module.uno_Sequence_long
, Module.uno_Sequence_unsigned_long
, Module.uno_Sequence_hyper
, Module.uno_Sequence_unsigned_hyper
, Module.uno_Sequence_float
, Module.uno_Sequence_double
, Module.uno_Sequence_char
, Module.uno_Sequence_string
, Module.uno_Sequence_type
, and Module.uno_Sequence_any
for the simple UNO component types; Module.uno_Sequence_...
followed by the UNO type name in dollar-separated notation (e.g., Module.uno_Sequence_com$sun$star$uno$XInterface
) for enum, struct, and interface component types; and Module.uno_SequenceN_...
, with N greater than 1, for sequence component types (e.g., Module.uno_Sequence2_long
for the UNO type “sequence of sequence of LONG
“). That means that there currently is just no way to come up with e.g. a JavaScript representation of the UNO type “sequence of interface com.sun.star.frame.XDesktop
“, as that sequence type happens to not be mentioned anywhere in the LibreOffice UNO API. (But for those sequence types that are used as interface method parameter or return types, corresponding JavaScript representations are provided. That should hopefully cover all relevant use cases for now; a future overhaul of this part of the mapping is likely.) These JavaScript sequence objects have two constructors, one taking a JavaScript array of member values (e.g., new Module.uno_Sequence_long([1, 2, 3])
) and one taking a size and a Module.FromSize
marker (as Emind does not allow to have multiple constructors with the same number of arguments) whose members will have default values (e.g., new Module.uno_Sequence_long(3, Module.FromSize)
). Additional methods are resize
(taking the new length as argument), size
(returning the current length), get
(taking an index as argument and returning the member at that index), and set
(taking an index and a new member value as arguments). (The naming of those resize
, size
, get
, and set
methods is modelled after Embind’s emscripten::register_vector
.)Module.uno_Type_...
followed by the UNO type name in dollar-separated notation (e.g., Module.uno_Type_com$sun$star$uno$TypeClass
).Module.uno_Type_...
followed by the UNO type name in dollar-separated notation (e.g., Module.uno_Type_com$sun$star$beans$NamedValue
, Module.uno_Type_com$sun$star$uno$Exception
). Polymorphic UNO struct types face a similar issue to sequence types, in that Embind does not allow to directly map their corresponding C++ class templates. It would be possible to do a similar hack and add specific mappings for all instantiated polymorphic struct types that are mentioned anywhere in the LibreOffice UNO API, but that has not been implemented so far. (And, similar to sequence types, a future overhaul of this part of the mapping is likely.)Module.uno_Type_...
followed by the UNO type name in dollar-separated notation (e.g., Module.uno_Type_com$sun$star$uno$XInterface
). Null references are mapped to JavaScript null
. The special com.sun.star.uno.XInterface
UNO interface methods queryInterface
, acquire
, and release
are not exposed to JavaScript client code.Module.uno_Function_...$$...
followed by the service’s name in dollar-separated notation, followed by the constructor’s name set of by two dollar signs (e.g., Module.uno_Function_com$sun$star$beans$Introspection$$create
). Like with other UNO language bindings, those functions take the com.sun.star.uno.XComponentContext
as an additional first argument.Module.uno_Function_...
followed by the singleton’s name in dollar-separated notation (e.g., Module.uno_Function_com$sun$star$frame$theDesktop
). Like with other UNO language bindings, those functions take the com.sun.star.uno.XComponentContext
as their (sole) argument.To make all this work, the Embind mapping of the LibreOffice UNO API needs to be set up first. This is done by a call to
const uno = init_unoembind_uno(Module);
which also returns a wrapper object uno
that allows for more natural access to all the UNOIDL entities whose mappings use that dollar-separated notation: Instead of Module.uno_Type_com$sun$star$uno$XInterface
one can write uno.com.sun.star.uno.XInterface
, and a call to uno_Function_com$sun$star$beans$Introspection$$create(context)
can be written as uno.com.sun.star.beans.Introspection.create(context)
. If you want to cut down on the common uno.com.sun.star
prefix even further,
const css = uno.com.sun.star;
lets you reduce that to just css.uno.XInterface
and css.beans.Introspection.create(context)
.
The starting points to access the LibreOffice UNO API from JavaScript are Module.getUnoComponentContext()
(returning the central css.uno.XComponentContext
, through which all the services and singletons are reachable) and a Module.getCurrentModelFromViewSh()
convenience function (returning the css.frame.XModel
of the currently showing document). The gitlab.com/allotropia/lowa-demos
repository is a growing site of example code showing all of this in action.
Summing this up, here is some example code that iterates over all the paragraphs of a Writer document and gives each of them a random foreground text color:
const uno = init_unoembind_uno(Module);
const css = uno.com.sun.star;
const model = Module.getCurrentModelFromViewSh();
const document = css.text.XTextDocument.query(model);
const text = document.getText();
const access = css.container.XEnumerationAccess.query(text);
const paragraphs = access.createEnumeration();
while (paragraphs.hasMoreElements()) {
const paragraph = css.text.XTextRange.query(
paragraphs.nextElement().get());
const props = css.beans.XPropertySet.query(paragraph);
const color = new Module.uno_Any(
Module.uno_Type.Long(),
Math.floor(Math.random() * 0xFFFFFF));
props.setPropertyValue("CharColor", color);
color.delete();
}
Embind is built on the concept that whatever C++ objects you reference from JavaScript, you manually and explicitly need to declare those references as no longer needed once you are done, by calling delete()
methods on the corresponding JavaScript objects. (Or else, you risk memory leaks.) This can be quite cumbersome and would pollute the code with tons of such delete()
calls. Luckily, JavaScript grew a FinalizationRegistry
mechanism that allows code to be executed when the JavaScript garbage collector finds an objet to be unused and reclaims it. (And that code can thus transparently do the delete()
call for us.) Embind implements such FinalizationRegistry
-support for some types (those that are modelled based on some “smart poiner”) but not for others.
That means that (besides all the primitive types) JavaScript mappings of UNO string, type, enums, sequences, exceptions, and interfaces all do not need explicit delete()
calls, while the mappings of UNO any and UNO sequences, and the various Module.uno_InOutParam_...
all need explicit delete()
calls.
Even though we expect that the JavaScript engines that we target do support the FinalizationRegistry
mechanism, Embind is prepared to work with older engines that do not support it. Therefore, whenever an object is transparently cleaned up, Embind logs a somewhat unhelpful warning to the JavaScript console, stating that it “found a leaked C++ instance” (and that it will “free it automatically”).
For each UNO interface type there is a JavaScript class method query
taking any JavaScript UNO object reference (in the form of the common com.sun.star.uno.XInterface
base interface) as argument (and internally using UNO’s queryInterface
to obtain either a correspondingly-typed reference to that object, or a null reference). There is also a JavaScript helper function Module.sameUnoObject
, taking two interface references as arguments and returning whether both are references to the same UNO object.
UNO interface methods taking out or in-out parameters need special treatment. There are Module.uno_InOutParam_...
wrappers (with a val
property carrying the actual value) that need to be set up and passed into the UNO method. Such wrappers have a constructor taking no arguments (creating a dummy object, suitable for pure out parameters) and another constructor taking one argument of the wrapped type (suitable for in-out parameters). For example, to read data from a com.sun.star.io.XInputStream
:
const stream = ...;
const input = css.io.XInputStream.query(stream);
if (input) {
const data = new Module.uno_InOutParam_sequence_byte;
input.readBytes(data, 100);
for (let i = 0; i != data.val.size(); ++i) {
console.log('read byte ' + data.val.get(i));
}
data.delete();
}
Support for throwing and catching exceptions between JavaScript and C++ is rather rough: JavaScript code can use try ... catch (e) ...
to catch a UNO exception thrown from C++, but all the information it can get about that exception is e.name
stating the exception’s type. Also, for technical reasons, the catch block needs some increment
– and decrementExceptionRefcount
boilerplate,
try {
...
} catch (e) {
incrementExceptionRefcount(e);
//TODO, needed when building with JS-based -fexceptions,
// see
// <https://github.com/emscripten-core/emscripten/issues/17115>
// "[EH] Fix inconsistency of refcounting in Emscripten
// EH vs. Wasm EH"
if (e.name === 'com::sun::star::uno::RuntimeException') {
...
}
decrementExceptionRefcount(e);
}
To throw UNO exceptions from JavaScript code, there is a helper function Module.throwUnoException
that takes a UNO (exception) type and an instance of that type:
Module.throwUnoException(
Module.uno_Type.Exception(
'com.sun.star.lang.IllegalArgumentException'),
{Message: 'bad argument', Context: null,
ArgumentPosition: 0});
The JavaSript-to-UNO binding is a full mapping, so you can even implement new UNO objects in JavaScript. This requires quite some boilerplate, though. For example, the below obj
implements com.sun.star.lang.XTypeProvider
and com.sun.star.task.XJob
:
const obj = {
// Implementation details:
implRefcount: 0,
implTypes: new Module.uno_Sequence_type([
Module.uno_Type.Interface(
'com.sun.star.lang.XTypeProvider'),
Module.uno_Type.Interface(
'com.sun.star.task.XJob')]),
implImplementationId: new Module.uno_Sequence_byte([]),
// The methods of XInterface:
queryInterface(type) {
if (type == 'com.sun.star.uno.XInterface') {
return new Module.uno_Any(
type,
css.uno.XInterface.reference(
this.implXTypeProvider));
} else if (type == 'com.sun.star.lang.XTypeProvider') {
return new Module.uno_Any(
type,
css.lang.XTypeProvider.reference(
this.implXTypeProvider));
} else if (type == 'com.sun.star.task.XJob') {
return new Module.uno_Any(
type,
css.task.XJob.reference(
this.implXJob));
} else {
return new Module.uno_Any(
Module.uno_Type.Void(), undefined);
}
},
acquire() { ++this.implRefcount; },
release() {
if (--this.implRefcount === 0) {
this.implXTypeProvider.delete();
this.implXJob.delete();
this.implTypes.delete();
this.implImplementationId.delete();
}
},
// The methods of XTypeProvider:
getTypes() { return this.implTypes; },
getImplementationId() {
return this.implImplementationId;
},
// The methods of XJob:
execute(args) {
if (args.size() !== 1 || args.get(0).Name !== 'name') {
Module.throwUnoException(
Module.uno_Type.Exception(
'com.sun.star.lang.IllegalArgumentException'),
{Message: 'bad args', Context: null,
ArgumentPosition: 0});
}
console.log(
'Hello, my name is ' + args.get(0).Value.get());
return new Module.uno_Any(
Module.uno_Type.Void(), undefined);
},
};
obj.implXTypeProvider
= css.lang.XTypeProvider.implement(obj);
obj.implXJob
= css.task.XJob.implement(obj);
obj.acquire();
// ... pass obj to UNO here ...
obj.release();
LibreOffice 24.2 Writer Guide has been published. It is available for free download (PDF, ODT) from the Documentation page on the LibreOffice website.
Printed copies can be purchased from the Documentation Team’s store at Lulu.com.
The numbering system for LibreOffice releases has changed to year.month. LibreOffice will continue to be updated twice a year, but current plans are to update each user guide only once a year.
LibreOffice 7.6 Getting Started Guide has been published. It is available for free download (PDF, ODT) from the Documentation page on the LibreOffice website.
Printed copies can be purchased from the Documentation Team’s store at Lulu.com.
After a long slog since November when the previous version of coverity was EOLed and we had to start using 2022.6.0 with its new suggestions for std::move etc, LibreOffice is now finally back to a 0 warnings coverity state
by caolan (noreply@blogger.com) at February 11, 2024 06:02 PM
by Popa Adrian Marius (noreply@blogger.com) at January 25, 2024 10:29 AM
Using comments is a key feature in text processing. A typical workflow might be to review a document where notes are made by different colleagues. LibreOffice Writer currently shows these comments in the document margin, which is limited to the page height, ending up in the need to scroll long text (even while editing [1]) and eventually in paging-like interactions if the number of comments exceed the total size.…
by Popa Adrian Marius (noreply@blogger.com) at December 18, 2023 07:20 PM
The LibreOffice 7.6 Calc Guide has been published. It is available for free download (PDF, ODT) from the Documentation page on the LibreOffice website.
Printed copies can be purchased from the Documentation Team’s store at Lulu.com.