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.
Text Area

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.
Horizontal Position
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.
left | center | right |
![]() |
![]() |
![]() |
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[1] 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
Justify
Left, Right, Center
And this is positioned left, centered or right into the text area.
Anchor left | ![]() |
Anchor center | ![]() |
Anchor right | ![]() |
Because of using the longest paragraph as reference, it makes no sense to enable “Word wrap”.
Internal Problems
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.
Anchor left Text overflows right. |
![]() |
Anchor center Text overflows to both sides. |
![]() |
Anchor right 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.
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.
Vertical Position
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 |
![]() |
![]() |
![]() |
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 | |
top | ![]() |
![]() |
middle | ![]() |
![]() |
bottom | ![]() |
![]() |
Internal Problems
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.
Gained Features
- 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.
Lost Features
- 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
‘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.
Upright
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
ODF
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.
OOXML
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 “20.1.10.83 ST_TextVerticalType (Vertical Text Types)” in the standard “ISO/IEC 29500-1:2016”.[2].
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.
Interoperability
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.
“horz”
No known problems | ![]() |