17 Nov 2022

Formatting the code in your patch for LibreOffice

Do you want to submit a patch to LibreOffice Gerrit, and you’re wondering if your code will be accepted or not? Other than providing a good solution to resolve a problem (fix a bug, implement a feature or enhancement), you should care about the code conventions, and in particular, code formatting. Suitable code formatting for LibreOffice is what we discuss here.

Code Formatting Conventions

LibreOffice is based on the old code base of OpenOffice. Because of that, LibreOffice uses the coding conventions of OpenOffice in most places. There is a list of rules that you can refer to when needed, in the OpenOffice Wiki:

Here is a comprehensive list of C++ coding rules:

You can see many rules and standards there – beyond code formatting – from design to the way you should declare and implement classes, and their functions and variables. You will also find topics about how to create header files, how to document code and how to achieve type safety.

But, focusing only on C++ code formatting, you should make sure that:

  1. Each source file should have a suitable header. For the existing files, please keep the header as is.
  2. The source file should have a newline character in the end.
  3. It is suggested that you limit the code to only ASCII characters, and avoid using utf-8 string literals directly.
  4. Indent your code with 4 spaces
  5. You should avoid tabs.

These rules have their own name in the OpenOffice code formatting conventions.

Let’s look at this code snippet from a C++ file: chart2/qa/extras/charttest.hxx.

uno::Sequence < OUString > ChartTest::getImpressChartColumnDescriptions( std::u16string_view pDir, const char* pName )
    uno::Reference< chart::XChartDocument > xChartDoc = getChartDocFromImpress( pDir, pName );
    uno::Reference< chart::XChartDataArray > xChartData ( xChartDoc->getData(), uno::UNO_QUERY_THROW);
    uno::Sequence < OUString > seriesList = xChartData->getColumnDescriptions();
    return seriesList;

As you can see, in the old OpenOffice style, there is a space after the starting parenthesis (, and also before the ending parenthesis ). Also,  before and after <>. This might look old fashioned, but this is the way old code is written, and you are supposed to avoid changing the old code only for making it look better! I am going to describe why.

Why Keep Old Style Formatting?

There is a very good reason to keep the old style code formatting. To understand the code, and possible problems, developers usually refer to the code, and its history. Thus, it is important to keep the history of the code clean.

To achieve this, experienced developers suggest that you should avoid changing the formatting of the code, even if you are tempted to do so! Changing spacing, curly braces and other things in order to look nicer will pollute the git history.

The nice tool git blame can show who to blame for each line of code! If you change the code format randomly, you may prevent other developers to understand the root cause of the change, and thus fixing the problems more difficult.

What to Do With the New Files?

For the newer files, the story is different! You are not only encouraged to use the nice looking new coding style, but  git hooks also prevents you from submitting a code that does not conform with the new code formatting conventions! If your code does not conform to the new code conventions, you will see this error message:

ERROR: The above differences were found between the code to commit 
and the clang-format rules. Tips:

- You may run '/opt/lo/bin/clang-format -i <problematic file>' to fix up style automatically.
- See solenv/clang-format/README on where to get the required version of clang-format binaries.
- If you renamed an excluded file, update solenv/clang-format/excludelist accordingly to keep it excluded.
solenv/clang-format/check-last-commit: KO
Makefile:485: recipe for target 'clang-format-check' failed
make: *** [clang-format-check] Error 1

As the tip suggests, there is a tool named clang-format that does the formatting automatically for you. You only need to invoke:

/opt/lo/bin/clang-format -i <your-new-source-file.cxx>

You can read more about how to set up and use clang-format here:

One nice thing about thing about clang-format binary is that you can instruct your IDE to use this binary to do the formatting for you with a simple shortcut.

Visual Studio Code

LibreOffice source code in Visual Studio Code

Each new file should have appropriate header and footer. You can look at TEMPLATE.SOURCECODE.HEADER and find an appropriate header and footer for you source code.

Final Notes

If you had problems with code formatting, try to read the above instructions and clean up your code. If you have tried but you couldn’t, that’s not a problem! Just ask me for help. I will try to help you format your code according to the LibreOffice coding conventions. Be bold and try to have your code merged in the LibreOffice code! You can learn more about how to get started with LibreOffice development here:

3 Nov 2022

Crash fixes, part 2: abort

One category of the bugs that we see in computer programs including LibreOffice is the unexpected crashes. You’re working with the application, and it is suddenly closed! In the previous part, I have discussed crashes that are caused by segmentation fault. In this article, I discuss the crashes from invoking abort() function. Please note that not an abort is not always a bad thing, or a bug.


In C/C++ sometimes we have to check the validity of certain conditions, and avoid certain bad situations. We do that to avoid data corruption or other problems that leads us to terminate the application instead of trying to continue.

This is different from normal error handling routines, because in error handling, we usually try to find errors, find ways out of the problem automatically, or ask user to fix the problem by changing the input and things like that. But, this is not always the case.

Better to Be Safe Than Sorry!

If you search for abort in the .cxx files, you will find a lot abort() usages:

git grep "abort(" *.cxx

Let’s look at some example. When writing tests, one of the problems that may occur while testing font rendering is the font fallback because of using fonts that are not available via LibreOffice. In this situation, it is better to stop the test application, so that the developers understand that they should use fonts that are available via LibreOffice. You can look vcl/unx/generic/fontmanager/fontconfig.cxx for more information.

Another example is the problem with the GPU. If you look at vcl/skia/gdiimpl.cxx, you can see this code. The comments explain the situation. When we run out of memory, the error is “unrecoverable without possible data loss”. So, the best thing that we can do, is to abort the program.

// If there's a problem with the GPU context, abort.
if (GrDirectContext* context = GrAsDirectContext(mSurface->getCanvas()->recordingContext()))
    // Running out of memory on the GPU technically could be possibly recoverable,
    // but we don't know the exact status of the surface (and what has or has not been drawn to it),
    // so in practice this is unrecoverable without possible data loss.
    if (context->oomed())
        SAL_WARN("vcl.skia", "GPU context has run out of memory, aborting.");
    // Unrecoverable problem.
    if (context->abandoned())
        SAL_WARN("vcl.skia", "GPU context has been abandoned, aborting.");

Generally speaking, the developer should try to recover from the errors, but in the end, some unrecoverable errors may remain, and the developer might decide to do an abort().

Fixing Bugs Related to abort

On the other hand, sometimes it is possible to avoid doing an abort(), and recover from an error. In such a case, fixing the crash would involve finding a way out of the problem, and continuing the normal execution of the program.

For example, take a look at tdf#138022 – LibreOffice exits/crashes when minimizing start center after closing a document (SKIA).  This issue is fixed in this patch:

commit 42e30c24615402c49351f80cc8a47d61d47267c6

Author: Jan-Marek Glogowski
Date: Mon Nov 16 22:43:51 2020 +0100
tdf#138022 Skia don't recreate empty surfaces
Skia can't create empty surfaces, so the recreation will hit the
std::abort() in SkiaSalGraphicsImpl::createWindowSurface. Origin
of the backtrace is some queued Resize event, which will hit
this a few times via SkiaSalGraphicsImpl::checkSurface.
This feels a bit like tdf#130831, where VCL tried to track damange
for an empty Qt image...

The idea of the fix is that Skia can not re-create empty surfaces, so when resizing events are queued, recreation of the surface will lead to an std::abort. To avoid this, Jan-Marek changed the code. Now LibreOffice does not re-create the surface in the case that both width and height of the surface are zero. Other than that, instead of using recreateSurface(), it uses two commands destroySurface() and then createSurface().

Final Words on Fixing a Crash

We have a meta tag dedicated to the crash bugs in Bugzilla. All the bugs related to the crashes are listed there:

tdf#133092 (Crash) – [META] Crash bugs

Other than that, we have EasyHacks related to the crashes. Some of them are discussed in our previous post, “Crashes that you can fix! – EasyHack“. You can try fixing them!

24 Oct 2022

Crashes that you can fix! – EasyHack

As discussed in the “crash fixes for LibreOffice, part 1: segfaults“, bugs that lead to crash in LibreOffice or other native applications can have different causes, and therefore need completely different fixes. Some of these bugs are hard to fix: you have to change the behavior of the application in order to avoid crashes.

On the other hand, there are some cases that a fix can be simple checking for a pointer to make sure that it is not NULL/nullptr before actually using it. Or, making sure that a variable has properly initialization before its use. In these cases, fixing the bug is not too hard, and a fix can be provided very quickly.

Example EasyHacks Related to Crashes

Thanks to Mike, this week we have 2 new EasyHacks of this kind:

When accessing uninitialized a TextInputStream, an exception should be thrown, in which leads to Basic run-time error. Instead, because of lack of appropriate checks, it leads to a crash.

You can debug and see the crash point by executing this LibreOffice BASIC code snippet:

Sub crash
  stream = CreateUnoService("com.sun.star.io.TextInputStream")
End Sub

To fix this bug, one should take a look at OTextInputStream code inside core/io/source/TextInputStream/TextInputStream.cxx:53 and make sure to check and throw runtime exception where appropriate.

This second issue is a crash that happens when parsing an XML with undeclared namespace. To catch and debug such a crash, you should create a tiny xml file as described in the bug report:

<?xml version="1.0" encoding="UTF-8"?>

Then, try to parse it with this LibreOffice BASIC script:

Sub Parse
  source = CreateUnoStruct("com.sun.star.xml.sax.InputSource")
  source.aInputStream = CreateUnoService("com.sun.star.ucb.SimpleFileAccess").openFileRead(ConvertToURL("path/to/file.xml"))
  parser = CreateUnoService("com.sun.star.xml.sax.FastParser")
End Sub

Then, you will see that the crash happens in this part of the code in core/sax/source/fastparser/fastparser.cxx:1281:

if ( !m_bIgnoreMissingNSDecl || URI != nullptr   )
    sNamespace = OUString( XML_CAST( URI ), strlen( XML_CAST( URI )), RTL_TEXTENCODING_UTF8 );

If you look closely, you will understand that if m_bIgnoreMissingNSDecl is false, then the URI check to make sure that it not nullptr is not happening. And dereferencing nullptr leads to crash.

The above two EasyHacks are good for the newcomers. So, if you have some knowledge on C++ and you are able to build LibreOffice, you can work on these issues. If so, first try to gain a basic understanding of the problem. Then assign the bug to yourself and try to fix it.

Writing Tests for Crash Fixes

Writing tests for the crash is also not a very hard task. Either a CppUnitTest, or a UITest can do the exact things that leads to crash without a fix. Then, the test will fail if a crash occurs preventing the same problem in the future.

If you search for “crash” in the LibreOffice commit titles, you can find many good examples of fixing the crashes. As an example that uses CppUnitTest, you can look at this patch:

And for a crash fix with a UITest, you can look at this patch:

Final Notes

EasyHacks are good starting points for the newcomers. If you want to contribute to LibreOffice code by working on this improvement, but you need to know how to get started with LibreOffice development, you can see our video tutorial:

Getting Started (Video Tutorial)

6 Sep 2022

Crash fixes, part 1: segfaults

One of the bugs that we see in computer programs including LibreOffice is the crash. You’re working with the application, and the program is suddenly closed! Here we discuss the usual causes for these crashes, and how to fix some of them.

Crash Report

Crash Report

There can be many reasons for a crash in C/C++, including segmentation faults (segfaults), assertion failures, aborts, and exceptions that lead to abort. Here we discuss the segmentation faults.

Usual Causes for the Segmentation Fault Crashes

There can be many reasons for a segfault in C/C++, and most of them are are related to the incorrect use of memory. For example:

1. Accessing memory that the program does now own
2. Using memory outside the allocated parts
3. Using uninitialized variables

If you use a variable without properly initializing it, that may lead to a segfault. For example:

int main() {
    int *p;
    *p = 1;
    return 0;

Trying to de-reference a null pointer also leads to crash.

int main() {
    int *p = nullptr;
    *p = 1;'
    return 0;

$ g++ main.cpp -o main; ./main
Segmentation fault (core dumped)

In C/C++, most compilers do not check the array bounds automatically. When working with arrays, you have to take care that you are accessing inside the array bounds, and not beyond them. Look at this example:

int main() {
    int a[10];
    a[1000000] = 1;
    return 0;

When working with pointers, it is important to make sure that the pointer is not null before working with it. For example, look at this file:


Inside it, look at this method:

bool lcl_HasRowOutline( const ScViewData& rViewData )
    const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.GetTabNo());
    if (pTable)
    const ScOutlineArray& rArray = pTable->GetRowArray();
    if ( rArray.GetDepth() > 0 )
        return true;
    return false;

Here, before working with pTable, with the condition “if (pTable)“, we make sure that the pTable pointer is not null.

That is also the case when working with arguments. Look at this function inside the same file:

IMPL_LINK_NOARG(ScTabView, TimerHdl, Timer *, void)
    if (pTimerWindow)
        pTimerWindow->MouseMove( aTimerMEvt );

The condition makes sure that pTimerWindow is not null.

How to Fix the segfaults?

To describe how to fix some of the segfaults, I discuss to fixes that are merged recently by Caolan and me.

First, please take a look at this recently fixed crash by Caolan:

The crash was happening when you created a page break, and then tried to edit it. The backtrace showed that the problem is happening in this file:


inside this method:

IMPL_LINK_NOARG(SwPageBreakWin, FadeHandler, Timer *, void)

just in this line, when invoking IsVisible().

if (IsVisible( ) && m_nFadeRate > 0 && m_nFadeRate < 100)

In a debug session for LibreOffice, one can see that the window object is actually destroyed, but the code wants to check if the window is visible or not. In this way, it leads to a segfault. Looking carefully, it becomes visible that the problem comes from some lines below that. We see that m_pLine->DestroyWin() is invoked, but the control sequence is working in a way that IsVisible() is called right after that. To fix this, Caolan simply added a return after destroying the window, and it fixed the problem.

    if ( m_bIsAppearing && m_nFadeRate > 0 )
        m_nFadeRate -= 25;
    else if ( !m_bIsAppearing && m_nFadeRate < 100 ) m_nFadeRate += 25; if ( m_nFadeRate != 100 && !IsVisible() ) Show(); else if ( m_nFadeRate == 100 && IsVisible( ) ) {

    if (IsVisible( ) && m_nFadeRate > 0 && m_nFadeRate < 100)

    if (IsVisible( ) && m_nFadeRate > 0 && m_nFadeRate < 100)

Different Fixes for the Crashes

Not all the crashes can be fixed this way. Sometimes, you have to work on the logic of the application in a higher abstraction level, to be able to fix the crash. For example, see one of the crashes that I have fixed recently.

You could select many footnotes by pressing up/down keys and holding shift, then delete all the footnotes at once. Then, hovering on a reference of such a footnote, lead to crash.

To fix the crash, I bibisected the problem to find the responsible commit. In that specific change, the commit author changed the behavior of the arrow keys to be able to to go to the beginning of the first line just by pressing up in the first line. Also, the same behavior for the last line was also part of the goal.

To fix the problem, I reduced the behavior change to anywhere other than the footnotes, and set the previous behavior for the footnotes. With this change, the crash no longer happened.

diff --git a/sw/source/core/crsr/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx
index b379fe6..06c73af 100644
--- a/sw/source/core/crsr/swcrsr.cxx
+++ b/sw/source/core/crsr/swcrsr.cxx
@@ -2088,7 +2088,9 @@ bool SwCursor::UpDown( bool bUp, sal_uInt16 nCnt,
            bRet = !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos );
        else if (!pFrame->IsInFootnote()) // tdf#150457 Jump to the begin/end
                                          // of the first/last line only if the
                                          // cursor is not inside a footenote
            sal_Int32 nOffset = 0;

@@ -2114,6 +2116,8 @@ bool SwCursor::UpDown( bool bUp, sal_uInt16 nCnt,

            *GetPoint() = aOldPos;

        DoSetBidiLevelUpDown(); // calculate cursor bidi level

Last line, *GetPoint() = aOldPos; comes from the previous behavior, as it was before the commit that introduced the regression.

More Information

There are many other types of crashes, and many other tricks for fixing those crashes. If you look for the crash fixes, you will find many of them, and take a look into them.

git log --oneline|grep -i crash|grep -i fix

You can also refer to a list of recently fixed crashes in the LibreOffice QA report of the August 2022 which is published recently:

QA/Dev Report: August 2022

One can learn a lot from the code itself!

I will continue this tutorial, and talk about other sources of crashes including assert failures, aborts, and exceptions that lead to abort.

Links Related to Program Crashes

In the end, these are some related links:

16 Aug 2022

ODF standard and the code – EasyHack

Open Document Format (ODF) is a standard (ISO/IEC 26300) and native file format that LibreOffice uses. OASIS developed this file format based on the file format of StarOffice, the ancestor of LibreOffice.

To understand the ODF standard, working with the LibreOffice code and fixing a small bug is enlightening. Here we discuss a related EasyHack.

ODF File Extensions

If you are a LibreOffice user, you may already know these extensions:

.odt for text processing documents (LibreOffice Writer)

.odp for presentations (LibreOffice Impress)

.ods for spreadsheets (LibreOffice Calc)

.odg for drawings (LibreOffice Draw)

.odf for formulas (LibreOffice Math)

Essentially, these are zip files containing XML and binary files. Additionally, Open Document Format can also be text-only format with the extensions of .fodt, .fods and .fodg instead of .odt, .ods, and odg respectively.

Open Document Specification

OASIS website provides the documents that describe the Open Document Format extensively . You can find the specifications for ODF 1.3, which is the latest version of the standard here:

OpenDocument V1.3 OASIS Standard published

Beside LibreOffice, several other applications support ODF standard, including Microsoft Office, ONLYOFFICE, Calligra Suite, Google Docs, and many others! But here, we focus on LibreOffice.

Basically, LibreOffice handles the Open Document format by opening the zip file using the zip parser and the deflate algorithm, and then parses the XML files inside it using libxml2 library.

On the other hand, the meaning of the XML tags and attributes are available via the specification from OASIS. In essence, the specification describes the text, pictures, shapes and other elements of the text processing, presentations, spreadsheets and formulas. Additionally, ODF standard discusses accessibility, collaboration and many more topics.

An EasyHack Related to ODF

To best way to understand the ODF standard, is working with the LibreOffice code. Please take a look at this small bug, which has the label of EasyHack (tdf#150302):

In this EasyHack, there is a sample document which is using a specific feature in the drawings. Looking at the <draw:enhanced-geometry> tag, one can see that there is an attribute draw:text-path-same-letter-heights that is used to control the way that text is rendered in shapes. It is described in ODF standard as:

19.224 draw:text-path-same-letter-heights

The draw:text-path-same-letter-heights attribute specifies if all letters in a custom shape have the same height.

The defined values for the attribute are:

  • false: letters in a custom shape need not have the same height.
  • true: letters in a custom shape have the same height.

The default value for this attribute is false.

The issue is that handling this parameter in the code is not correct. Specifically, the effect of this bug is that if you open the sample file and click on the SameLetterHeights button in the Fontwork toolbar, nothing happens. On the other hand, the correct behavior should be that the shape changes, and “shows the characters vertical stretched to fit the place between the paths”.

Shapes inside the example ODF file

Shapes inside the example ODF file

After all, to fix this issue one should refer to the code pointer that Regina Henschel has provided:

If you open the file core/svx/source/toolbars/fontworkbar.cxx, you will see that the else part is missing, and that is the cause of the problem. Specifically, you should fix that part of the code, and then submit your changes to Gerrit. To begin, please refer to this page for more information on how to submit code to LibreOffice Gerrit:

Writing a Test for the Fix

The fix is rather straightforward, but this is not the end of the task! Furthermore, you have to make sure that the problem does not happen again. In order to achieve this goal, writing a unit test is necessary. The test code should open the example file, and then use the toolbar option. This is possible by invoking .uno:FontworkSameLetterHeights command.

Fontwork toolbar, SameLetterHeightsFontwork toolbar: SameLetterHeights is the second icon is the second icon

Fontwork toolbar: SameLetterHeights is the second icon

There are some examples in sd/qa/unit/uiimpress.cxx that invoke UNO commands, and you may use as the starting point.

Final Notes

EasyHacks are good starting points for the newcomers. This specific improvement is filed as tdf#150302.

If you want to contribute to LibreOffice code by working on this improvement, but you need to know how to get started with LibreOffice development, I suggest you to see our video tutorial:

Getting Started (Video Tutorial)


19 May 2022

Interacting with the Review Bot on Gerrit

Have you received “A polite ping, still working on this bug?” message on one of your Gerrit submissions? You can simply send an arbitrary reply to avoid the patch being abandoned within a month. Here we discuss more about Review bot, which is one of the QA (Quality Assurance) tools for the LibreOffice QA team to manage old submissions.

Interacting with the Review bot

You may have received messages from some bots, including Review bot. This bot checks Gerrit to find the older submissions, and abandon the old ones that no one actively work on them.

If it has been several months after the last change on a submission, the bot adds a comment and asks:

A polite ping, still working on this bug?

Then, you can do one of these things, according to your choice:

    1. Reply with whatever you want, and the Review bot will not longer try to abandon your submission. Do this if you want to continue work on the submission.
    2. Mark the patch as “work in progress”. This would be helpful if you want to prevent the bot from monitoring the submission.
    3. Leave it as is. Then, after 1 month your patch will be automatically abandoned with this message:


Abandoning this for the moment due to inactivity. Be aware it can be reopened anytime if you still want to continue working on it. Do not forget to rebase it first.

Even in this case, you don’t have to worry! As the message says, you can simply click on the RESTORE button on the top right of the page to restore the patch and continue working on it.

Please note that if you have restored an old submission, you have to do a re-base to get the latest changes in the LibreOffice code, pushed while your submission was remaining intact. If there is not a merge conflict, you can do this easily by clicking on the REBASE button on the top right of the page. Otherwise, you have to download the patch and resolve the merge conflict manually.

More information

You can find more information about

Also, you can read this previous post on how to use Gerrit code review.

5 May 2022

How to write a good commit message

If you have started your journey to become an experienced developer, you already know that you have to describe what you have done when you change the code and submit it to be merged in the master branch. In git and many other source code management systems, this description is called a commit message.

The commit message has a title, and can have a detailed description. You should separate the description from the title by adding a blank line after the title.

Why it matters to write a good commit message

Some may argue that the code itself is the most important thing, and you should provide a readable clean code. This is true, and you should care most for the code. But, on the other hand, when you are working on a big project with hundreds of developers, it is also important to write descriptive commit message that is easy to read for other developers who work on the same project.

In order to be able to search and find the relevant historical information about different aspects in the code, a good way would be searching in the commit messages. You can invoke:

$ git log

You can press “/” for search, then type a search phrase. By pressing “Enter”, you will get a match, and then the next match by pressing the key “n”. This happens if matches are found.

This is only possible if you and others have previously provided good information on what each change does, providing enough details and keywords, so that others could be able to search efficiently.

If you have added your changes to the staging area of the git, then you can invoke this command to commit the changes.

$ git commit

You should provide a good title and description. Here’s how.

Information Beyond the Commit Message

Git commits also have other information, that are automatically generated. For example, the “Author” field comes from your git settings. You should use your complete first name and surname, and a valid email. Your timezone also comes from your system settings.

The Change-Id is generated when you commit, and it is used to identify a submission across different patch sets. Gerrit adds “Reviewed-on”, “Reviewed-by” and “Tested-by” fields automatically. For example, consider this commit:

Author: Miklos Vajna <vmiklos@collabora.com>
Date: Wed Apr 27 20:12:52 2022 +0200

sd theme: add PPTX import for shape fill color effects

This is always direct formatting, so FillProperties::pushToPropMap()
always has the needed info at hand.

Change-Id: I3317b618e0e8bb7688d0f0fbfe4546e2e8b4e947
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133525
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>

Use an informative title with 50 characters or less

You should describe what you have done in a commit in the commit title. The suggested maximum length for the commit title is 50 characters. Also, you should limit the amount of work in a single commit to only one thing. There are many reasons for that; one of them is to be able to roll back your changes with a single git revert.

For LibreOffice commits, you should provide the issue id at the first of the title. For example, tdf#xyz refers to the bug number xyz in the Bubzilla:

Another abbreviation, cid#xyz refers to the Coverity scan report number xyz:

For other abbreviations, please refer to this Wiki article:

You can refer to the module with its abbreviation (sw, sc, etc.) to emphasize the module that the patch is related to. Here is an overview of the modules of LibreOffice.

It is important to keep the title short, because other people can get the whole idea of the change that you have done at a glance. You can see the list of recent changes in this page:

Also, you can pull the latest changes, and use information from git locally, using these commands:

$ ./g pull -r
$ git log --oneline --since=1week

The above command lists the title of commits from the previous week.

Provide a Detailed Description

Beyond the title, you should provide detailed description of what you have done in the current commit. You can link to other commits with the commit hash if needed.

Using lists with *, +, – or characters like that can help to provide a better formatting for the texts. For example, consider this commit:

Author: Miklos Vajna <vmiklos@collabora.com>
Date: Thu Apr 21 09:08:03 2022 +0200

sw content controls: add insert UI

- add an SwWrtShell::InsertContentControl() to put the current selection
into a content control
- if there is no selection, add a non-empty placeholder
- expose this as a new .uno:InsertContentControl uno command
- add this new command to the bottom of the form menu -- probably we can
have a sub-menu there once there will be more types

Various details are provided as a list. The syntax is mostly similar to the Markdown. Some people argue that the width of the lines should be limited to 72 characters, but there is no consensus on the exact maximum width for line wrapping.

What to Include in the Commit Message?

Include relevant information that can help other developers, including but not limited to:

  • Affected modules
  • history of the problem, and the solution
  • cause of the regression, if this is a fix for a regression
  • backtrace (or parts of it), if needed
  • references
  • any relevant information

How Much Should I Write?

It all depends on you, but please describe what you are doing! Remember that what you write will be read in the future by the others, thus it should describe the changes that you have done in the commit.

Writing a paragraph is the minimum thing that is expected, so the suggestion is that you avoid submitting patches with blank description.

26 Apr 2022

Supporting metafile formats: WMF/EMF/EMF+

LibreOffice supports many file formats, and among them are some raster and vector image formats from Microsoft. Metafile formats WMF, EMF and EMF+ are among the vector formats usable in Microsoft products, and also in LibreOffice. Here we discuss the implementation of the  support for these file formats in LibreOffice.

We call these file formats metafiles, as they are means of storing drawing commands that are calls to the Windows API that draws shapes and text on the screen. It is possible to replay these metafiles to have a graphical output in an appropriate context.

It is possible to create complex shapes using metafiles. For example, if you take a look at the odk/examples/basic/forms_and_controls folder in the LibreOffice source code, you will see some nice examples. Here is one of them: A delicious burger created using vector primitives.



The oldest metafile format is WMF, which goes back to Windows 3.1 during 1990s, but EMF and EMF+ are newer formats and support many new features.

LibreOffice can open these file formats. It is also possible to use them as the images inside another formats. For example, you can have WMF inside a DOC or DOCX file, or in native ODT format of LibreOffice.

Code Structure for Metafile Support

The emfio module of LibreOffice handles reading of the metafile records. It essentially translates these records into the mataactions. Then, vcl and drawinglayer display them. You can take a look at emfio/source/reader to see the main source files, wmfreader.cxx and emfreader.cxx in which rely on mtftools.cxx.

An important class which is used here is the GDIMetaFile class, which is described in the vcl/README.GDIMetaFile.md as:

The GDIMetaFile class reads, writes, manipulates and replays metafiles via the VCL module.

A typical use case is to initialize a new GDIMetaFile, open the actual stored metafile and read it in via GDIMetaFile::Read( aIStream ). This reads in the metafile into the GDIMetafile object – it can read in an old-style VCLMTF metafile (back in the days that Microsoft didn’t document the metafile format this was used), as well as EMF+ files – and adds them to a list (vector) of MetaActions. You can also populate your own GDIMetaFile via AddAction(), RemoveAction(), ReplaceAction(), etc.

Once the GDIMetafile object is read to be used, you can “play” the metafile, “pause” it, “wind forward” or “rewind” the metafile.

Other than reading, LibreOffice can write metafile formats as the output. For the output filter, vcl/source/filter/wmf is the place to look at.

Tools for Working with Metafile Formats

As the metafile formats are binary files, you will need tools to be able to work with these formats. We discuss three useful tools among others: mso-dumper, limerest and mtf-demo.


The mso-dumper is an in-house developed tool created and used by LibreOffice developers to dump information from the Microsoft binary formats. It reads the binary files WMF, EMF, EMF+ in addition to DOC, PPT, XLS and other Microsoft formats, and dumps it as xml.

To be able to dump a metafile, you should do this:

$ git clone https://git.libreoffice.org/mso-dumper
$ cd mso-dumper
$ ./wmf-dump.py burger.wmf
$ ./emf-dump.py computer_mail.emf

Please note that the burger.wmf and computer_mail.emf files are available inside the LibreOffice source. You should replace the burger.wmf and computer_mail.emf with the path of the file you want to dump its structure.


Re-lab provides this tool which had the former name of OLEToy. It is a graphical tool that is suitable for understanding the binary file formats including the metafiles, and investigating the contents of the sample binary files. In addition to the visual display of records and their contents, it has a nice hex viewer that is very handy when debugging binary formats.

You can download this tool from here:



The mtf-demo is also a useful tool for displaying the metafiles WMF, EMF/EMF+, and also dumping the metaactions.

A demo renders of a metafile using vcl be seen by:

./bin/run mtfdemo odk/examples/basic/forms_and_controls/burger.wmf

This opens the burger.wmf as displays it in a window. You should have built the LibreOffice from sources to be able to run the above command from the LibreOffice source folder.

For debugging purposes, it is also possible to dump metaactions created as the intermediary format before rendering the metafile using -d option:

./bin/run mtfdemo -d odk/examples/basic/forms_and_controls/burger.wmf

If the command is successful, this message will be shown, and metadump.xml will be put in the current folder. The output will be:

Dumped metaactions as metadump.xml

DrawingLayer Primitives

The actual drawing is done using vcl and drawinglayer. One can dump the drawinglayer primitives for debugging purposes. We don’t have a dedicated tool to dump the primitives, but if you look at the tests inside emfio/qa/cppunit/emf/EmfImportTest.cxx, you can add this code snippet to do this:
Primitive2DSequence aSequence = parseEmf(u"emfio/qa/cppunit/wmf/data/stockobject.emf");
drawinglayer::Primitive2dXmlDump dumper;
Primitive2DContainer aContainer(aSequence);
dumper.dump(aContainer, "/tmp/drawinglayer.xml");

Then, after invoking make CppunitTest_emfio_emf, /tmp/drawinglayer.xml will be the dump of the drawinglayer primitives to the /tmp/drawinglayer.xml.

Status of Metafile Support in LibreOffice

Support of metafile formats in LibreOffice is not complete. Some unimplemented records, and some bugs remaining. If you want to help, you can refer to the emfio documentation to see the list of unimplemented records in WMF and EMF/EMF+. Please look at the “Limitations” section.

Recently, Bartosz Kosiorek implemented SETARCDIRECTION record of the EMF. He added the support for this specific record to the LibreOffice core with this commit:

His implementation consists of several changes, including a change in emfio/source/reader/emfreader.cxx to read the record from the file and create tools::Polygon aPoly with additional parameter IsArcDirectionClockWise(). Also, change in emfio/source/reader/mtftools.cxx to set the mbClockWiseArcDirection member variable of the MtfTools class, adding setter/getter for this variable, and also changes in tools/source/generic/poly.cxx to change Polygon::Polygon() and ImplPolygon::ImplPolygon() to add support for the extra parameter, a boolean variable bool bClockWiseArcDirection.

This commit also contains a sample document and a new unit test, TestSetArcDirection() to make sure that the support for this record does not break easily in the future.

Final Notes

For a detailed tutorial on how to fix regressions, you can refer to this blog post:

Regression Fix: Missing Lines in DOCX

19 Apr 2022

Using cmake to build LibreOffice C++ SDK examples

These days, many C++ projects are built using build tools like cmake and meson in addition to GNU make (gmake). In this blog, I have already written on how to compile and run LibreOffice SDK examples using gmake. Now I want to discuss instructions for compiling and running C++ examples using cmake as the build tool. For the previous instructions using gmake, see this older post:

Working with LibreOffice SDK Examples

Compiling and Running using cmake

Using Official LibreOffice Binaries and SDK

To be able to compile and run the C++ examples using cmake, you should have installed LibreOffice and LibreOffice SDK, then you should set LOROOT in CMakeLists.txt to appropriate folder. For LibreOffice 7.3 SDK, you should use this line:

set (LOROOT /opt/libreoffice7.3)

Compiling and running the C++ programs would be easy. For some of the examples, you need to run an instance of LibreOffice to listen for the incoming connections. So, you have to invoke:

$ libreoffice7.3 "--accept=socket,port=2083;urp;"

and then just open the project file in Qt Creator (or any other IDE of your choice that supports cmake), and click Build and then Run.

Local Build

If you have built LibreOffice yourself, use the instdir path for LOROOT:

set(LOROOT = /home/hossein/Projects/libreoffice/core/instdir)

If you use a local build, you may need a running instance of LibreOffice. For this purpose, invoke:

$ ./instdir/program/soffice.bin "--accept=socket,port=2083;urp;"

And execute the project from your IDE.

Building and Running from Command Line using cmake

Here I assume that the project is named example. To build and run the example from the command line using cmake, go to the source folder, and then invoke:

$ mkdir build
$ cd build
$ cmake ..
$ make
$ ./example

The CMakeLists.txt file containing the instructions for building the the example project can be as following:

cmake_minimum_required(VERSION 3.5)

project(example LANGUAGES CXX)


set(LOROOT /opt/libreoffice7.3)

add_executable(example main.cpp)


target_link_directories(example PRIVATE

execute_process(COMMAND ${LOROOT}/sdk/bin/cppumaker -Gc -O. ${LOROOT}/program/types.rdb ${LOROOT}/program/types/offapi.rdb)

Essentially, the above file specifies these:

1) Include directories: sdk/include, com/sun/star and also the source directory.

2) Library directories: sdk/lib and program.

3) Name of the linked libraries: -luno_sal -luno_cppu -luno_cppuhelpergcc3 -luno_salhelpergcc3 -lunoidllo -lxmlreaderlo -lreglo -lmergedlo

4) Using cppumaker utility to generate UNO headers from the binary description offapi.rdb. You can read more about LibreOffice SDK utilities in LibreOffice DevGuide chapter 4.

Important Environment Variables

Several environment variables are set when you use the original scripts provided by the LibreOffice SDK. When using cmake, you may have to set these environment variables:

On Linux:

export UNO_PATH=/opt/libreoffice7.3/program
export URE_BOOTSTRAP=vnd.sun.star.pathname:/opt/libreoffice7.3/program/fundamentalrc

and on Windows:

SET UNO_PATH=C:/Progra~1/LibreOffice/program
SET URE_BOOTSTRAP=vnd.sun.star.pathname:C:/Progra~1/LibreOffice/program/fundamental.ini

LibOCon 2021 Presentation

I have previously talked about using cmake to build LibreOffice SDK examples in LibOCon 2021. You can see the presentation below:

Please accept YouTube cookies to play this video. By accepting you will be accessing content from YouTube, a service provided by an external third party.

YouTube privacy policy

If you accept this notice, your choice will be saved and the page will refresh.

Presentation: LibreOffice SDK Examples OverhaulHossein Nourikhah

This talk was presented in the LibreOffice Conference 2021 (LibOCon 2021) (Slides)

LibreOffice Conference 2021

LibreOffice Conference 2021

11 Apr 2022

How to use Gerrit code review effectively

Any code submission to LibreOffice should pass Gerrit code review in order to get merged into the LibreOffice codebase. This is possible using the code review tool Gerrit.

To understand more, refer to these articles:

For getting started with LibreOffice development in general, watch this video.

Here, I assume that you have read those articles, you have created your account, and you have done your first contributions to the LibreOffice – but you want to know more about how to effectively use Gerrit. This is what I am discussing in this article.

LibreOffice Gerrit

LibreOffice Gerrit

How to Find Reviewers

If you are a newcomer, possibly I (Hossein) and Ilmari can help you. You can add us as the reviewers, and we will review your submissions. Just search for our names in front of the “Reviewers” part in your submission, and you can easily add me or Ilmari as the reviewer.

If you want to work on more complicated tasks, you can also refer to this page to find experts in different areas.

We have experts in these areas:

Access2Base, Automated tests, BASIC, Bugzilla, build system (gbuild), Calc, Chart, Database, Debian, adding dictionaries, documentation, Draw, General UI problems, git⁠ and Writer RTF filters, gerrit⁠, GUI design questions, headless (–without-x build switch), i18nlangtag, i18npool, Impress presentation, KDE, Libcdr and Corel Draw import, libvisio and Visio import, localization fixes / l10n / non-English bug description, localization fixes / l10n tools, Mac, ODF filter, Pebble, PostgreSQL integration, QA, ScriptForge, Translation, Ubuntu, UI, UNO, sal, configmgr, VCL, Weblate, Wiki, Wiki Extension, Wiki Config, Editing, Windows, Windows Installer, Writer, writerfilter, X11-specific

Quite a lot of topics!

But, as you can read in that Wiki page, please “Please do not email these developers personally with your personal pet bug”. Just add them as the CC or the reviewer of a relevant submission.

Handling Problems with the Jenkins builds

Sometimes it happens that Jenkins can not build your submission. This can happen for several reasons. It can be because of a problem in your code, or a problem with the build system itself.

If the problem is from your side, then fix the problem and re-submit your code as a new patch set, and Jenkins will try to build it again.

But, if the problem is with Jenkins, you can either ask on #tdf-infra and someone with appropriate access will resume your build. Another solution would be doing a re-base and then Jenkins will build your submission again. For doing this, you do not need any special permissions.

Please be patient, and keep in mind that CI is a limited resource, so you should use it carefully. Please note that you should build (using make) and test (using make check) on your computer locally before submitting the code to the Gerrit.

Working on Multiple Gerrit Submissions

Sometimes you want to work simultaneously on multiple submissions. Then, you have to create a branch for each of these submissions to be able to work on them.

A tool named git review can help you manage your submissions easily. It is a review tool, but you can use it both for submissions from others (for reviewing), and also your own submissions.

To download a submission with the id of 1234, you can easily do:

git review -d 1234

The id (1234 here) is the number you see in the end of the submission URL, for example: https://gerrit.libreoffice.org/c/core/+/1234. Then, your patch is downloaded, and a new branch is created for that submissions.

If you want to do a re-base, do this:

./g pull -r

If you face a merge conflict, you have to download your changes using the above method, do a re-base, and then try to resolve the conflicts. Then, you have to use git add to stage the changed source files.

When you have finished fixing your submission, you can do:

git commit --amend
./logerrit submit master

If you want to go back to the master branch, just use:

git checkout master

Handling Requested Changes From the Reviewers

In the review process, you should do the changes requested from the reviewers. Sometimes, you may disagree with the reviewers, but then you should provide good reasons for not doing the requested changes in order to convince the reviewers.

Anyway, you should know that your code must conform to certain requirements in order to be eligible for merging. So, try to convince the reviewers that your code deserves to get merged into the code.

For the code conventions and coding style, it is suggested that you refer to these links:

For newer files, you may have to use clang-format, to re-format the code, and convince the CI that your code is fine!

There are many rules in the above links. Among them, this list on variable name prefixes is very useful. The list is extracted and suggested by Mike and Heiko:

  • (Smart) pointer/reference to an UNO interface -> xName
  • (Smart) pointer to anything else -> pName
  • Reference to anything else -> rName
  • Integer value -> nName
  • Floating-point number -> fName
  • Boolean -> bName, or isName, or canName
  • String -> optionally sName (for char * -> pName; for OUString -> aName)
  • Another object -> aName

Other conventions are gathered as extensive set of rules. If you had any doubts, you can refer to the appropriate section that discuss design, implementation, testing and other aspects.

On the other hand, you should know that the easiest part of the change is the coding style! There are many things related to good design and implementation that you should take care to be able to pass the code review and get a +2.

Search Among the Gerrit Submissions

By clicking on the name of each person in the Gerrit, you can see their contributions. But, this is not the whole story! Gerrit has a powerful query language that you can use inside the search box. For example:

owner:self: My own contributions

reviewer:self: Submissions that I am reviewing

owner:self status:merged: My merged contributions

owner:self status:open: My open contributions

You can use boolean operators between the queries:

AND -(negation) OR

If you do not specify anything, then AND is used.

You can do a lot of complicated searches among the submissions using its manual.

Final Remarks on Gerrit Code Review

Gerrit is a powerful tool, and you can use many features of it to work more effectively. But, keep in mind that your focus should be on fixing the possible problems of your code, and improving your code quality.

Don’t worry! Reviewers usually help you on the road to fix the problems. Although in the end, it is your responsibility to address the concerns and do the recommendations – and eventually fix your code.

To get more help, you can always refer to its manuals.

I hope that you will get the best out of this review tool!