Development/Remarks on Fontwork and TextWarp

This is about the type of shapes, which are available via the "Fontwork Gallery" in LibreOffice and via the "Text Effects" of kind "Transform" in 'WordArt styles' in MS Office.

File Format
LibreOffice has "Fontwork"-shapes. Those are custom-shapes, where the attribute 'text-path' is set to 'true'. The text of the custom-shape is given as a sequence of text:p and/or text:list elements.

MS Office has WordArt "Transformation". Such object is a shape with prstGeom="rect", and therein a textbox with element prstTxWarp and attribute prst="textFoo".

Style
LibreOffice uses for the fill and outline of the characters not the text style of the characters but the fill and stroke of the shape.

The fill and outline of text in TextWarp are properties of character portions (called 'run' in DrawingML). The object itself can have fill and stroke in addition. Fill and outline of characters belong to extensions of MS Office to the standard.

LibreOffice does not allow different styles for portions of the text in a Fontwork shape, but all characters have the same text properties. That is a restriction of LibreOffice, not of ODF.

TextWarp shapes can use different styles for portions of the text.

Path
ODF allows arbitrary paths to be used by 'text-path'. LibreOffice renders them, but has no UI to define such paths.

DrawinML allows only the 40 predefined paths, which are given in presetTextWarpDefinitions.xml (https://standards.iso.org/ittf/PubliclyAvailableStandards/c071691_ISO_IEC_29500-1_2016_Electronic_inserts.zip)

Alignment
LibreOffice uses the middle of multi-line text to align the text radial to the curve in 'circle', 'arch up', 'arch down' and 'button' shape.

MS Office aligns it at bottom or top, depending on the shape, so that the second and following lines will be outside the frame box (= the rectangle of the resize handles).

LibreOffice does not allow different paragraph alignments inside a single Fontwork shape. The paragraph alignments in TextWarp-shapes are independent from another.

Scale
ODF has the attributes text-path-mode (values 'normal', 'path' and 'shape') and text-path-scale (values path/shape). The descriptions are vague. text-path-mode text-path-scale http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#attribute-draw_text-path-mode
 * normal: text is drawn along the path without scaling;
 * path: text is fitted to a path;
 * shape: text is fitted to the bounding box of a shape.
 * path: text scaling is determined by the length of the path from the draw:enhanced-path attribute.
 * shape: text scaling is determined by the width of a shape.

LibreOffice hasn't implemented the attribute 'text-path-mode', although it is published in the API.

LibreOffice uses in addition the graphic-property 'draw:fit-to-size' for legacy fontwork shapes, which is the property 'Stretchy Justify' in the UI.

OOXML has a detailed description, how to render the text in section 20.1.9.19 in part ISO/IEC 29500-1:2016(E)

http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html

The given algorithm does not fit to the types 'textCircle', 'textArchUp', 'textArchDown' and 'textButton', which are listed in category 'Follow Path' in the MS Office UI. MS Office has two ways to draw the text for these shapes. If the shape is newly created, the text is drawn with the font size, which is applied to the characters. If the shape is imported from binary ppt-format, the property 'fromWordArt' is set to "1" (true) and the text is drawn similar to the way older MS Office versions handle it. The font size is increased, so that the text is as width as the path. But the path length seems to be restricted compared to older versions.

Legacy shapes
LibreOffice uses the element text:list for the content of a curve type Fontwork shape, which is imported from legacy file formats, current Fontwork shapes use a sequence of text:p elements. LibreOffice uses this difference to render the shapes same as in legacy formats and to evaluate "Stretchy Justify" for those shapes.

MS Office uses the 'fromWordArt' attribute of the 'bodyPr' element, which is provided by the standard for this purpose.

ScaleX
ODF attribute text-path-scale is mapped to the API property boolean ScaleX. Description: This property specifies if the text is scaled using the shape path.

ODF attribute text-path-mode is mapped to API property ::com::sun::star::drawing::EnhancedCustomShapeTextPathMode TextPathMode. Description: This property specifies how the text is drawn. The enum has values NORMAL, PATH and SHAPE. LibreOffice does not use it, but preserves it.

Notice, that the descriptions are as bad as those from ODF. You get the meaning only from code.

https://opengrok.libreoffice.org/xref/core/xmloff/source/draw/ximpcustomshape.cxx?r=43635b47#1107

gives the mapping

ScaleX="true" <=>  text-path-scale="shape"

ScaleX="false" <=> text-path-scale="path"

The implementation in

https://opengrok.libreoffice.org/xref/core/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx?r=4709cd82#183

really explains how LibreOffice uses it: if (rFWData.bScaleX) aFont.SetFontHeight( nFontSize ); else aFont.SetFontHeight( rSdrObjCustomShape.GetLogicRect.GetHeight / rFWData.nMaxParagraphsPerTextArea );

The effect of the property ScaleX depends on the type of Fontwork-shape. The shapes (in UI names) "Arch Up (Curve)", "Arch Down (Curve)", "Circle (Curve)" and "Open Circle (Curve)" belong to the category, which are named "Follow Path" in the UI of MS Office. The shapes (in UI names) "Arch Left (Curve)" and "Arch Right (Curve)" are faulty and do not work at all. All other shapes belong to the category, which is named "Warp" in the UI of MS Office.

Shapes of category "Follow path" have this behavior: In case ScaleX=true, the width and height of the characters is given by the font settings of the paragraph properties element, which is contained in the graphic style, which is applied to the shape. Settings on the paragraph or characters of the contained text are ignored. In case ScaleX=false, the font size is increased until the width of the text has the length of the path. For multi-line text, the longest line is considered.

Shapes of category "Warp" always have pairs of paths. The text is non-uniformly scaled in the height, so that it fits between the two paths of a pair in all cases. In case ScaleX=true the width is taken from the font size of the text, and in case ScaleX=false, the width is scaled, so that the width of the text has the length of the path.

The property ScaleX has currently no UI, but can be used via macro.

To get a visual appearance that is nearer to that of MS Office, for the TextWarp types of the category "Warp" in MS Office UI, the setting needs to be ScaleX=false. That way the text fills more of the available area. But there will be still the problem, that LibreOffice treats the pairs separately and MS Office treats all text as one block, according to the algorithm in the standard. Besides that, there seem to be differences in the way space above and below the lines is handled.

Shape Names
LibreOffice distinguishes the treatment of the different shapes depending on the attribute draw:type in ODF and the preset shape names of OOXML.

LibreOffice uses an enum MSO_SPT, which is defined in include/svx/msdffdef.hxx. The names and numbers are not arbitrary, but follow the table in section 2.4.24 MSOSPT of [MS-ODRAW] Office Drawing Binary File Format.

Relevant mappings are:

/core/filter/source/msfilter/util.cxx CustomShapeTypeTranslationTable {const char* sOOo, const char* sMSo} Methode GetOOXMLPresetGeometry

All names for Fontwork shapes are commented out. Therefore, when this is used in oox/source/export/shapes always "rect" was returned. This table contains 'ooxml-foo' names too, but is not reversible. The prefix 'ooxml-' is set on import from OOXML file format.

/core/filter/source/msfilter/util.cxx pDMLToVMLTable {const char* sDML, MSO_SPT nVML} Methode GETVMLShapeType

This table contains the presetWarpDefinitions and names of ordinary shapes. It maps strings to enums, which then can be used in switch statements.

oox\source\drawingml\presetgeometrynames.cxx pPresetGeometryNameArray {const char* pMsoName, const char* pFontworkType} Methode GetFontworkType and (proposed) for the reverse direction GetMsoName

It maps between presetWarpDefinitions and the internal DrawingML names, but only for Fontwork types.

svx/source/customshapes/EnhancedCustomShapeTypeNames.cxx pNameTypeTableArray {const char* pS; MSO_SPT const pE;} Method Get in both directions

It maps between the internal DrawingML names and the enums.