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:
GDIMetaFileclass reads, writes, manipulates and replays metafiles via the
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
GDIMetafileobject – it can read in an old-style
VCLMTFmetafile (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
GDIMetafileobject 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
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
./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
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");
Then, after invoking
/tmp/drawinglayer.xml will be the dump of the drawinglayer primitives to the
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
ImplPolygon::ImplPolygon() to add support for the extra parameter, a boolean variable
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.
For a detailed tutorial on how to fix regressions, you can refer to this blog post: