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 theVCL
module.A typical use case is to initialize a new
GDIMetaFile
, open the actual stored metafile and read it in viaGDIMetaFile::Read( aIStream )
. This reads in the metafile into theGDIMetafile
object – it can read in an old-styleVCLMTF
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) ofMetaActions
. You can also populate your ownGDIMetaFile
viaAddAction()
,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.
Mso-dumper
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.
Limereset
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:
https://gitlab.com/re-lab-project/limerest
Mtf-demo
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: