SVG 1.2 - 27 October 2004

Previous | Top | Next

4 Flowing text and graphics

SVG 1.2 enables a block of text and graphics to be rendered inside a shape while automatically wrapping the objects into lines using the flowRoot element. The idea is to mirror, as far as practical, the existing SVG text elements.

4.1 The flowRoot element

The flowRoot element specifies a block of graphics and text to be rendered with line wrapping. It contains at least one flowRegion element that defines regions into which the children elements of the flowRoot should be flowed.

flowRoot Schema

  <define name='flowRoot'>
    <element name='flowRoot'>
      <ref name='attlist.flowRoot'/>
      <ref name='SVG.flowRoot.content'/>
    </element>
  </define>

  <define name='attlist.flowRoot' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.Focusable.attrib'/>
    <ref name='SVG.flowalign.attlist'/>
  </define>

  <define name='SVG.flowRoot.content'>
    <zeroOrMore>
      <ref name='SVG.Description.class'/>
    </zeroOrMore>
    <ref name='SVG.flowRoot.class'/>
  </define>

  <define name='SVG.flowRoot.class'>
    <ref name='flowRegion'/>
    <zeroOrMore>
      <ref name='SVG.moreFlowRegions.class'/>
    </zeroOrMore>
    <oneOrMore>
      <ref name='flowPara'/>
    </oneOrMore>
  </define>

  <define name='SVG.flowRoot.class' combine='interleave'>
    <interleave>
      <ref name='flowRegionExclude'/>
      <zeroOrMore>
        <ref name='flowDiv'/>
      </zeroOrMore>
    </interleave>
  </define>

  <define name='SVG.moreFlowRegions.class' combine='interleave'>
    <ref name='flowRegion'/>
  </define>

  <define name='SVG.flowalign.attlist' combine='interleave'>
    <optional>
      <attribute name='text-align' svg:animatable='true' svg:inheritable='true'/>
      <attribute name='display-align' svg:animatable='true' svg:inheritable='true'/>
    </optional>
  </define>


4.2 The flowRegion element

The flowRegion element contains a set of shapes and exclusion regions in which the text content of a parent flowRoot element is drawn into. A flowRegion element has basic shapes and path elements as children, as well as a flowRegionExclude element. The children of a flowRegion element are inserted into the rendering tree before the text is drawn and have the same rendering behavior as if they were children of a g element.

The child elements create a sequence of shapes in which the text content for the parent flowRoot will be drawn. Once the text fills a shape it flows into the next shape. The flowRegionExclude child describes a set of regions into which text will not be drawn, such as a cutout from a rectangular block of text.

The child elements of a flowRegion can be transformed as usual but the text is always laid out in the coordinate system of the flowRoot element. For example, a rect child with a 45 degree rotation transformation will appear as a diamond but the text will be aligned along the regular axis.

flowRegion Schema

  <define name='flowRegion'>
    <element name='flowRegion'>
      <ref name='attlist.flowRegion'/>
      <ref name='SVG.flowRegion.content'/>
    </element>
  </define>

  <define name='attlist.flowRegion' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
  </define>

  <define name='SVG.flowRegion.content'>
    <ref name='SVG.flowRegion.class'/>
  </define>

  <define name='SVG.flowRegion.class'>
    <ref name='rect'/>
  </define>

  <define name='SVG.flowRegion.class' combine='choice'>
    <choice>
      <ref name='g'/>
      <ref name='use'/>
      <ref name='text'/>
      <ref name='SVG.Shape.class'/>
    </choice>
  </define>

4.3 The flowRegionExclude element

The flowRegionExclude element contains a set of shapes defining regions in which flowed text is not drawn. It can be used to create exclusion regions from within a region of text.

If flowRegionExclude is a child of a flowRegion then it describes an exclusion region for that particular flowRegion. If it is a child of flowRoot then it describes exclusion regions for all flowRegion children of the flowRoot.

flowRegionExclude Schema

  <define name='flowRegionExclude'>
    <element name='flowRegionExclude'>
      <ref name='attlist.flowRegion'/>
      <ref name='SVG.flowRegion.content'/>
    </element>
  </define>

4.4 The flowDiv element

The flowDiv element specifies a block of text and/or graphics to be inserted into the layout, and marks it as a division of related elements. The children of the flowDiv element will be rendered as a block and offset from their parent's siblings both before and after. By separating the logical order of text (in successive flowDiv elements) from the physical layout (in regions, which can be presented anywhere on the canvas) the SVG document structure encourages creation of a default, meaningful linear reading order while preserving artistic freedom for layout. This enhances accessibility.

flowDiv Schema

  <define name='flowDiv'>
    <element name='flowDiv'>
      <ref name='attlist.flowDiv'/>
      <ref name='SVG.flowDiv.content'/>
    </element>
  </define>

  <define name='attlist.flowDiv' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Style.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.GraphicalEvents.attrib'/>
  </define>

  <define name='SVG.flowDiv.content'>
    <zeroOrMore>
      <ref name='SVG.Description.class'/>
      <ref name='flowPara'/>
      <ref name='flowRegionBreak'/>
    </zeroOrMore>
  </define>

4.5 The flowPara element

The flowPara element marks a block of text and graphics as a logical paragraph.

flowPara Schema

  <define name='flowPara'>
    <element name='flowPara'>
      <ref name='attlist.flowPara'/>
      <ref name='SVG.flowPara.content'/>
    </element>
  </define>

  <define name='attlist.flowPara' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.Focusable.attrib'/>
    <ref name='SVG.Editable.attrib'/>
    <ref name='SVG.flowalign.attlist'/>
  </define>

  <define name='SVG.flowPara.content'>
    <zeroOrMore>
      <ref name='SVG.flowPara.class'/>
    </zeroOrMore>
  </define>

  <define name='SVG.flowPara.class'>
    <choice>
      <ref name='flowSpan'/>
      <text/>
    </choice>
  </define>

4.6 The flowSpan element

The flowSpan element specifies a block of text to be rendered inline, and marks the text as a related span of words. The flowSpan element is typically used to allow a subset of the text block, of which it is a child, to be rendered in a different style or to mark it as being in a different language.

flowSpan Schema

  <define name='flowSpan'>
    <element name='flowSpan'>
      <ref name='attlist.flowSpan'/>
      <ref name='SVG.flowSpan.content'/>
    </element>
  </define>

  <define name='attlist.flowSpan' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.Focusable.attrib'/>
  </define>

  <define name='SVG.flowSpan.content'>
    <zeroOrMore>
      <ref name='SVG.flowSpan.class'/>
    </zeroOrMore>
  </define>

  <define name='SVG.flowSpan.class'>
    <choice>
      <ref name='flowSpan'/>
      <text/>
    </choice>
  </define>

4.7 The flowRegionBreak element

When the flowRegionBreak element is inserted into the text stream it causes the text to stop flowing into the current region at that point. The text after the flowRegionBreak element begins in the next region. If there are no more regions into which text could flow, then the text will stop being rendered at the point of the flowRegionBreak.

flowRegionBreak Schema

  <define name='flowRegionBreak'>
    <element name='flowRegionBreak'>
      <ref name='attlist.flowRegionBreak'/>
      <ref name='SVG.flowRegionBreak.content'/>
    </element>
  </define>

  <define name='attlist.flowRegionBreak' combine='interleave'>
    <empty/>
  </define>

  <define name='SVG.flowRegionBreak.content'>
    <empty/>
  </define>

4.8 The flowLine element

The flowLine element is used to force a line break in the text flow. The content following the end of a flowLine element will be placed on the next available strip in the flowRegion that does not already contain text. This happens even if the flowLine element has no children.

If there are no printable characters between adjacent flowLine elements then only the first flowLine element is rendered.

In all other aspects, the flowLine element is functionally equivalent to the flowSpan element.

flowLine Schema

  <define name='flowLine'>
    <element name='flowLine'>
      <ref name='attlist.flowLine'/>
      <ref name='SVG.flowLine.content'/>
    </element>
  </define>

  <define name='attlist.flowLine' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Style.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.GraphicalEvents.attrib'/>
  </define>

  <define name='SVG.flowLine.content'>
    <zeroOrMore>
      <ref name='flowSpan'/>
      <ref name='flowImage'/>
      <ref name='flowRegionBreak'/>
    </zeroOrMore>
  </define>

4.9 The flowTref element

The flowTref element is used to insert the child text content of a referenced element. It's effect is analogous to the tref element.

flowTref Schema

  <define name='flowTref'>
    <element name='flowTref'>
      <ref name='attlist.flowTref'/>
      <ref name='SVG.flowTref.content'/>
    </element>
  </define>

  <define name='attlist.flowTref' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Style.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.GraphicalEvents.attrib'/>
    <ref name='SVG.XLinkRequired.attrib'/>
  </define>

  <define name='SVG.flowTref.content'>
    <empty/>
  </define>

4.10 The flowImage element

The flowImage element defines a container for graphics which are to be rendered inline in the text layout. It can be used to insert images or any other graphic object that will flow inline with the text flows.

The flowImage element establishes a new viewport for contained graphic elements. If flowImage specifies an absolute size, that size is used as the bounding rectangle for the flowImage region. If flowImage specifies a percentage as its size, the percentage is represented as a percentage of the current viewport.

In the absence of either width or height on the flowImage element, no new viewport is established. Any contained graphic elements are sized relative to the current viewport. In that case, the bounds of the flowImage element are calculated from the bounding box of any contained child graphic elements.

flowImage Schema

  <define name='flowImage'>
    <element name='flowImage'>
      <ref name='attlist.flowImage'/>
      <ref name='SVG.flowImage.content'/>
    </element>
  </define>

  <define name='attlist.flowImage' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Style.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.GraphicalEvents.attrib'/>
  </define>

  <define name='SVG.flowImage.content'>
    <zeroOrMore>
      <ref name='g'/>
      <ref name='use'/>
      <ref name='text'/>
      <ref name='image'/>
      <ref name='video'/>
      <ref name='flowRoot'/>
      <ref name='flowRef'/>
      <ref name='SVG.Shape.class'/>
      <ref name='SVG.Text.class'/>
    </zeroOrMore>
  </define>

4.11 The flowRef element

The flowRef element references a flowRegion element. It causes the referenced element's geometry to be drawn in the current user coordinate system along with the text that was flowed into the region.

flowRef Schema

  <define name='flowRef'>
    <element name='flowRef'>
      <ref name='attlist.flowRef'/>
      <ref name='SVG.flowRef.content'/>
    </element>
  </define>

  <define name='attlist.flowRef' combine='interleave'>
    <ref name='SVG.Core.attrib'/>
    <ref name='SVG.Style.attrib'/>
    <ref name='SVG.Presentation.attrib'/>
    <ref name='SVG.GraphicalEvents.attrib'/>
    <ref name='SVG.XLinkRequired.attrib'/>
  </define>

  <define name='SVG.flowRef.content'>
    <zeroOrMore>
      <ref name='SVG.Description.class'/>
    </zeroOrMore>
  </define>

4.12 Text Flow

Text flow is defined as a post processing step to the standard text layout model of SVG. At a high level the steps for flowing text are as follows:

  1. The text is then processed in logical order to determine line breaking opportunities between characters, according to Unicode Standard Annex No. 14
  2. Text layout is performed as normal, on one infinitely long line, soft hyphens are included in the line. The result is a set of positioned Glyphs.
  3. Glyphs represent a character or characters within a word. Each glyph is associated with the word that contains its respective characters. In cases where characters from multiple words contribute to the same glyph the words are merged and all the glyphs are treated as part of the earliest word in logical order.
  4. The glyphs from a word are collapsed into Glyph Groups. A Glyph Group is comprised of all consecutive glyphs from the same word. In most cases each word generates one glyph group however in some cases the interaction between BIDI and special markup may cause glyphs from one word to have glyphs from other words embedded in it.
  5. Each Glyph Group has two extents calculated: it's normal extent, and it's last in text region extent. It's normal extent is the sum of the advances of all glyphs in the group except soft hyphens. The normal extent is the extent used when a Glyph Group from a later word is in the same text region. The last in text region extent includes the advance of a trailing soft hyphens but does not include the advance of trailing whitespace or combining marks (ABC width?). The last in text region extent is used when this glyph group is from the last word (in logical order) in this text region.
  6. The location of the first strip is determined based on the first word in logical order (see Calculating Text Regions and determining strip location).
  7. Words are added to the current Strip in logical order. All the Glyph Groups from a word must be in the same strip and all the glyphs from a Glyph Group must be in the same Text Region.

    When a word is added the line height may increase, it can never decrease from the first word. An increase in the line height can only reduce the space available for text placement in the span.

    The span will have the maximum possible number of words.

  8. The Glyphs from the Glyph Groups are then collapsed into the text regions by placing the first selected glyph (in display order) at the start of the text region and each subsequent glyph at the location of the glyph following the preceding selected glyph (in display order).
  9. The next word is selected and the next strip location is determined. Goto Step 7.

4.13 Determining Strip Location

To determine the placement of a strip the Glyph Groups from first word is used. The initial position for the strip is calculated, taking into account the end (in non text progression direction) of the previous strip and the appropriate margin properties.

The line-box is calculated using the initial position as the top/right edge of the line-box, and the line-height of the first word. The 'bottom/right' edge of the line-box must be checked against the margin properties, if it lies within the margin then processing moves to the next flow region.

Once the line-box is calculated the Strip and it's associated Text Regions are calculated (see: Calculating Text Regions). If the first word can be placed in the text regions of this Strip then this location is used for the next line of text. If the first word does not fit then the top/right edge is shifted by 'line-advance' and the new line-box is checked. This proceeds until the word fits or end of the flow region is reached at which point processing moves to the next flow region.

4.13.1 Calculating Text Regions

In order to flow text into arbitrary regions it is necessary to calculate what areas of the arbitrary region are available for text placement.

Firstly, the flow region geometry is intersected with the current line-box. The result of this intersection is referred to as the strip. The strip is then split into text regions where ever a piece of geometry from the flow region 'intrudes'. It is important to ignore edges & points that are co-incident with the top or bottom of the line-box.

The diagram below shows the text strips used on a given shape.

image describing the location of text strips

The following is a more detailed description of the algorithm:

The current flow region and any applicable exclude regions must be combined into one piece of geometry, simply concatenating the geometry is sufficient as this entire algorithm deals simply with segments of the paths and does not use directionality information until the inclusion tests at the end. The result of the concatenation of the geometry is referred to as the flow geometry.

Next the line-box is calculated, from the top/right edge of the line, the line-height and the bounding box of the flow region. This line-box is intersection with the flow geometry, clipping the flow geometry segments to the line box.

The bounding box is then calculated separately for each of the segments in the intersection.

The left and right (top and bottom respectively for vertical text) edges of the bounding boxes are sorted in increasing coordinate order (x for horizontal text, y for vertical text), for edges at the same location the left/top (or opening) edge is considered less than right/bottom (or closing) edges. The following pseudo code then generates the list of open areas for the current line:

        Edge [] segs = ...; // The sorted list of edges.

        Edge edge = segs[0];
        int count = 1;
        double start = 0;
        for (i=1; i<segs.length; i++) {
            edge = segs[i];
            if (edge.open) { 
              // 'open' is true, this is the start of a block out region.
                if (count == 0) {
                    // End of an open region so record it.
                    rgns.add(new TextRegion(start, edge.loc));
                }
                count++;
            } else {
              // 'open' is false, this edge is the end of a block out region.
                count--;
                if (count == 0) {
                    // start of an open area remember it.
                    start = edge.loc;
                }
            }
        } 

This gives the regions of the strip that are unobstructed by any flow geometry (from either exclusion or flow regions), however those regions may be outside the flow region (such as in a hole, such as the middle of an 'O'), or inside an exclusion region. Thus the center of each rectangle should be checked first to see if it lies inside any exclusion region if so the rectangle is removed from the list. Second it must be checked for inclusion in the flow region, if it is inside the flow region then the rectangle is available for text placement and becomes a text region for the current strip.

Once all the text regions for a strip are located left and right Margins for horizontal text (top and bottom margins for vertical) as well as indent are applied. Margins are applied to each text region. For the first span in a paragraph (flowPara for flowRegionBreak) the indent is added to the appropriate margin of the first text region. For left to right text this is the left margin of the left most text region, for right to left text this is the right margin of the right most text region, and for vertical text is the top margin of the top most text region.

If the left/right (top/bottom) edges of a text region pass each other due to the application of margins (or indent) the text region is removed from the list. If the text region removed had indent applied the indent is not applied to the next text region in text progression direction it is simply ignored.

Flowing text using system fonts is a difficult operation. Content developers should not expect reproducible results between implementations. The most likely scenario for a reproducible result, although still not completely guaranteed, will be achieved by using SVG Fonts.

4.14 Alignment

4.14.1 The text-align property

Alignment in the inline progression direction in flowing text is provided by the text-align property. It is a modified version of the CSS3 property.

text-align
Value: start | end | center | justify
Initial: start
Applies to: flowText, flowPara, flowDiv elements
Inherited: yes
Percentages: N/A
Media: visual
Animatable: yes

For details refer to the CSS3 Text Module. Note that SVG does not allow the value "string" for this property, and that the values "left" and "right" have been removed as they do not make sense in an internationalized context.

The values "start" and "end" are dependent on the writing system being used.

4.14.2 The progression-align property

Alignment in the direction of line progression for flowing text is provided by the progression-align property.

progression-align
Value: before | after | center
Initial: before
Applies to: flowRoot, flowPara, flowDiv elements
Inherited: yes
Percentages: N/A
Media: visual
Animatable: yes

The progression-align property causes blocks of flowed text to align within their containing regions. If the line progression direction is left to right, a setting of "before" will align the block of text at the top of the region, a setting of "after" will align the text at the bottom of the region, and a setting of "center" will vertically center the block of flowed text. The combined line heights of the entire flowed text is used for alignment (as opposed to maximum glyph extents on the flowed text lines).

4.14.3 Overflow

When the last element within a flow cannot be placed within the specified flow regions due to lack of space, then the flowRegion element is in an "overflow" state. The opposite state, the "underflow" state, is when the last element within a given flow can be placed within the given flow region.

Whenever a flow region changes from underflow state to overflow state, then the {"http://www.w3.org/2000/svg", "overflow"} event is fired. Whenever a flow region changes from overflow state to underflow state, then the {"http://www.w3.org/2000/svg", "underflow"} event is fired. These events are only fired with state changes. If a flow region is already in the overflow state and new content is appended to the end of the flow, then the state has not changed and therefore the {"http://www.w3.org/2000/svg", "overflow"} must not be fired.

The following is the definition of interface SVGOverflowEvent which is the event interface corresponding to the "overflow" and "underflow" events:

interface SVGOverflowEvent : events::Event {                                    
  readonly attribute SVGFlowParaElement flowPara;                                 
}                                                                               

The currentTarget for the event is the flowRegion element whose overflow state has changed to underflow state, or vice versa.

4.15 Example

Below is an example of the flowing text capabilities:

<svg xmlns:svg="http://www.w3.org/2000/svg" version="1.2"
     xmlns:xlink="http://www.w3.org/1999/xlink" 
  width="100%" height="100%" viewBox="0 0 300 310">
  <title>Basic textflow</title>
  <rect x="0" y="0" width="100%" height="100%" fill="yellow"/>
  <flowRoot font-size="16">
    <flowRegion>
      <path d="M100,50L50,300L250,300L300,50z"/>
    </flowRegion>
      <flowPara>Tomorrow, and tomorrow, and tomorrow; creeps in this 
       petty pace from day to day, until the last syllable of recorded time. 
       And all our yesterdays have lighted fools the way to dusty death.
     </flowPara>
  </flow>
  <path d="M90,40L40,270L260,270L210,40z" fill="none" stroke="black" stroke-width="5"/>
</svg>

Image showing simple text wrapping

View this image as SVG (SVG 1.2 enabled browsers only)

A more complicated example is shown below. It is not included inline. Please see the SVG file for the source.

Image showing extended text wrapping

View this image as SVG (SVG 1.2 enabled browsers only)

4.16 DOM Interfaces

The DOM Interfaces to the flow elements are straightforward. There are no extra attributes or methods, beyond those inherited from other interfaces.

All elements that have textual content derive from the SVGTextContentElement interface. This is the same interface used by the regular, non-flowing, text elements.

interface SVGFlowRootElement : SVGElement, SVGTests, SVGLangSpace,
    SVGExternalResourcesRequired, SVGStylable, events:EventTarget {};
           
interface SVGFlowRegionElement : SVGElement {};

interface SVGFlowRegionsExcludeElement : SVGElement {};

interface SVGFlowDivElement : SVGTextContentElement {};

interface SVGFlowParaElement : SVGTextContentElement {};

interface SVGFlowSpanElement : SVGTextContentElement {};

interface SVGFlowRegionBreakElement : SVGElement {};

interface SVGFlowLineElement : SVGTextContentElement {};

interface SVGFlowTRefElement : SVGTextContentElement,
    SVGURIReferenceElement {};
                           
interface SVGFlowRefElement : SVGElement, SVGURIReferenceElement {};

interface SVGFlowImageElement : SVGElement, SVGExternalResourcesRequired,
             SVGStylable, events:EventTarget {};