Documentation/Text in Custom Shapes
This article is intended for advanced users and developers. It shows the possibilities which are basically given in the file formats ODF and OOXML and presents their implementations in the current (vers. 6.4) LibreOffice.
- 1 Text Area
- 2 Horizontal Position
- 3 Vertical Position
- 4 Text in Custom Shapes in Writer
- 5 Padding
- 6 Rotation of Text
- 7 Vertical Writing Mode
- 8 Problems with Attribute vert
The area into which a text is written is determined by the attribute draw:text‑areas of element <draw:enhanced‑geometry> in ODF. That is the property TextFrames in property Path in API/core.
The examples use the shape “Right Triangle”. Its text area is defined as left=1/12, top=7/12, right=7/12 and bottom 11/12. For a shape with 12cm width and 6cm height this results in a text area rectangle with left|top (1cm|3.5cm), right|bottom (7cm|5.5cm), width 6cm and height 2cm. This text area is indicated by a green dashed rectangle.
These values in the example belong to the definition of the shape in the OOXML preset and in the MS binary format preset. LibreOffice's own shapes are derived from the latter. The text area position and size differs slightly between OOXML and MS binary format, but that is not relevant here.
If no text area is specified, the entire shape rectangle is used.
OOXML has the concept of a “text area” too. There it is determined by the child element <rect> of the <custGeom> element and similar specified in the definitions of the preset shapes. The datatype is CT_GeomRect. Its values are always given in the outer coordinate system of the shape. That is different from ODF, where the values are given in the internal coordinate system of the shape, which is specified by the attribute svg:viewBox.
LibreOffice produces interoperability only in case of OOXML preset shapes by applying some tricks. It uses the attribute values svg:viewBox="0 0 0 0" to indicate, that no internal coordinate system is used and writes a value of kind "ooxml-foo" for the attribute draw:type with the name of the preset as 'foo'. So LibreOffice can regenerate the used preset and use it for export to OOXML and for rendering.
On export of own shapes to OOXML, always a text area <a:rect l="l" t="t" r="r" b="b"/> or <a:rect l="0" t="0" r="r" b="b"/> (which is the same in content) is generated. That is in many cases not usable, it fails for "Diamond", "Right Triangle" and block arrows, for example. In case of import from OOXML and a shape with element <custGeom>, LibreOffice uses the current shape size as text area, without considering the element <rect> at all.
The horizontal position of the text is determined by the graphic style attribute draw:textarea‑horizontal‑align with values justify, center, left and right. That is the property TextHorizontalAdjust with the values LEFT, CENTER, RIGHT, BLOCK of enum TextHorizontalAdjust in API/core. The UI for these settings is the section “Text Anchor” in tab “Text” of the “Text”-dialog.
In all three cases the option “Full width” is not checked.
A checked option “Full width” corresponds to the value justify. If a horizontal writing mode is used, the three center positions top, middle and bottom are possible. If a vertical writing mode is used, the three middle positions right, center and left are used instead.
The description in section 20.166 in the standard ODF 1.2 is, “The draw:textarea-horizontal-align attribute specifies the horizontal alignment of the text area inside a shape.” This is misleading. Of course this does not mean the alignment of the text area in the shape; it has a fixed position. It is about the alignment of the text within this area. The examples show how it works.
All settings are different from the alignment settings of paragraphs. To make this distinction visible, the term “Anchor” instead of “Alignment” is used in the UI.
The examples use the following text:
First line, paragraph alignment “centered”: First
Second line, paragraph alignment “left”: Second
Third line, paragraph alignment “right”: Third a long text
|The reference for the paragraph alignment is the left edge, the center line and the right edge of the text area, according the alignment setting of the paragraph.|
Left, Right, Center
|The reference for the paragraph alignment is start and end of the longest paragraph.|
|After positioning the paragraphs relative to each other, the bounding rectangle of the entire text is built.|
And this is positioned left, centered or right into the text area.
Because of using the longest paragraph as reference, it makes no sense to enable “Word wrap”.
What happens, if a paragraph is longer than the width of the text area? The examples use the text “Third line has a very long long text” for the third line now.
I expect this behavior for the TextHorizontalAdjust cases left, center and right:
The entire text as unit (bounding rectangle) uses
the left edge of the text area as anchor and overflows to the right in case of value left.
the middle of the text area as anchor and overflows to both sides in case of value center.
the right edge of the text area as anchor and overflows to the left side in case of value right.
The actual rendering is as expected.
Text overflows right.
Text overflows to both sides.
Text overflows left.
I expect this behavior for the cases TextHorizontalAdjust case justify:
A left aligned paragraph uses the left edge of the text area as anchor and overflows to the right.
A center aligned paragraph uses the middle of the text area as anchor and overflows to both sides.
A right aligned paragraph uses the right edge of the text area as anchor and overflows to the left side.
|The expected behavior would look like this:|
|But LibreOffice (6.4) builds a text block same as in case left, center or right. The position of the text block depends on module and shape type:
Behavior of text in case of overflow is discussed in bug tdf#106503
Interoperability with OOXML
The horizontal position of shape text is determined by the attribute anchorCtr of the element <bodyPr>. Its datatype is boolean, with value 0 (= false) and 1 (= true).
The UI of PowerPoint provides the alignment options “Top”, “Middle”, “Bottom”, “Top Centered”, “Middle Centered” and “Bottom Centered” in drop-down list “Vertical alignment” of section “Text Box”. The first three options produce the attribute anchorCtr="0", the others three the attribute anchorCtr="1".
Rendering is similar to the ODF value justify in case of value 0, rendering is special in case of value 1. OOXML does not support a rendering like the values left, center or right of attribute draw:textarea‑horizontal‑align.
With the same texts and same shape I get these results in PowerPoint for the case anchorCtr="0":
|Text is smaller than the width of the text area||Text is wider than the width of the text area|
If all paragraphs are shorter than the text area width, the rendering is the same as with draw:textarea‑horizontal‑align="justify". In case the paragraphs are longer, the rendering looks like, what I have described as “expected” behavior above.
The rendering is the same in case anchorCtr="1" as in case anchorCtr="0", if the paragraphs are shorter than the text area width.
|If the paragraphs are longer, the rendering is this:|
|My explanation for rendering:
PowerPoint positions the paragraphs same as in case anchorCtr="0". Then it builds a bounding rectangle around the entire text (red). This rectangle is horizontally positioned so that its center is the same as the text area center.
The vertical position of the text is determined by the graphic style attribute draw:textarea‑vertical‑align with values justify, top, middle and bottom. That is the property TextVerticalAdjust with the values TOP, CENTER, BOTTOM, BLOCK of enum TextVerticalAdjust in API/core.
The UI for these settings is again the section “Text Anchor” in tab “Text” of the “Text”-dialog. The chosen option always combines a vertical adjustment with a horizontal adjustment. The examples below use horizontal alignment justify. All paragraphs are left aligned.
Top, Middle, Bottom
Vertical positioning works as expected. The bounding rectangle of the text is anchored to the top, middle or bottom of the text area. In case it is higher than the height of the text area, the text overflows down in case of anchor top, overflows to both sides in case of anchor middle, and overflows up in case of anchor bottom.
|small height||large height|
The value justify or BLOCK, respectively, is not fully implemented in LibreOffice. Only justify in writing direction is available.
Interoperability with OOXML
The vertical position of shape text is determined by the attribute anchor of the element <bodyPr> in OOXML. Its datatype is ST_TextAnchoringType, with values t, ctr, b, dist and just.
The UI of PowerPoint provides the alignment options “Top” and “Top Centered, “Middle” and “Middle Centered”, “Bottom” and “Bottom Centered” in drop-down list “Vertical alignment” of section “Text Box”. The “Top” options correspond to the attribute anchor="t", the “Middle” options to attribute anchor="ctr", and the “Bottom” options to the attribute anchor="b".
PowerPoint does not interpret the values dist and just. So there is currently no problem with the fact, that LibreOffice has not fully implemented the property value justify.
Text in Custom Shapes in Writer
The module Writer can use custom shapes the same way as modules Draw/Impress and Calc. But it has the additional feature to add a “Text Box” to a shape. It changes the internal treatment so, that the frame text engine is used instead of the draw text engine.
- The shape text can include complex content like images and Math equations. In case you want to produce valid ODF 1.2 strict file format, you need to anchor the objects “as character”.
- The shape text can use vertical writing modes. (Feature is not yet finished, see bug tdf#128120)
- Text in shape can use paragraph and character styles.
- The shape text can include tables. The ODF 1.2 strict file format does not allow tables as direct content in shapes, and the table will be lost, if you insert the table directly into the “Text Box”, see bug tdf#102256. You can enter a frame into the “Text Box” and anchor the frame as character. Then you can enter the table into the frame. This workaround is not needed, if the document is saved in ODF 1.2 extended file format.
- The shape is not transformed together with shape transformations like rotation and shearing.
- The added “Text Box” is not able to rotate.
- Text cannot overflow the “Text Box”.
There is no automatic transfer of text between “Text Box” and ordinary text area, when a “Text Box” is added or removed. You need to use copy&paste.
‘Padding’ is the region between the edge of the text area and the text content area. It is determined by the attributes fo:padding-foo of the element <style:graphic-properties> in ODF. Thereby ‘foo’ is one of left, top, right and bottom. So values can be set for each side independent from the others.
The UI for these attributes is the section “Spacing to Borders” on the left side of the tab “Text” in the “Text” dialog. LibreOffice allows entering negative values, but that results in invalid ODF.
The padding value, which can be seen in the left screenshot where applied to an arrow. The red dashed line indicates the text area inside the arrow. The red hatched area is the padding region. The blue rectangle indicates the actual region for text content.
The values are absolute, they do not use the internal coordinate system of the custom shape. So when the size of the shape is changed, the text area adapts to the new size, but not the padding values.
The corresponding properties in the API are TextLeftDistance, TextUpperDistance, TextRightDistance and TextLowerDistance of the service TextProperties. Values are in 1/100mm in the API.
This padding is called ‘Inset’ in the standard OOXML. The values are set using the attributes lIns, tIns, rIns and bIns of the element <bodyPr>. MS Office provides a UI for these values.
Rotation of Text
Rotating the Entire Text Area
ODF and LibreOffice. Text area rotation is determined by the attribute draw:text-rotate-angle of the element <draw:enhanced-geometry> in file format. The angle is in degree, the rotation is counter-clockwise for positive values. Rotation center is the center of the text area. The entire text area is rotated including the padding region. LibreOffice has no UI for this attribute, but it is usable via macro.
Rotation is determined by the property TextRotateAngle in service EnhancedCustomShapeGeometry in the API. Angles are in degree in the API too, that is different from shape rotation, where the unit is 1/100 degree in API.
Rotation is only available for ordinary text areas, not for the “Text Box” in custom shapes in Writer.
OOXML and MS Office. Text area rotation is determined by the attribute rot of the element bodyPr. MS Office has not UI for this feature. MS Word does not interpret this attribute, PowerPoint and Excel render it correctly.
Interoperability. LibreOffice reads the attribute rot of OOXML shapes, but implementation and rendering is buggy, for example bugs tdf#127457 and tdf#127437. LibreOffice does not export the ODF attribute draw:text-rotate-angle to the corresponding OOXML attribute rot.
The element <bodyPr> of a shape in OOXML can have the boolean attribute upright. If it is true, the rot attributes in the elements <bodyPr> (text area rotation) and <xfrm> (shape rotation) should not be applied to the text, so that the text is shown upright.
MS Word: This attribute exists as option “Do not rotate text” in the UI. The attribute upright prevents applying shape rotation to the text. Word does not interpret text area rotation at all.
Excel, PowerPoint: They have no corresponding option. The attribute upright only prevents applying shape rotation to the text, the text area rotation is still applied.
ODF has no corresponding property for custom shapes. LibreOffice ignores this attribute on import from OOXML.
Vertical Writing Mode
Writing mode is determined by the attribute style:writing-mode. ODF 1.2 allows the values lr-tb (short lr), rl-tb (short rl), tb-rl (short tb), tb-lr, or page. For next versions value bt-lr is in discussion. This attribute can be used with elements <style:graphic-properties>, <style:paragraph-properties>, <style:page-properties> and <style:section-properties>. Because an element <style:style> of family graphic, can have a child element <style:graphic-properties>, but can have a child element <style:paragraph-properties> too, setting the writing mode for the text in a shape is in theory possible by including the attribute
- in the <style:graphic-properties> of a <style:style> element of family graphic, which is assigned to the shape,
- in the <style:paragraph-properties> of a <style:style> element of family graphic, which is assigned to the shape,
- in the <style:paragraph-properties> of a <style:style> element of family paragraph, which is assigned to the paragraphs of the text of the shape.
The standard defines for padding only the writing mode independent attribute variants left, top, right and bottom for the attribute fo:padding. Therefore the padding region is not affected by changing the writing mode.
Writing mode for paragraphs, table cells and sections can be set by the element textDirection. That corresponds to setting writing mode in paragraph-properties in ODF.
OOXML has in addition an attribute vert of the element bodyPr. Possible values are horz, eaVert, mongolianVert, vert, ver270, wordArtVert, wordArtVertRtl. For a detailed description see section “184.108.40.206 ST_TextVerticalType (Vertical Text Types)” in the standard “ISO/IEC 29500-1:2016”..
The text rotations introduced by this attribute do only affect the text itself, but not the insets.
There exists no directly corresponding attribute for shapes in ODF.
LibreOffice tries to emulate the attribute vert. Main problems are correct treating of insets and handling of CJK characters. The treatment depends on the used text engine. Problems are discussed in the next section.
Problems with Attribute vert
The screenshots are taken from PowerPoint. The texts in the screenshots belong to the arrow. The green dashed rectangle indicates the text area. The padding was set to 0.5cm left and 1.5cm top.
|No known problems|
|Draw text engine, pptx: CJK characters are not shown upright. The file resaved as pptx shows the text as 180° rotation in PowerPoint. Opening the resaved pptx file in Libreoffice shape has lost padding. Saving the document in ODF generates rotated padding and so on reopening the text has wrong position.
Frame text engine, docx: The text is set to writing mode tb-rl, therefor the CJK characters are upright on opening. The text position is wrong because of rotated padding. Immediate saving to docx writes vert="vert" instead of vert="eaVert" so that CJK characters are not upright. And an attribute rot="5400000" is written in addition, which is only not visible in Word, because Word ignores the attribute rot. Saving to odt looses the writing-mode. Instead text-rotate-angle=−90 is written, which the frame text engine cannot render, when reopened.
Proposal for both engines: Use tb-rl writing mode in the paragraph-properties of the graphic style of the shape and do not rotate at all. Or even implement tb-rl writing mode in graphic-properties and use the existence of this attribute value to write the correct value eaVert for attribute vert on export to OOXML.
|Draw text engine, pptx: The text is rendered correctly in first opening. This case is handled the sames as case eaVert. So this time CJK characters are correct, because they are rotated. But all other errors are the same as in case eaVert. It is possible to use text-rotate-angle=−90 for ODF and adapt padding on the way from OOXML to ODF. But for newly created ODF documents it would not be clear, whether to use vert="vert" or attribute rot (text area rotation) on export to OOXML.
Proposal for Draw text engine: Use text-rotate-angle="-90" for ODF and adapt the padding on import from and export to OOXML. Assume that the original document has been an OOXML document in case the shape has a type “ooxml-foo“ and write vert="vert" instead of attribute rot in those cases for export to OOXML.
Frame text engine, docx: Case vert is handled the same as case eaVert. So this time CJK characters are wrong because they are not rotated. This time writing vert="vert" is correct, but the other errors are the same in case eaVert.
Proposal for Frame text engine: Use the frame text engine in this case on import only, if the text content makes it necessary.
|Draw text engine, pptx: The text is correctly rotated and has the correct position on first opening. Saving to ODF sets the correct text orientation, but has a wrong text position. Saving to pptx sets the correct text orientation, but has a wrong (different from ODF) text position. Opening the resaved pptx file in PowerPoint shows a text, which looks like the rendering of case horz rotated by 180°. The file has an additional attribute rot="16200000".
Frame text engine, docx: The latin text has the correct orientation. The CJK characters are upside-down. It looks, as if the rendering of case eaVert is rotated by 180°, but it should look like rotating the rendering of case horz by 270°. The position of the text is wrong, because of wrong padding. Saving to ODF looses the vertical orientation of the text. The resaved file looks correct in Word, but it has an additional attribute rot="16200000", which is not visible in Word, because Word ignores attribute rot.
|Draw text engine, pptx: Text is rendered same as in case vert and it has the same errors. An immediately resave to pptx does not generate the attribute vert="mongolianVert". The draw text engine is currently not able to use vertical writing-modes other than tb-rl and mongolian writing mode cannot be emulated by rotation.
Frame text engine, docx: Text is rendered same as in case eaVert and it has the same errors. An immediately resave to docx does not generate the attribute vert="mongolianVert".
Proposal for frame text engine: Implement style:writing-mode="tb-lr" for shape text. It works already, if you set the writing mode at style “Frame”, that is value “left-to-right (vertical)” in option ”Text direction” in the UI.
“wordArtVert” and “wordArtVertRtl”
|These vertical writing-modes are ignored in Impress and rendered same as case eaVert in Writer. Word renders them like case mongolianVert. PowerPoint renders them correctly.
ODF has the attribute style:direction="ttb" which would produce stacked text, but that is only usable with table-cell-properties and chart-properties.