a:custGeom (Custom Geometry)
Specifies a custom geometric shape defined by a series of paths and drawing instructions.
Description
\:custGeom\ is the DrawingML element that defines a fully custom geometric shape. It is defined in ECMA-376 Part 1 §20.1.8.18.
Custom geometries provide complete control over shape definition via:
- Guide values (\:gdLst) — Computed shape dimensions
- Adjustment handles (\:ahLst) — User-adjustable parameters
- Paths (\:pathLst) — Vector drawing commands
- Connection points (\:cxnSpLst) — Where connectors attach
Custom geometries enable:
- Arbitrary shape creation beyond preset library
- Complex, precise geometry definitions
- Shape customization via adjustment handles
- Professional infographic design
- Company-specific branded shapes
- Specialized diagram elements
Custom geometries differ from:
- Preset geometry (\:prstGeom) — Built-in shapes with preset IDs
- Path geometry (\:path) — Single path (custom geometry uses multiple paths)
Attributes
| Attribute | Type | Values | Description |
|---|---|---|---|
| (none) | - | - | \:custGeom\ has no attributes; all properties in child elements. |
Content Model
\:custGeom\ structure:
- a:avLst (optional, 0–1) — Adjustment value list for shape customization
- a:gdLst (optional, 0–1) — Guide list with computed values
- a:ahLst (optional, 0–1) — Adjustment handle list for UI manipulation
- a:cxnSpLst (optional, 0–1) — Connection point list
- a:pathLst (required, 1) — Path list defining shape outline
Child Elements Table
| Element | Occurrence | Description |
|---|---|---|
| \:avLst\ | 0–1 | Adjustment values for shape parameters |
| \:gdLst\ | 0–1 | Guide entries with formulas for computed dimensions |
| \:ahLst\ | 0–1 | Adjustment handles for interactive shape resizing |
| \:cxnSpLst\ | 0–1 | Connection points where connectors attach |
| \:pathLst\ | 1 | Paths that define shape outline/fill |
Examples
Simple Custom Triangle
<a:custGeom>
<a:avLst/>
<a:gdLst/>
<a:ahLst/>
<a:cxnSpLst/>
<a:pathLst>
<a:path w="100000" h="100000">
<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>
</a:pathLst>
</a:custGeom>
using DocumentFormat.OpenXml.Drawing;
var custGeom = new CustomGeometry();
custGeom.Append(new AdjustmentValueList());
custGeom.Append(new GuideList());
custGeom.Append(new AdjustmentHandleList());
custGeom.Append(new ConnectionShapeList());
var pathLst = new PathList();
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());
pathLst.Append(path);
custGeom.Append(pathLst);
Custom Shape with Adjustment Handles
<a:custGeom>
<a:avLst>
<a:gd name="adj" val="50000"/>
</a:avLst>
<a:gdLst>
<a:gd name="x1" fmla="*/ adj 100000 100000"/>
</a:gdLst>
<a:ahLst>
<a:ahXY n="adj" x="x1" y="50000"/>
</a:ahLst>
<a:cxnSpLst/>
<a:pathLst>
<a:path w="100000" h="100000">
<a:moveTo><a:pt x="0" y="50000"/></a:moveTo>
<a:lnTo><a:pt x="x1" y="0"/></a:lnTo>
<a:lnTo><a:pt x="100000" y="50000"/></a:lnTo>
<a:lnTo><a:pt x="x1" y="100000"/></a:lnTo>
<a:close/>
</a:path>
</a:pathLst>
</a:custGeom>
var custGeom = new CustomGeometry();
var avLst = new AdjustmentValueList();
var adjGd = new GuideEntry { Name = "adj", Formula = "50000" };
avLst.Append(adjGd);
custGeom.Append(avLst);
var gdLst = new GuideList();
var guideGd = new GuideEntry
{
Name = "x1",
Formula = "*/ adj 100000 100000"
};
gdLst.Append(guideGd);
custGeom.Append(gdLst);
var ahLst = new AdjustmentHandleList();
var ah = new AdjustmentHandle { PositionH = "x1", PositionV = "50000" };
ah.Append(new GuideRef { Name = "adj" });
ahLst.Append(ah);
custGeom.Append(ahLst);
custGeom.Append(new ConnectionShapeList());
var pathLst = new PathList();
var path = new Path { Width = 100000, Height = 100000 };
// Add path drawing commands...
pathLst.Append(path);
custGeom.Append(pathLst);
Custom Callout Shape
<a:custGeom>
<a:avLst/>
<a:gdLst/>
<a:ahLst/>
<a:cxnSpLst>
<a:cxnSp x="50000" y="100000"/>
<a:cxnSp x="0" y="50000"/>
</a:cxnSpLst>
<a:pathLst>
<a:path w="100000" h="100000">
<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="80000"/></a:lnTo>
<a:lnTo><a:pt x="50000" y="100000"/></a:lnTo>
<a:lnTo><a:pt x="100000" y="80000"/></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>
</a:pathLst>
</a:custGeom>
var custGeom = new CustomGeometry();
custGeom.Append(new AdjustmentValueList());
custGeom.Append(new GuideList());
custGeom.Append(new AdjustmentHandleList());
var cxnSpLst = new ConnectionShapeList();
var cxnSp1 = new ConnectionShape { X = 50000, Y = 100000 };
var cxnSp2 = new ConnectionShape { X = 0, Y = 50000 };
cxnSpLst.Append(cxnSp1);
cxnSpLst.Append(cxnSp2);
custGeom.Append(cxnSpLst);
var pathLst = new PathList();
var path = new Path { Width = 100000, Height = 100000 };
// Add callout shape path commands...
pathLst.Append(path);
custGeom.Append(pathLst);
Shape with Multiple Paths (Composite)
<a:custGeom>
<a:avLst/>
<a:gdLst/>
<a:ahLst/>
<a:cxnSpLst/>
<a:pathLst>
<a:path w="100000" h="100000">
<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>
<a:path w="100000" h="100000">
<a:moveTo><a:pt x="25000" y="25000"/></a:moveTo>
<a:lnTo><a:pt x="75000" y="25000"/></a:lnTo>
<a:lnTo><a:pt x="75000" y="75000"/></a:lnTo>
<a:lnTo><a:pt x="25000" y="75000"/></a:lnTo>
<a:close/>
</a:path>
</a:pathLst>
</a:custGeom>
var custGeom = new CustomGeometry();
custGeom.Append(new AdjustmentValueList());
custGeom.Append(new GuideList());
custGeom.Append(new AdjustmentHandleList());
custGeom.Append(new ConnectionShapeList());
var pathLst = new PathList();
// Outer rectangle
var path1 = new Path { Width = 100000, Height = 100000 };
// Add outer path commands...
pathLst.Append(path1);
// Inner rectangle (hole)
var path2 = new Path { Width = 100000, Height = 100000 };
// Add inner path commands...
pathLst.Append(path2);
custGeom.Append(pathLst);
Shape with Guide Formulas
<a:custGeom>
<a:avLst>
<a:gd name="w" val="100000"/>
<a:gd name="h" val="100000"/>
</a:avLst>
<a:gdLst>
<a:gd name="halfW" fmla="*/ w 1 2"/>
<a:gd name="halfH" fmla="*/ h 1 2"/>
</a:gdLst>
<a:ahLst/>
<a:cxnSpLst/>
<a:pathLst>
<a:path w="w" h="h">
<a:moveTo><a:pt x="halfW" y="0"/></a:moveTo>
<a:lnTo><a:pt x="w" y="halfH"/></a:lnTo>
<a:lnTo><a:pt x="halfW" y="h"/></a:lnTo>
<a:lnTo><a:pt x="0" y="halfH"/></a:lnTo>
<a:close/>
</a:path>
</a:pathLst>
</a:custGeom>
var custGeom = new CustomGeometry();
var avLst = new AdjustmentValueList();
avLst.Append(new GuideEntry { Name = "w", Formula = "100000" });
avLst.Append(new GuideEntry { Name = "h", Formula = "100000" });
custGeom.Append(avLst);
var gdLst = new GuideList();
gdLst.Append(new GuideEntry { Name = "halfW", Formula = "*/ w 1 2" });
gdLst.Append(new GuideEntry { Name = "halfH", Formula = "*/ h 1 2" });
custGeom.Append(gdLst);
custGeom.Append(new AdjustmentHandleList());
custGeom.Append(new ConnectionShapeList());
var pathLst = new PathList();
var path = new Path { Width = "w", Height = "h" };
// Add diamond path...
pathLst.Append(path);
custGeom.Append(pathLst);
Path Drawing Commands
Custom geometry paths use these commands:
| Command | Element | Purpose |
|---|---|---|
| Move To | \:moveTo\ | Set starting point (lift pen) |
| Line To | \:lnTo\ | Draw line to point |
| Arc To | \:arcTo\ | Draw arc to point |
| Quadratic Bezier | \:quadBezTo\ | Draw quadratic curve |
| Cubic Bezier | \:cubicBezTo\ | Draw cubic curve |
| Close | \:close\ | Close shape path |
Guide Formulas
Common guide formula patterns:
| Formula | Purpose | Example |
|---|---|---|
| */ a b c\ | Multiply and divide | */ 50000 w 100000\ (scale) |
| +- a b c\ | Add/subtract | +- w 10000 5000\ (offset) |
| +/ a b c\ | Add and divide | +/ w h 2\ (average) |
| \if a b c\ | Conditional | \if cond val1 val2\ |
| \bs a\ | Absolute value | \bs adj\ |
| \min a b\ | Minimum | \min w h\ |
| \max a b\ | Maximum | \max w h\ |
| \mod a b c\ | Modulo | \mod val divisor remainder\ |
Adjustment Handles
Handles allow user interaction:
| Handle Type | Purpose | Properties |
|---|---|---|
| \:ahXY\ | 2D point | x, y coordinates |
| \:ahPolar\ | Polar point | radius, angle |
Handles reference guide values via \:gd\ child elements.
Connection Points
Connection points (\:cxnSp) define where connectors attach:
xml
<a:cxnSpLst>
<a:cxnSp x="50000" y="0"/>
<a:cxnSp x="100000" y="50000"/>
<a:cxnSp x="50000" y="100000"/>
<a:cxnSp x="0" y="50000"/>
</a:cxnSpLst>
Typically placed at cardinal/ordinal points of shape boundary.
Common Patterns
Simple Diamond
xml
<a:pathLst>
<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>
</a:pathLst>
Hexagon with Center
xml
<a:pathLst>
<a:path w="100000" h="100000">
<!-- Outer hexagon path -->
<a:moveTo><a:pt x="50000" y="0"/></a:moveTo>
<!-- ... 6 points ... -->
</a:path>
<a:path w="100000" h="100000">
<!-- Center hole path -->
<a:moveTo><a:pt x="40000" y="40000"/></a:moveTo>
<!-- ... inner rectangle ... -->
</a:path>
</a:pathLst>
Notes
Element Characteristics
- Fully custom: Complete control over shape geometry
- Path-based: Defined via drawing commands (moveTo, lineTo, etc.)
- Parameterized: Adjustable via adjustment values and handles
- Guides: Support computed dimensions via formulas
- Connectable: Optional connection points for diagram connectors
Complexity vs Presets
- Preset: Simple ID, fast rendering, limited customization
- Custom: Full control, complex definition, slower rendering
Rendering
- Fill: Applied to enclosed area within paths
- Outline: Applied to path strokes
- Transformation: Applied to entire geometry
- Performance: More complex than preset geometry
Best Practices
- Use preset geometry when available (simpler, faster)
- Custom geometry for unique/branded shapes
- Test geometry rendering across Office versions
- Use guides for proportional scaling
- Document shape parameters and adjustments
- Consider performance impact with complex paths
- Provide adjustment handles for user customization
Compatibility
- Office 2007 and later
- All custom geometry commands consistently supported
- Complex geometries may render differently in older versions
- PDF export preserves custom geometry rendering
File Size
- Simple custom geometry: Similar to preset
- Complex multi-path geometry: Larger file size
- Guides and formulas: Minimal size impact
- Optimization: Remove redundant guides and paths