Development/ODF Implementer Notes

From The Document Foundation Wiki
Jump to: navigation, search

This page concerns ODF-specific Implementer Notes for LibreOffice and DLP developers.

For more general information about the format, see the ODF page.

ODF Technical Events

LibreOffice participates in ODF Plugfests hosted by the OpenDoc Society:

ODF Extensions

ODF Version Choice

In the code, look for ODFVER_* enum values - currently, there is ODFVER_UNKNOWN, ODFVER_010, ODFVER_011, ODFVER_012, ODFVER_012_EXT_COMPAT, and ODFVER_LATEST (this means ODF 1.2 extended at the moment). If you implemented a feature that needs to extend ODF - please use the following pattern:

 // export only if ODF 1.2 extensions are enabled
 if( SvtSaveOptions().GetODFDefaultVersion() > SvtSaveOptions::ODFVER_012 )
 {
  ...
 }

ODFVER_012_EXT_COMPAT should be used to fallback to old, incorrect output - we e.g. use that to write svg:d path statements that are interpreted correctly by buggy legacy ODF consumers.

If you need to distinguish between specific ODF versions, for example if a feature is proposed to be included in ODF and will be part of ODF 1.3, then you need to write it as an extension for earlier versions and as standardized for versions >= 1.3, then use the SvtSaveOptions::ODFSaneDefaultVersion enum for conditions. The difference between ODFSaneDefaultVersion and ODFDefaultVersion enums is that ODFSaneDefaultVersion provides enums that can be used in exact comparisons and the "plain" ODF version is smaller than the same version with extensions allowed and if extensions are allowed the ODFSVER_EXTENDED bit is set and SvtSaveOptions::GetODFSaneDefaultVersion() returns always a specific ODF version, whereas ODFDefaultVersion::ODFVER_LATEST is a running target meaning always the latest and greatest that you don't know which it actually is.

Obtained either through SvtSaveOptions::GetODFSaneDefaultVersion() or SvXMLExport::getSaneDefaultVersion():

SvtSaveOptions::ODFSaneDefaultVersion eVersion = SvtSaveOptions::GetODFSaneDefaultVersion();
if (eVersion < SvtSaveOptions::ODFSVER_013 && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0))
{
    ... write extension namespace etc. ...
}
else if (eVersion >= SvtSaveOptions::ODFSVER_013)
{
    ... write ODF 1.3 namespace etc. ...
}
// else, version is < 1.3 and extensions are not allowed, the feature is not saved.

Another way to express "save only if 1.2 extended or in next version 1.3 or later" with SvXMLExport& rExport would be

// Export only for 1.2 with extensions or 1.3 and later.
SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
if (eVersion > SvtSaveOptions::ODFSVER_012)
{
    // For 1.2+ use loext namespace, for 1.3 use number namespace.
    rExport.AddAttribute(
            ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
            XML_your_feature, aString );
}

Extension namespaces

Elements and attributes that are not defined in an ODF specification yet, even if already submitted as a proposal to the OASIS ODF-TC, have to be written with an extension namespace, otherwise validators will complain about invalid elements or attributes. For example, instead of text:new-attribute it needs to be loext:new-attribute. There are predefined extension namespaces available, the most common is XML_NAMESPACE_LO_EXT that maps to the loext of the example, which you would use instead of XML_NAMESPACE_TEXT. (See XML_NAMESPACE_EXT in include/xmloff/xmlnmspe.hxx for definitions of namespaces)

In the ODF reader prepare to import both namespaces, in this example the loext that is written and the proposed future namespace, here text. If you don't do this then the current version will not be able to read documents that use the proposed attribute once it was accepted by the OASIS ODF-TC. For example by using

if (nPrefix == XML_NAMESPACE_LO_EXT || nPrefix == XML_NAMESPACE_TEXT)
{
 ...
}

or in an import (!) property map have two SvXMLTokenMapEntry

{ XML_NAMESPACE_LO_EXT, XML_NEW_ATTRIBUTE, XML_TOK_TEXT_NEW_ATTRIBUTE },
{ XML_NAMESPACE_TEXT,   XML_NEW_ATTRIBUTE, XML_TOK_TEXT_NEW_ATTRIBUTE },


Extensions in XMLPropertyMapEntry arrays

In XMLPropertyMapEntry arrays that are passed to the XMLPropertySetMapper ctor you can use the XMLPropertyMapEntry::mbImportOnly member variable to be able to read more than one namespace/element/attribute name using the same XMLPropertyHandler but write only one combination, for example

MAP_EXT( "NewAttribute", XML_NAMESPACE_LO_EXT, XML_NEW_ATTRIBUTE, XML_TYPE_PROP_foo|XML_TYPE_bar, 0 ),
MAP_EXT_I( "NewAttribute", XML_NAMESPACE_TEXT, XML_NEW_ATTRIBUTE, XML_TYPE_PROP_foo|XML_TYPE_bar, 0 ),

writes and reads loext:new-attribute (mbImportOnly=false) and also reads text:new-attribute (mbImportOnly=true). See sc/source/filter/xml/xmlstyle.cxx for the definition of the MAP_EXT and MAP_EXT_I macros.

LibreOffice ODF extensions

LibreOffice OpenFormula extensions