SVG 1.2 - 27 October 2004
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.
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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:
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.
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.
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.
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.
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.
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.
Alignment in the direction of line progression for flowing text is provided by the progression-align property.
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).
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.
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>
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.
View this image as SVG (SVG 1.2 enabled browsers only)
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 {};