Welcome to The Document Foundation Planet

This is a feed aggregator that collects what LibreOffice and Document Foundation contributors are writing in their respective blogs.

To have your blog added to this aggregator, please mail the website@global.libreoffice.org mailinglist or file a ticket in Redmine.


Wednesday
22 October, 2025


face

In LibreOffice C++ code, there are many cases where developers want to use string literals in their code. If these are messages in the graphical user interface (GUI), they should add them to the translatable messages. But, there are many cases where the string literals has nothing to do with other languages, and there will not be any further translations. In these cases, enumarray is helpful. Although enumarray can be used beyond string literals, for any kind of data.

Using Symbolic Constants

In old C code, using #define was the preferred way one could give a name to a string literal or other kinds of data. For example, consider this code:

const char[] FRAME_PROPNAME_ASCII_DISPATCHRECORDERSUPPLIER = "DispatchRecorderSupplier";
const char[] FRAME_PROPNAME_ASCII_ISHIDDEN = "IsHidden";
inline constexpr OUString FRAME_PROPNAME_ASCII_LAYOUTMANAGER = "LayoutManager";
const char[] FRAME_PROPNAME_ASCII_TITLE = "Title"_ustr;
const char[] FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION = "IndicatorInterception";
const char[] FRAME_PROPNAME_ASCII_URL = "URL";

And also, the relevant states:

#define FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER 0
#define FRAME_PROPHANDLE_ISHIDDEN 1
#define FRAME_PROPHANDLE_LAYOUTMANAGER 2
#define FRAME_PROPHANDLE_TITLE 3
#define FRAME_PROPHANDLE_INDICATORINTERCEPTION 4
#define FRAME_PROPHANDLE_URL 5

Although this C code still works in C++, it is not the desired approach in modern C++.

Using enumarrays

In modern C++ code, you can use enumarry from o3tl library in LibreOffice. The above code becomes:

enum class FramePropNameASCII
{
    DispatcherRecorderSupplier,
    IsHidden,
    LayoutManager,
    Title,
    IndicatorInterception,
    URL,
    LAST=URL
};

And also, the string literal definitions:

constexpr o3tl::enumarray<FramePropNameASCII, OUString> FramePropName = {
    u"DispatchRecorderSupplier"_ustr,
    u"IsHidden"_ustr,
    u"LayoutManager"_ustr,
    u"Title"_ustr,
    u"IndicatorInterception"_ustr,
    u"URL"_ustr
};

Why an enumarray?

The names are much more readable, as they do not have to be ALL_CAPPS, as per convention for symbolic constants in C. Their usage is also quite easy. For example, one can use [] to access the relevant string literal:

- xPropSet->getPropertyValue( FRAME_PROPNAME_ASCII_LAYOUTMANAGER ) >>= xLayoutManager;
+ xPropSet->getPropertyValue( FramePropName[FramePropNameASCII::LayoutManager] ) >>= xLayoutManager;

Final Notes

In LibreOffice, enumarrays are not limited to string literals, and they can be used with other data. This task is tdf#169155, and if you like, you may try finding some instances in the code and modernize it using enumarrays.

To learn more about LibreOffice development, you can refer to TDF Wiki. You may follow this blog to read about EasyHacks, tutorials and announcements related to LibreOffice development.


Tuesday
21 October, 2025


face

General Activities LibreOffice 25.2.6 was announced on September 8 Olivier Hallot (TDF) improved the help for Select Function in Calc’s formula bar, expanded help for the selection of chart data sources, added AutoFilter and Pivot Table/Chart to the help page on sheet protection, added information about summary above/below to the


Thursday
16 October, 2025


face

Since C++11 when enum class (also named scoped enum) is introduced, it is preferred to plain enum which is inherited from C programming languages. The task here is to convert the old enum instances to enum class.

Rationale

enum class has many benefits when compared to plain enum, as it provides better type safety among other things. Implicit conversion to integers, lack of ability to define the underlying data type and compatibility issues were some of the problems with plain enum that enum class solved in C++11. Although since then enum has improved and one can specify underlying type in the scoped enumerations.

Plain enums pollute namespace, and you have to pick names that are too long, and have to carry the context inside their names. For example: INETMSG_RFC822_BEGIN inside enum _ImplINetRFC822MessageHeaderState. With an enum class, it is simply written as HeaderState::BEGIN. When placed inside a file/class/namespace that makes it relevant, it is much easier to use: it is more readable, and causes no issues for other identifiers with possible similar names.,

See this change:

You can read more about that in:

Finding Instances

You may find some of the instances with:

$ git grep -w enum *.cxx *.hxx|grep -v "enum class"

When you count it with wc -l, it shows something more than 2k instances.

Examples Commits

You can see some of the previous conversions here, which is around 1k changes:

$ git log --oneline -i -E --grep="convert enum|scoped enum"

This is a good, but lengthy example of such a conversion:

Implementation

First of all, please choose good names for the new enum class and values. For example, you may convert APPLICATION_WINDOW_TITLE into Application::WindowTitle. Therefore, do not use the old names as they were.

Converting enum to enum class is not always straightforward. You should try to understand the code using the enum, and then try to replace it with enum class. You may need to add extra state/values for situations where 0 or -1 or some default value was used. There are cases where a numerical value is used for different conflicting purposes, and then you have to do some sort of conflict resolution to separate those cases.

You may end up modifying more and more files, and a few static_casts where they are absolutely necessary because you are interpreting some integer value read from input. These are the places where you should check the values yourself in the code. You have to make sure that the numerical value is appropriate before casting it to the enum class.

If you want to do bitwise operations, you should use o3tl::typed_flags, for example:

enum class FileViewFlags
{
    None = 0x00,
    MultiSelection = 0x02,
    ShowType = 0x04,
    ShowNone = 0x20,
};

template<> struct o3tl::typed_flags : o3tl::is_typed_flags<FileViewFlags, 0x26> {}

Then, you may use it like this:

    if (nFlags & FileViewFlags::MULTISELECTION)
        mxTreeView->set_selection_mode(SelectionMode::Multiple 

Tuesday
07 October, 2025


face

Writer recently got a Markdown import & export filter and there were a number of improvements to that.

This work is primarily for Collabora Online, but the feature is available in desktop Writer as well.

Motivation

Ujjawal Kumar contributed a markdown import to Writer, as part of Google Summer of Code (GSoC) this summer. Mike Kaganski of Collabora also created a minimal markdown export in Writer. I looked at the feature differences between the two, and filled in various gaps in the markdown export. I also added a few general markdown import/export improvements relevant for normal Writer documents, like embedded image support.

Results so far

Here is a sample case of a document using inline code spans:

Code span: baseline

Exporting this to markdown & loading back to Writer, the code span was lost:

Code span: old result

And now it's preserved:

Code span: new result

This also works with code blocks.

Second, here is a document with lists:

Lists: baseline

Exporting this to markdown & loading back to Writer, the lists were lost:

Lists: old result

And now they are preserved:

Lists: new result

This also works with nested lists.

Third, here is a document with an image:

Image: baseline

Exporting this to markdown & loading back to Writer, the image was lost:

Image: old result

And now it's preserved:

Image: new result

This also works with embedded and anchored images.

Fourth, here is a document with a table:

Table: baseline

Exporting this to markdown & loading back to Writer, the table was lost:

Table: old result

And now it's preserved:

Table: new result

This also works with table alignments and nested tables (to the extent the markdown markup allows that).

Fifth, here is a document with a quote block:

Quote: baseline

Exporting this to markdown & loading back to Writer, the quote's paragraph indentation was lost:

Quote: old result

And now it's preserved:

Quote: new result

How is this implemented?

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. Core side:

Want to start


Thursday
18 September, 2025


face

If you are working with LibreOffice code, trying to understand the code, fix bugs, or implement new features, you will need to debug the code at some point. Here are some general tips for a good debugging experience. Let’s start from the platform

Choose the Right Debug Platform

Choosing a platform to debug usually depends on the nature of the problem. If the problem is Windows-only, you need a Windows environment to build and debug the problem. But, if the problems can be reproduced everywhere, then you can choose the platform of your choice with the debugging tools that you prefer to debug the problem.

On Linux, it matters if you are running X11 or Wayland. Also, as there are multiple graphical back-ends available for LibreOffice, it matters if you are using X11, GTK3/4, or Qt5/6 back-end for your debugging. Some bugs are specific to GTK, then you should use GTK3 UI for testing. In 2025, GTK4 UI of LibreOffice is still experimental, so it is better to work with GTK3. For making the debugging easier, many developers work on X11 (gen) UI for debugging.

Debugging Tools

Various debugging tools can be used to debug the soffice.bin/soffice.exe LibreOffice binary that you have built. For the common debuggers, you can use GDB on Linux, lldb on macOS, and WinDbg or Visual Studio on Windows.

For using the above debuggers, you can use the IDE or front-end that support them. Various IDEs are usable with LibreOffice code. For a detailed explanation, refer to this Wiki article:

Make sure that you can build and debug a simple program before trying to build and debug LibreOffice.

Environment Variables

To have a better debugging experience, or to avoid problems you may have to customize the debugging session with environment variables. A complete article of the TDF Wiki is dedicated to discuss the environment variables that can be used with LibreOffice:

Here is some of the most important ones:

1) Using the X11 user interface:

If you want to use the X11 back-end that is simpler, and usually easier to work with on debug sessions, you have to set SAL_USE_VCLPLUGIN environment variable:

export SAL_USE_VCLPLUGIN=gen
That is specially useful when you are debugging graphical problems. But in some cases, you may need to avoid it or at least customize it. For example, while debugging mouse-related problems you may need to tell LibreOffice to avoid mouse grabbing this way:

export SAL_NO_MOUSEGRABS=1

2) Using GTK user interface

If you are using GTK user interface, then you may use GTK inspector to interactively debug LibreOffice GUI. You can use it this way:

export GTK_DEBUG=interactive

Pretty Printers

In solenv/gdb/ inside LibreOffice source code, you may find pretty printers for GDB. This is helpful when debugging LibreOffice with GDB, to be able to see data in a more readable way.

Dumping Data

Sometimes when you debug a LibreOffice application, it is easier to


Tuesday
16 September, 2025


face

General Activities LibreOffice 25.8.0 and LibreOffice 25.8.1 were announced on August 20 and August 29 respectively Olivier Hallot (TDF) updated help for the option to load printer settings with document, sorting blocks of cells in Calc, hyphenation, statistical functions, number of lines in charts, exponentiation operator in Calc, remote files,


Monday
15 September, 2025


face

Once upon a time, there was a girl, who used WhatsApp in her iPhone. She was rather active there, and collected quite some important data in the app over time. But some things in her iPhone were inconvenient; and the phone was slowly aging. So she wanted to change her phone some day.

For her birthday, a fairy, who learned somehow about the girl’s wish, presented her a new Android phone. That was a nice new phone, and the girl was so happy! She decided to move everything from the old phone to the new one immediately.

She was worrying about how to move the precious data between the devices; but she felt a huge relief, when the phone spoke: “The fairy told me how important your data is to you; and I have magic powers to handle it all. Just connect the old phone to me with a cord”. So she did.

The new phone started its work; and the girl could see how the progress bar was gradually moving to completion; but suddenly it stopped; minutes passed, but the bar was motionless. The girl was impatient to start using her new shiny device, but she knew that she needs to wait. And she waited; and waited; but after an hour passed, she noticed something horrible: the old phone was sucking the life out of the new device through the cable!

The scared girl could only hope that the process would resume, and finish before the new phone is out of power. She searched and learned, that iPhones are known for their insatiable hunger, and whenever they are connected to anything with energy, they start sucking it. She couldn’t even ask the new phone to shine less brightly to save the energy – because it wasn’t ready for such things yet. She used her wireless charger, but its powers were fewer than the hunger of iPhone, combined with the hard work done by Android. The energy level still decreased too fast.

In the end, when the hope almost vanished, the progress resumed moving! But immediately, the new phone said: “When I collected your data from your old phone, something bad happened, and I failed to collect something. I will continue, but please check later, what’s missing!”.

Only a couple of energy drops were remaining in the new phone, when it finished its task, and could be disconnected from the vampire. But the girl was terrified, when she opened WhatsApp, connected to it (using a magic SMS confirmation), only to see that all her data is lost! She tried to open WhatsApp on the old phone to check if something is still there, and saw that the app had disconnected her. So she used the SMS magic again, and – to her great relief – everything was there!

She askes WhatsApp, how to move the data; and it answered, that if she moved from iPhone to iPhone, or from Android to Android, she could use a backup; but from


Thursday
11 September, 2025


face

C++ Standard library, which resides in std:: namespace provides common classes and functions which can be used by developers. Among them, Standard Template Library (STL) provides classes and functions to better manage data through data structures named containers. Here I discuss how to use STL functions for better processing of data, and avoid loops.

Checking Conditions

To iterate over a container to see if some specific condition is valid for all, any, or none of the elements in that container, C/C++ developers traditionally used loops.

On the other hand, since C++11, there are functions that can handle such cases: all_of, any_of and none_of. These functions process STL containers, and can replace loops. If you want to know if a function returns true for all, any, or none of the items of the container, then you can simply use these functions. This is the EasyHack dedicated to such a change:

Here is an example patch which uses any_of instead of a loop:

-    bool bFound = false;
     // convert ASCII apostrophe to the typographic one
     const OUString aText( rOrig.indexOf( '\'' ) > -1 ? rOrig.replace('\'', u'’') : rOrig );
-    size_t nCnt = aVec.size();
-    for (size_t i = 0;  !bFound && i < nCnt;  ++i)
-    {
-        if (aVec[i] == aText)
-            bFound = true;
-    }
+    const bool bFound = std::any_of(aVec.begin(), aVec.end(),
+        [&aText](const OUString& n){ return n == aText; });

As you can see, the new code is more concise, and avoids using loops.

Conditional Copying, Removing and Finding

If you want to copy, remove or simply find a value in a container which conforms to a specific functions, you may use copy_if, remove_if or find_if.

Again, this is an example patch:

-  for ( size_t i = 0; i < SAL_N_ELEMENTS( arrOEMCP ); ++i )
-        if ( arrOEMCP[i] == codepage )
-            return true;
-
-    return false;
+    return std::find(std::begin(arrOEMCP), std::end(arrOEMCP), codepage) != std::end(arrOEMCP);

Final Words

Refactoring code is a good way to improve knowledge on LibreOffice development. The above EasyHacks are among EasyHacks that everyone can try.

More information about EasyHacks, and how to start working on them can be found on TDF Wiki:


Tuesday
09 September, 2025


face

Writer has some support for interdependent (or hierarchical) tracked changes: e.g. the case when you have a delete on top of an insert. See the second post for background.

This work is primarily for Collabora Online, but the feature is available in desktop Writer as well.

Motivation

With the already mentioned improvements in place, the area of format redlines with character style or direct formatting changes were still lacking: Writer's original model here was just marking a text range as "formatted" and then either accept the format redline as-is, or reject reverting back to the paragraph style (default formatting), losing the old character style or old direct formatting.

Results so far

Here is a sample case of a document where the old character style is Strong (~bold) and the font size is 24pt, while the new character style is Quote (~italic) and the font size is 36pt. The rest of the document uses no specific character styles and has the font size of 12pt:

Interdependent tracked change: improved format, after document load

Rejecting that format redline resulted in just the defaults, i.e. no character style and 12pt font size:

Interdependent tracked change: old reject, lost character style / direct format

But now we track the old character style & direct format:

Interdependent tracked change: new reject, handled character style / direct format

This required changes in the DOCX import, ODF import and ODF export, too.

How is this implemented?

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. Core side:

Want to start using this?

You can get a development edition of Collabora Online 25.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 (26.2).


Thursday
28 August, 2025


face

LibreOffice handles different input and output formats, and also displays text and graphics alongside inside the GUI on computer displays. This requires LibreOffice to understand various different measurement units, and convert values from one to another.

Unit selectionUnit selection

The unit conversion can be done by writing extra code, where one should know the units, and calculate factor to convert them to each other.

For example, consider that we want to convert width from points into 1/100 mm, which is used in page setup.

We know that:

1 point = 1/72 inch
1 inch = 25.4 mm = 25400 microns
factor = 25400/(72*10) ≈ 35.27777778

Then, it is possible to write the conversion as:

static int PtTo10Mu( int nPoints )
{
return static_cast<int>((static_cast<double>(nPoints)*35.27777778)+0.5);
}

A separate function that casts integer nPoints to double, then multiplies it by the factor which has 8 decimal points, and then rounds the result by adding 0.5 and then truncates it and stores it in an integer. This approach is not always desirable. It is error-prone, and lacks enough accuracy. For big values, it can calculates values off by one.

Another approach is to use o3tl (OpenOffice.org template library) convert function. It is as simple as writing:

int nResult = o3tl::convert(nPoint, o3tl::Length::pt, o3tl::Length::mm100)

As you can see, it is much cleaner, and gives the output, properly rounded as an integer!

You need a double? No problem! You can use appropriate template to achieve that:

double fResult = o3tl::convert<double>(nPoint, o3tl::Length::pt, o3tl::Length::mm100)

These are the supported units, defined in the header include/o3tl/unit_conversion.hxx:

mm100 – 1/100th mm = 1 micron

mm10 – 1/10 mm

mm – millimeter

cm – centimeter

m – meter

km – kilometer

emu – English Metric Unit (1/360000 cm)

twip – Twentieth of a point (1/20 pt)

pt – Point (1/72 in)

pc – Pica (1/6 in)

in1000 – 1/1000 in

in100 – 1/100 in

in10 – 1/10 in

in – inch

ft – foot

mi – mile

master – PPT Master Unit (1/576 in)

px – Pixel unit (15 twip, 96 ppi)

ch – Char unit (210 twip, 14 px)

line – Line unit (312 twip)

Handling Overflows

If you are doing a conversion, it is possible that the result overflows. With o3tl::convert() you can handle it this way:

sal_Int64 width = o3tl::convert(nPoint, o3tl::Length::pt, o3tl::Length::mm100, overflow, 0);
if (overflow)
{
...
}

Code Pointers

To to find instances to change, one can try finding some magic numbers listed here. For example, consider measuring a line based on twips:

line – Line unit (312 twip)

If you search for 312, you may find some examples:

$ git grep -w 312 *.cxx

Final Words

The task described here is filed as tdf#168226:

EasyHacks are well-defined small tasks that are designed to help newcomers begin LibreOffice programming. If you like it, you can start working on it!

Using o3tl::convert() not only simplifies the


Friday
15 August, 2025


face

This post is part of a series to describe how Writer now gets a feature to handle tables that are both floating and span over multiple pages.

This work is primarily for Collabora Online, but is useful on the desktop as well. See the 11th post for the previous part.

Motivation

Previous posts described the hardest part of multi-page floating tables: making sure that text can wrap around them and they can split across pages. In this part, we'll look at a conflicting requirement. On one hand, headings want their text to not split across pages (and shapes anchored into paragraphs are considered part of the paragraph, too). On the other hand, it should be OK to have a floating table at the bottom of a page and the following heading to go to the next page.

It turns out, Writer gave "keep together" a priority, while Word gave "floating tables are OK to split to a previous page" a priority.

Note that if you have a shape (e.g. a triangle) and not a floating table, then both Word and Writer prevents the move of that shape to a previous page (if the shape is anchored in a heading); this difference was there just for floating tables.

Results so far

Here is how the tdf#167222 bugdoc looks like now in Writer:

Floating table, followed by heading: new Writer render

And here is how it used to look like:

Floating table, followed by heading: old Writer render

And here is the reference rendering:

Floating table, followed by heading: reference render

This means that we leave layout for shapes unchanged in general: shapes anchored in headings are still considered to be part of headings and don't split. But for floating tables, we now allow them to split and use space at a previous page if they fit there.

How is this implemented?

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:

Want to start using this?

You can get a development edition of Collabora Online 25.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 (26.2).


Monday
11 August, 2025


face

General Activities LibreOffice 25.2.5 was announced on July 17 Olivier Hallot (TDF) updated help for CSV import, explained Property Mapping in help for Charts and improved help for Calc’s FILTERXML function and AutoFilter Gábor Kelemen (Collabora) did many code cleanups Tomaž Vajngerl (Collabora) made internal hyperlinks in a table of


Friday
25 July, 2025


face

You know what: Microsoft became miserably incompetent in IT.

I develop open-source code. But that never made me one of the “I hate proprietary software or IT giant corporations” types. I always saw the nice things that Microsoft offered to its users; I saw not only downsides in its products. And I also used (and continue to use) things created by it: Windows to start with (and I develop there, being able to debug and address issues specific to the platform that most of our users use); but also its email service for personal mail.

This Monday, I decided to send something to LibreOffice dev mailing list. Something I do from time to time, you know. Not too fascinating, right?

Well, this time, it turned out, Microsoft decided to teach me to fear them. Thunderbird shown me a message, that the mail couldn’t be sent (well, not a problem: will re-try again…), but then I found myself logged off, with “Your account has been blocked” message. They decided, that I violated their service agreement!

FTR: here is the mail. I was able to send it using another tech giant’s mail service. You may see that it’s full of links. Yes, that’s true; I prefer to provide references to my words. But tell me where was it violating anything in MS agreement?

OK, they have a stupid AI that is worse than good old filters. OK, they made it react immediately, as an undoubted authority. But that’s not a big problem, right? They provide a way to appeal! Let me do that.

And of course, they ask for the phone, and I provide it, just to get a nice reply:

And guess what: there is no other method!

OK! Let’s ask their support. (I am approaching to the point that fascinated me most.) I found a link to “Contact Microsoft Support” on the “Troubleshooting verification code issues” page; and after some automatic answers there, which didn’t answer my problem, I finally got a button telling me … tada …

Yes, you got it right. “Here is a page where we discuss problems signing in. You attempted our FAQ suggestions? You still can’t sign in? No problem! Contact our Support team, and we will solve your problem is a minute! But first, please sign in to continue.”

Heh. I used my wife’s account to contact support. And then I was given a very secret link to an appeal form, where I could file a support ticket. And the next morning, I got a message! Yay! It told me to do something! Let me try! What is that they tell me to do? Reading… hmm… go to sign-in page, and when they tell me that my account is blocked, provide a phone number? Wasn’t it exactly the thing I attempted and failed, and told them about that? But hey, they obviously fixed that problem overnight, they couldn’t just send me the useless instructions, right


Tuesday
08 July, 2025


face

Writer has some support for interdependent (or hierarchical) tracked changes: e.g. the case when you have a delete on top of an insert. See the first post for background.

This work is primarily for Collabora Online, but the feature is available in desktop Writer as well.

Motivation

With the already mentioned improvements in place, a few areas were still lacking: we didn't have UI for all cases where the DOCX import was possible already; combining tracked changes (redlines) were not complete (so you don't have to reject all parts of a logical redline one by one) and some of the undo/redo code didn't work as expected.

Results so far

Here is a sample case where the UI was missing to create something that was possible to import from DOCX: a format redline on top of an insert redline.

If you had a document with an insert:

Interdependent tracked change: just insert

And you selected BBB to mark those characters as bold, we just updated the existing insert redline to be bold:

Interdependent tracked change: old, format is not tracked separately

But now we track a format change on top of the insert separately:

Interdependent tracked change: new, format is tracked separately

This is also visible if you open the track changes dialog, which explains that now you have part of the insert redline covered by a format redline:

Interdependent tracked change: UI dialog now showing multiple redlines

How is this implemented?

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. Core side:

Want to start using this?

You can get a development edition of Collabora Online 25.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.8).


Monday
02 June, 2025


face

Writer has some support for interdependent (or hierarchical) tracked changes: e.g. the case when you have a delete on top of an insert. While there were some working cases, handling of many combinations were missing. I started to make systematic improvements in this area in the recent past, this post gives you an overview what's done so far.

This work is primarily for Collabora Online, but the feature is available in desktop Writer as well.

Motivation

DOCX files in Word can often have overlapping tracked changes: Writer tries to split these up to make sure there is only one tracked change under the cursor at the same time. Still, it's possible that you have a tracked change with multiple types: e.g. a delete on top of an insert.

The focus in on 3 combinations which appear in DOCX files a lot: "insert, then delete", "insert, then format" and "delete, then format".

This mostly affects the UI and import/export filters of ODT and DOCX.

Results so far

Given an insert, then delete:

Interdependent tracked change: insert, then delete

Most operations worked nicely here, but in case your cursor was in the middle of AAA and you did a reject, followed by an undo, proper handling of that was missing, now implemented.

But then given an insert, then a format:

Interdependent tracked change: insert, then format

Then a handling of more actions were missing:

  1. DOCX import is now implemented.
  2. ODT import is now implemented.
  3. Accepting when you're inside AAA is now implemented: the insert is accepted for BBB but the format stays unchanged.
  4. Rejecting when you're inside AAA is now implemented: the insert is rejected and BBB is also removed, together with the format on top of it.
  5. Accepting the BBB now correctly operates on the insert type, so the format type remains after accept.
  6. If you accept BBB, now the surrounding AAA and CCC also get accepted as well, as expected.
  7. Now if you reject BBB, then it gets removed from the document, since you rejected an insert.
  8. When you reject BBB, the surrounding AAA and CCC also get rejected.

The combined implementation of these should give you a smooth feeling in case you're used to how Word works: if there is a format redline combined with an insert, then the operations act on the insert type, and format is only accepted/rejected when there is no insert "under" the format.

Similarly: it's a bit of an implementation detail that Writer splits redlines on DOCX import: so if you e.g. accept AAA then we combine that with BBB and CCC when it makes sense, so you need to click a lot less.

Finally, given a delete, then a format:

Interdependent tracked change: delete, then format

Then again handling of some actions were missing:

  1. DOCX import is now implemented.
  2. ODT import is now implemented.
  3. ODT export is now implemented.
  4. Accepting AAA now correctly operates on the delete type of BBB.
  5. Rejecting AAA

Wednesday
28 May, 2025


face

This deal unites the largest team of corporate Office engineers to deliver on Collabora Productivity’s mission to restore Digital Sovereignty to its users, while making Open Source Office Rock. It supercharges Collabora’s Online Office products and services portfolio with rich German language capability, deeper experience of vertical applications, new Web Assembly skills, and a wider unified partner ecosystem. Through improved product richness this sharpens the competitive edge of FLOSS Office productivity against mass-market proprietary alternatives.

CAMBRIDGE, UK – May 28th 12:00 CEST – 2025

Collabora Productivity, the world’s leading provider of collaborative Open Source Office editors have completed a merger with allotropia. Collabora has invested heavily in building Collabora Online (COOL) – a market leading, on-premise, secure, interoperable, open-source solution for document editing and collaboration deployed to any modern browser. This is complemented by desktop and mobile apps across Linux, Windows, Mac, Android, iOS and Chrome-OS. Collabora provides support subscriptions to enterprise customers worldwide via a network of hundreds of trusted partners. This is now augmented by allotropia’s partner and customer base. Together with our partners we deliver document and productivity excellence integrated with our partners product and service offerings.

allotropia’s expertise around Web Assembly combined with Collabora Online will we expect, in time, enable customer use-cases such as well as office-as-component embedding scenarios in vertical applications as well as off-line and end-to-end encrypted editing, and. This work builds on some visionary prototype funding from the Bundesministerium des Inneren (BMI) for a collaboration between the companies to enable the use of Collabora Online off-line in the browser.

Further details of product investment, and direction will be announced and decided in workshops with our key customers and partners at our annual COOL Days conference in Budapest next week where staff, community and our customer and partner-ecosystem meet, swap ideas, and hear about the latest work in our upcoming major release featuring improved performance, usability, interoperability and much more.

“Collabora is excited to welcome each member of the allotropia team today!” said Michael Meeks, CEO, Collabora Productivity, “We are excited to work together to accelerate our product development, enjoy our first COOL Days together, and plan the next features and possibilities to delight our customers.”

Collabora has invested in building a network of hundreds of partners and is approaching one hundred million docker image downloads of its document editing server software, with millions of paying users of its products, all of whom will start to benefit from this merger from today.We expect to bring the experience that allotropia has from it’s relationship with CIB around vertical desktop applications (Fachverfahren) to help partners and customers migrate their Windows & Microsoft Office based business process to easy to deploy multi-platform web applications.

“With our awesome team of engineers, and our WebAssembly know how, we can add significantly to Collabora’s powerhouse of Office engineering prowess & their product offerings”, says Thorsten Behrens, CEO of allotropia, “we’ve worked with them as partners for many years, and align perfectly in our goals


Monday
26 May, 2025


face

Yesterday I merged a fix for Writer’s tdf#165094. Not that it was something exceptional; something that often happens when we change the huge code: a regression. Something that we try to do for them: a fix. Why mention it here?

It happens to show something, that people underestimate. The complexity of what they call “proper testing” – you know, that “I found a bug! Do you even try to test your software???” rant you often see in discussions. Let’s look at this case.

The problem was, that in some specific document, where there was a manually inserted page break, that page break, defined in a hidden paragraph, disappeared after an upgrade. Sounds easy? Should be caught immediately in the release testing? But other page breaks weren’t lost.

Debugging showed, that the bug would only occur when all of the following happened:

  • The page break was defined in a hidden paragraph (something already known from the reporter – thank you Gabor!), and
  • There were at least 26 paragraphs before that hidden paragraph, all on the same page, and
  • The page break defined a paragraph style, and
  • That page break defined a page number, and
  • That assigned new page number happened to be the same “oddity” as the current one (i.e., either the number of that page with 26+ paragraphs was odd, and the new page number was odd; or the number of that page with 26+ paragraphs was even, and the new page number was even), and
  • After the hidden paragraph (which defined the page break), a table immediately followed.

I suppose, that’s a combination of factors, that any QA engineer would naturally test first, don’t you agree? (Disclaimer: no I don’t think so.)

Note that the complexity of this constellation of causing factors is, again, not uncommon in our codebase. In fact, it only needed less than ten features to take their specific forms, from thousands of features and options that the suite offers.

But it is completely unsurprising, that the bug, that requires such a constellation of factors, actually appeared in our bug tracker. Given the tens of millions of users, who work with who knows how many documents, every low-probability event will happen, sooner or later. This is good; and we are thankful to everyone who files bugs.

And let me say, that we at Collabora Productivity are glad to do many good things to make the office suite better for everyone.


Thursday
08 May, 2025


face

Writer has the concept of rejecting tracked changes: if a proposed insertion or deletion is not wanted, then one can reject it to push back on the proposal. So far such an action left no trace in the document, which is sometimes not wanted. Calling reinstate on a change behaves like reject, but with history: it reinstates the original state, with the rejected change preserved in the document.

This work is primarily for Collabora Online, but the feature is available in desktop Writer as well.

Motivation

When Alice works on a document to insert e.g. new conditions for a contract, then perhaps Bob is not happy with the proposal. But just rejecting the change "silently" would not be polite: the tracked change then disappears, so possibly Alice thinks it was accepted and Bob didn't communicate the pushback explicitly in the resulting document, either.

Reinstate is meant to improve this interaction: if an insert is reinstated, then an explicit delete is created on top of the insert, so Alice can see that Bob was not happy with the proposal. Or in case Alice proposed a delete, Bob can reinstate that by adding the same content again to the document, without typing the text manually after the delete.

This is a UI feature: the resulting model still only contains inserts and deletes, so it works even with DOCX files.

Results so far

Given an insert:

Reinstate: an insert

Now you can easily create a delete on top of the insert:

Reinstate: a reinstated insert

And given a delete:

Reinstate: a delete

Now you can easily create an insert right after the delete, preserving complex content:

Reinstate: a reinstated delete

As you can see, this creates the opposite of the original change as a new tracked change, so it will in the end still reject the change, but without deleting the original change.

How is this implemented?

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. Core side:

Online side:

Want to start using this?

You can get a development edition of Collabora Online 25


Wednesday
30 April, 2025


face

This FirebirdSQL pull request introduces support for Windows ARM64 builds to the Firebird project. The changes cover updates to build scripts, configuration files, and Visual Studio solution/project files to accommodate ARM64 architecture, ensuring compatibility and enabling compilation and functionality on Windows ARM64 platforms.


face

This&nbsp;FirebirdSQL pull request introduces SQL-compliant aliases GREATEST and LEAST for the existing MAXVALUE and MINVALUE functions. These aliases align with the SQL:2023 standard and provide a more intuitive and widely recognized syntax. The changes include updates to documentation, keywords, parser tokens, and system function definitions to support these new aliases.


Friday
18 April, 2025


face

As a LibreOffice user, you have certainly seen the LibreOffice splash screen. It is displayed when you open LibreOffice, it has a progress bar, and when loading the application is finished it goes away. Here we discuss a suggested improvement for this splash screen.

Current Implementation Approach

Currently, the splash screen is implemented by creating a custom widget with a custom painting mechanism that draws the splash image and also the progress bar and moves the progress indicator.

This has some drawbacks:

1. The splash screen does not always scale to the same size as the main LibreOffice Window.

2. The style of the progress bar is somehow different from other UI elements, looks mostly like gen interface.

3. It needs and uses a custom paint code.

4. It does not conform to the dark/light theme.

5. It is not easily localize-able. In fact, the only text is from the displayed image, in English. When you build from sources, the image file is instdir/program/intro.png.

LibreOffice splash screen bitmapLibreOffice splash screen bitmap

6. It is a separate binary (oosplash). You may run it with:

$ ./instdir/program/oosplash
LibreOffice dev splash screenLibreOffice dev splash screen

VCL Weld Mechanism

I have previously written about VCL weld mechanism, which is based on creating user interface files (.ui) and loading them inside the application.

The weld mechanism greatly reduces the complexity of creating user interfaces, and also improves other aspects of the user interface, including the consistency.

Code Pointers

Most of the code for the current implementation resides in:
desktop/source/splash/splash.cxx.

The SplashScreenWindow class has an custom paint method, SplashScreenWindow::Paint(), which draws the bitmap, and also the progress. A new UI file is needed for this purpose, which should use GtkProgressBar, which will be considered a weld::ProgressBar. VCL then uses appropriate progress bar widget in different graphical plugins of VCL.

You may look into some dialogs like tip of the day to get some insight:

It would be interesting to avoid a separate binary, but it is fine to keep things as is, and just change to use .ui file.

Final Words

The above issue is tdf#166128. If you would like to work on fixing it, you can just follow the Bugzilla link to see more information.

You may also use ideas from a minimal weld application here:

VCL weld: create LibreOffice GUI from design files


Tuesday
08 April, 2025


face

Here is the description : "The range-based FOR statement is used to iterate over a range of numeric values. The iteration is performed in increasing order when used with TO clause and in decreasing order when used with DOWNTO clause"Syntax[&lt;label&gt; :]&nbsp;&nbsp;FOR &lt;variable&gt; = &lt;initial value&gt; {TO | DOWNTO} &lt;final value&gt; [BY &lt;by value&gt;] DO&nbsp;&nbsp; &nbsp;&nbsp; &


Monday
07 April, 2025


face

We are happy to announce&nbsp;the release of Jaybird 6.0.1 and Jaybird 5.0.7. Both releases provide a number of performance improvements to blob handling, and some bug fixes.We plan to offer more blob performance improvements in upcoming releases of Jaybird 5 and 6, for Firebird 5.0.3 and higher (see also New Article: Data access methods used in Firebird).


Friday
14 March, 2025


face

Last year, I attended the annual LibreOffice Conference in Luxembourg with the help of a generous travel grant by The Document Foundation (TDF). It was a three-day event from the 10th to the 12th of October 2024, with an additional day for community meetup on the 9th.

Luxembourg is a small country in Western Europe. It is insanely wealthy with high living standards. After going through an arduous visa process, I got to the country on the 8th of October. Upon arriving in Luxembourg, I took a bus to the city center, where my hotel — Park Inn — was located. I deboarded the bus at the Luxembourg Central station. Before walking towards my hotel, I stopped to click a few pictures of the beautiful station.

All the public transport in Luxembourg was free of cost. The experience of being in Luxembourg was as if I had stepped in another world. The roads had separate tracks for cycling and separate lanes for buses, along with wide footpaths. In addition, the streets were pretty neat and clean.

Luxembourg's Findel Airport. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

Separate cycling tracks in Luxembourg. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

A random road in Luxembourg with separate lane for buses. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

The conference venue was in Belval, while I stayed in the city center. Even though my stay was 20 km from the conference venue, the commute was convenient thanks to free of cost train connections. The train rides were comfortable, smooth, and scenic, covering the distance in half an hour. Moreover, I never found the trains to be very crowded, which enabled me to always get a seat.

This is what trains look like in Luxembourg. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

The train ride from my hotel to the conference venue had some scenic views like this one on the way. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

A tram in Luxembourg with Luxembourg Central station in the background. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

My breakfast was included in the hotel booking. The breakfast had many options. It had coffee and fruit juices, along with diverse food options. Some of the items I remember were croissant, pain au chocolat, brie (a type of cheese), scrambled eggs, boiled eggs, and various types of meat dishes. Other than this, there were fruits such as pears.

That circular pie in the center of the image is brie - a type of cheese - which I found delicious. Photo by Ravi Dwivedi. Released under the CC-BY-SA 4.0.

Pre-conference, a day was reserved for the community meetup on the 9th of October. On that day, the community members introduced themselves and their contributions to the LibreOffice project. It acted as a brainstorming session. All the attendees got a lovely conference bag, which contained a T-Shirt, a pen and a few


Thursday
06 March, 2025


face

We’ve added a great new Vue.js-3 ZetaJS demo (source)! It showcases word processing and spreadsheets inside a single web app. Calc is being used as a data source for an HTML app, filling letter templates in Writer. You can even upload custom data spreadsheets or document templates! And have you seen the nice Writer toolbar, all done with Vue.js?

We’ve also updated the existing demos, showcasing Chrome PWA support with the Ping Monitor demo – just click the little install button at the top-right of the address bar, to get the Ping Monitor “installed” on your desktop!

new demo combining Writer, Calc and the complete toolbar

Talks

Meanwhile, our team was giving some great talks about our work for ZetaOffice and LibreOffice. Why not check out the recordings during your lunch break?

ZetaJS & ZetaOffice

FOSDEM LibreOffice DevRoom talks

News clippings

Look, we made some headlines! TheRegister was following up some earlier coverage about the WebAssembly port, after Thorsten gave Liam a demo during FOSDEM. Read up the full article here.

Next up

In case you’re around, meet us in two weeks at the FOSSAsia Summit in Bangkok, where Sarper Akdemir will give an update over our work. Dates are March 13-15.

If you’re based in Europe, you might instead enjoy Thorsten’s talk at the Chemnitz Linux Days (Germany) from March 22-23.

Looking forward to meet you there!

Feedback appreciated!

Please subscribe to our Newsletter or on Mastodon and let us know how you liked ZetaJS and the demos! If you’re playing with the code leave a star at the ZetaJS repo or if you hit any issues please file a report on GitHub.

Or just leave a comment and let us know directly – thanks for reading! 🙂


Monday
03 March, 2025


face

By Ndidi Folasade Ogboi

For the past two months, I’ve been working on adding more templates to LibreOffice Writer as part of my Outreachy project. My goal has been to create functional templates that users need the most.

I created these templates based on what you told us in our survey and your response was incredible!…


Wednesday
26 February, 2025


face

Firebird Project is happy to announce general availability of Firebird 5.0.2 — the latest minor release in the Firebird 5.0 series.This minor release offers bug fixes as well as a few improvements, please refer to the Release Notes for the full list of changes.Binary kits for Windows, Linux, MacOS and Android platforms are immediately available for download.


Monday
17 February, 2025


face

LibreOffice inherits a gigantic code base from its ancestors, StarOffice and OpenOffice. Here I discuss some notes for the newcomers on how to better understand the existing LibreOffice code, and improve the patches.

Studying the Existing Code

As said, LibreOffice is a huge code base, containing ~10 million lines of mostly C++ code. There are different assumptions, conventions and coding styles across ~200 modules that LibreOffice has.

Therefore, it is important to first, study the existing code, through reading and debugging LibreOffice source code, to understand the things that it does, and the way you can implement your ideas, including bug fixes and adding new features.

And although implementing some ideas seem to be straightforward at first sight, it is meaningful to study the details.

Quality Assurance Point of View

First of all, you should understand the thing that you want to implement. No matter if it is a bug, a new feature, or just an EasyHack, you should understand what is requested, what works and what does not work. This requires careful reading of the Bugzilla pages.

User Point of View

Then, you should try to run LibreOffice to understand the exact place in the application where you want to change. LibreOffice user interface has thousands of dialog boxes, so you need to make sure that you understand the thing that you want to do.

Developer Point of View

And at last, you get into implementing something in the code. Here are some questions that you can ask yourself about the details, when reading the existing code:

  • Why this statement is here, in the first place? (detail-oriented view)
    • You can use git blame to see the last author of a specific line
    • You can use git log to study the details by knowing the commit hash
    • What can this part of code actually does?
    • Can I see its effect?
git loggit log

Or, you may be interested in the code behavior in the big picture:

  • What does the code do as a whole? (holistic view)
  • There are many other statements, functions and other constructs in the code. What do they do?
  • What is the overall goal of the code?
  • Can I test that in action?

You can do some small changes, before even getting into implementing your idea:

  • What happens if I remove it? (small changes)
  • Does the removal prevent the code from working?
  • Is it incomplete, or does it actually do something useful, which
  • will be absent if I remove it?

Then, you can work on the actual implementation. Ask yourself:

  • How can I implement the idea in its simplest form? (straightforward change)
  • Does it have side effects?
  • How can I make sure every thing else works as before?
  • How can I write a test for it?

After understanding some of the basic details about the way things work, you may go into improving your implementation.

  • How can I make it better? (sophisticated change)
  • Can I make the code more robust where it is brittle?
  • Can I complete the

Wednesday
29 January, 2025


face

We are pleased to announce the successful migration of Firebird Docker images to their new home:https://github.com/FirebirdSQL/firebird-dockerThe images are now published on Docker Hub athttps://hub.docker.com/r/firebirdsql/firebirdThanks to Adriano dos Santos Fernandes for his invaluable contributions and improvements throughout this process.


Monday
13 January, 2025


face

Helen Borrie, a key figure in the Firebird relational database project and a longtime contributor at IBPhoenix, passed away on January 2, 2025. Her contributions were essential to Firebird’s creation and its development over the past 25 years.Read the rest of the official announcement

<- Current blog entries