a:path (Geometry Path)
Specifies a single path within a custom geometry, composed of drawing commands.
Parent elements
Child elements
Description
\:path\ is the DrawingML element that defines a single geometric path within a custom shape. It is defined in ECMA-376 Part 1 §20.1.8.30.
Paths form the foundation of custom geometries by specifying:
- Outline: Boundary of the shape
- Drawing commands: Move, line, arc, bezier curve operations
- Fill regions: Enclosed areas that receive fill color
- Stroke rendering: Path outlines rendered with line properties
Paths enable:
- Vector graphics definition with precision coordinates
- Complex shapes via multiple connected paths
- Bezier curves for smooth, professional shapes
- Multiple fill regions (e.g., hole in shape via reverse-winding)
- Precise geometric control
Each path is self-contained and operates within the coordinate space defined by its \w\ (width) and \h\ (height) attributes.
Attributes
| Attribute | Type | Range | Description |
|---|---|---|---|
| \w\ | Integer | Positive | Path width in EMU (English Metric Units) |
| \h\ | Integer | Positive | Path height in EMU |
| \ill\ | String | “norm”, “evenOdd” | Fill rule: normal or even-odd winding |
| \stroke\ | Boolean | true/false | Whether to stroke the path outline |
| \extrusionOk\ | Boolean | true/false | Whether 3D extrusion is valid |
Content Model
\:path\ contains drawing commands:
- a:moveTo (0–1) — Initial starting point
- Drawing commands (0–∞) — Series of drawing operations:
- \:lnTo\ — Line to point
- \:arcTo\ — Arc to point
- \:quadBezTo\ — Quadratic Bezier curve
- \:cubicBezTo\ — Cubic Bezier curve
- a:close (0–1) — Close path to starting point
Drawing Commands Reference
Move To (a:moveTo)
Lifts pen and moves to starting point without drawing.
xml
<a:moveTo>
<a:pt x="0" y="0"/>
</a:moveTo>
| Property | Type | Purpose |
|---|---|---|
| \:pt\ | Point | Starting coordinate |
Line To (a:lnTo)
Draws straight line from current position to point.
xml
<a:lnTo>
<a:pt x="100000" y="50000"/>
</a:lnTo>
Multiple consecutive \:lnTo\ commands create polygonal shapes.
Arc To (a:arcTo)
Draws elliptical arc to point (complex; rarely used directly).
xml
<a:arcTo wR="100000" hR="100000" stAng="0" swAng="2700000"/>
| Attribute | Type | Purpose |
|---|---|---|
| \wR\ | Integer | Ellipse width radius |
| \hR\ | Integer | Ellipse height radius |
| \stAng\ | Integer | Start angle (1/60000°) |
| \swAng\ | Integer | Sweep angle (1/60000°) |
Quadratic Bezier (a:quadBezTo)
Draws smooth quadratic curve to point using one control point.
xml
<a:quadBezTo>
<a:pt x="50000" y="25000"/>
<a:pt x="100000" y="50000"/>
</a:quadBezTo>
Second \:pt\ is endpoint; first is control point.
Cubic Bezier (a:cubicBezTo)
Draws smooth cubic curve using two control points (most flexible).
xml
<a:cubicBezTo>
<a:pt x="33333" y="33333"/>
<a:pt x="66667" y="66667"/>
<a:pt x="100000" y="50000"/>
</a:cubicBezTo>
Points 1–2 are control points; point 3 is endpoint.
Close Path (a:close)
Closes path by drawing line back to start point.
xml
<a:close/>
Examples
Simple Rectangle Path
<a:path w="100000" h="100000" fill="norm" stroke="true">
<a:moveTo><a:pt x="0" y="0"/></a:moveTo>
<a:lnTo><a:pt x="100000" y="0"/></a:lnTo>
<a:lnTo><a:pt x="100000" y="100000"/></a:lnTo>
<a:lnTo><a:pt x="0" y="100000"/></a:lnTo>
<a:close/>
</a:path>
using DocumentFormat.OpenXml.Drawing;
var path = new Path
{
Width = 100000,
Height = 100000,
Fill = PathFillModeValues.Normal,
Stroke = true
};
var moveTo = new MoveTo();
moveTo.Append(new Point { X = 0, Y = 0 });
path.Append(moveTo);
var lnTo1 = new LineTo();
lnTo1.Append(new Point { X = 100000, Y = 0 });
path.Append(lnTo1);
var lnTo2 = new LineTo();
lnTo2.Append(new Point { X = 100000, Y = 100000 });
path.Append(lnTo2);
var lnTo3 = new LineTo();
lnTo3.Append(new Point { X = 0, Y = 100000 });
path.Append(lnTo3);
path.Append(new CloseShapePath());
Triangle Path
<a:path w="100000" h="100000" fill="norm" stroke="true">
<a:moveTo><a:pt x="50000" y="0"/></a:moveTo>
<a:lnTo><a:pt x="100000" y="100000"/></a:lnTo>
<a:lnTo><a:pt x="0" y="100000"/></a:lnTo>
<a:close/>
</a:path>
var path = new Path { Width = 100000, Height = 100000 };
var moveTo = new MoveTo();
moveTo.Append(new Point { X = 50000, Y = 0 });
path.Append(moveTo);
var lnTo1 = new LineTo();
lnTo1.Append(new Point { X = 100000, Y = 100000 });
path.Append(lnTo1);
var lnTo2 = new LineTo();
lnTo2.Append(new Point { X = 0, Y = 100000 });
path.Append(lnTo2);
path.Append(new CloseShapePath());
Diamond Path (4 points)
<a:path w="100000" h="100000">
<a:moveTo><a:pt x="50000" y="0"/></a:moveTo>
<a:lnTo><a:pt x="100000" y="50000"/></a:lnTo>
<a:lnTo><a:pt x="50000" y="100000"/></a:lnTo>
<a:lnTo><a:pt x="0" y="50000"/></a:lnTo>
<a:close/>
</a:path>
var path = new Path { Width = 100000, Height = 100000 };
// Center top point
var moveTo = new MoveTo();
moveTo.Append(new Point { X = 50000, Y = 0 });
path.Append(moveTo);
// Right, bottom, left points
path.Append(CreateLineTo(100000, 50000));
path.Append(CreateLineTo(50000, 100000));
path.Append(CreateLineTo(0, 50000));
path.Append(new CloseShapePath());
LineTo CreateLineTo(int x, int y)
{
var ln = new LineTo();
ln.Append(new Point { X = x, Y = y });
return ln;
}
Path with Bezier Curve (Smooth Shape)
<a:path w="100000" h="100000" fill="norm" stroke="true">
<a:moveTo><a:pt x="0" y="50000"/></a:moveTo>
<a:cubicBezTo>
<a:pt x="33333" y="0"/>
<a:pt x="66667" y="0"/>
<a:pt x="100000" y="50000"/>
</a:cubicBezTo>
<a:cubicBezTo>
<a:pt x="66667" y="100000"/>
<a:pt x="33333" y="100000"/>
<a:pt x="0" y="50000"/>
</a:cubicBezTo>
<a:close/>
</a:path>
var path = new Path { Width = 100000, Height = 100000 };
var moveTo = new MoveTo();
moveTo.Append(new Point { X = 0, Y = 50000 });
path.Append(moveTo);
// Top curve
var bezier1 = new CubicBezierCurveTo();
bezier1.Append(new Point { X = 33333, Y = 0 });
bezier1.Append(new Point { X = 66667, Y = 0 });
bezier1.Append(new Point { X = 100000, Y = 50000 });
path.Append(bezier1);
// Bottom curve
var bezier2 = new CubicBezierCurveTo();
bezier2.Append(new Point { X = 66667, Y = 100000 });
bezier2.Append(new Point { X = 33333, Y = 100000 });
bezier2.Append(new Point { X = 0, Y = 50000 });
path.Append(bezier2);
path.Append(new CloseShapePath());
Hexagon Path
<a:path w="100000" h="100000" fill="norm" stroke="true">
<a:moveTo><a:pt x="50000" y="0"/></a:moveTo>
<a:lnTo><a:pt x="100000" y="25000"/></a:lnTo>
<a:lnTo><a:pt x="100000" y="75000"/></a:lnTo>
<a:lnTo><a:pt x="50000" y="100000"/></a:lnTo>
<a:lnTo><a:pt x="0" y="75000"/></a:lnTo>
<a:lnTo><a:pt x="0" y="25000"/></a:lnTo>
<a:close/>
</a:path>
var path = new Path { Width = 100000, Height = 100000 };
var moveTo = new MoveTo();
moveTo.Append(new Point { X = 50000, Y = 0 });
path.Append(moveTo);
var points = new[]
{
(100000, 25000),
(100000, 75000),
(50000, 100000),
(0, 75000),
(0, 25000)
};
foreach (var (x, y) in points)
{
var lnTo = new LineTo();
lnTo.Append(new Point { X = x, Y = y });
path.Append(lnTo);
}
path.Append(new CloseShapePath());
Heart Shape with Curves
<a:path w="100000" h="100000" fill="norm" stroke="true">
<a:moveTo><a:pt x="50000" y="100000"/></a:moveTo>
<a:cubicBezTo>
<a:pt x="0" y="75000"/>
<a:pt x="0" y="50000"/>
<a:pt x="25000" y="50000"/>
</a:cubicBezTo>
<a:cubicBezTo>
<a:pt x="35000" y="40000"/>
<a:pt x="45000" y="30000"/>
<a:pt x="50000" y="10000"/>
</a:cubicBezTo>
<a:cubicBezTo>
<a:pt x="55000" y="30000"/>
<a:pt x="65000" y="40000"/>
<a:pt x="75000" y="50000"/>
</a:cubicBezTo>
<a:cubicBezTo>
<a:pt x="100000" y="50000"/>
<a:pt x="100000" y="75000"/>
<a:pt x="50000" y="100000"/>
</a:cubicBezTo>
<a:close/>
</a:path>
var path = new Path { Width = 100000, Height = 100000 };
var moveTo = new MoveTo();
moveTo.Append(new Point { X = 50000, Y = 100000 });
path.Append(moveTo);
// Add cubic bezier curves for heart lobes and point
var curves = new[]
{
((0, 75000), (0, 50000), (25000, 50000)),
((35000, 40000), (45000, 30000), (50000, 10000)),
((55000, 30000), (65000, 40000), (75000, 50000)),
((100000, 50000), (100000, 75000), (50000, 100000))
};
foreach (var (cp1, cp2, ep) in curves)
{
var bezier = new CubicBezierCurveTo();
bezier.Append(new Point { X = cp1.Item1, Y = cp1.Item2 });
bezier.Append(new Point { X = cp2.Item1, Y = cp2.Item2 });
bezier.Append(new Point { X = ep.Item1, Y = ep.Item2 });
path.Append(bezier);
}
path.Append(new CloseShapePath());
Coordinate System
Path coordinates use EMU (English Metric Units):
| Unit | Conversion | Notes |
|---|---|---|
| EMU | 914400 = 1 inch | Standard Office unit |
| Pixels | 9525 EMU = 1 px @ 96 DPI | Screen display |
| Point | 12700 EMU = 1 pt | Font measurement |
| Twip | 635 EMU = 1 twip | Word measurement |
Path bounds: (0, 0) to (w, h) — coordinates should stay within declared dimensions.
Fill Rules
| Rule | Behavior | Use Case |
|---|---|---|
| \ | ||
| orm\ (default) | Non-zero winding | Handles complex curves correctly |
| \evenOdd\ | Even-odd winding | Alternate fill for holes |
Winding order: Clockwise = positive area; counter-clockwise = negative area.
Stroke Rendering
Path outline rendering:
- Stroke = true: Outline drawn per \:ln\ (line) properties
- Stroke = false: No outline drawn, fill only
- Line properties: Applied from parent shape \:ln\ element
Path Composition
Multi-path shapes use multiple \:path\ elements:
xml
<a:pathLst>
<a:path w="100000" h="100000">
<!-- Outer boundary -->
</a:path>
<a:path w="100000" h="100000">
<!-- Inner hole (reverse winding) -->
</a:path>
</a:pathLst>
Example: Donut shape (outer circle + inner hole).
Common Patterns
Rounded Rectangle (approx with beziers)
xml
<a:path w="100000" h="100000">
<a:moveTo><a:pt x="20000" y="0"/></a:moveTo>
<a:lnTo><a:pt x="80000" y="0"/></a:lnTo>
<a:cubicBezTo>
<a:pt x="100000" y="0"/>
<a:pt x="100000" y="0"/>
<a:pt x="100000" y="20000"/>
</a:cubicBezTo>
<!-- Continue for other corners... -->
</a:path>
Concave Polygon
xml
<a:path w="100000" h="100000">
<a:moveTo><a:pt x="50000" y="0"/></a:moveTo>
<!-- Draw star points with inward-pointing lines -->
</a:path>
Notes
Path Characteristics
- Vector-based: Precise, scalable without pixelation
- Coordinate-based: Uses EMU coordinates
- Self-contained: Each path operates independently
- Fill/stroke: Separate rendering properties
- Composed: Multiple paths build complex shapes
Best Practices
- Keep paths simple for better performance
- Use bezier curves for smooth, professional appearance
- Test path rendering at different scales
- Document coordinate space expectations
- Group related paths logically
- Use guides for proportional scaling
Performance
- Simple paths: Fast rendering (lines, basic shapes)
- Complex curves: More processing (many bezier points)
- Many paths: Cumulative rendering impact
- Optimization: Simplify unnecessary path points
Compatibility
- Office 2007 and later
- All path commands consistently supported
- Complex paths may render differently in older versions
- PDF export preserves path rendering