Customize Presentation Shapes in C++
Overview
This article explains how to customize presentation shapes in Aspose.Slides by editing shape geometry through edit points and geometry paths. It shows how to work with GeometryPath and IGeometryPath to modify existing shapes, perform basic path editing operations, add or remove points, and apply updated geometry back to a shape.
It also demonstrates how to create custom and composite shapes, build shapes with curved corners, and determine whether a shape geometry is closed.
Change a Shape Using Edit Points
In PowerPoint, you can use Edit Points to modify a square:
- Move a corner inward or outward.
- Adjust the curvature at a point.
- Add new points.
Editing points lets you modify a shape’s outline or use an existing shape as the basis for a new one.
Shape Editing Tips

When editing PowerPoint shapes, keep these concepts in mind:
- A shape (or its path) can either be closed or open.
- A shape’s path consists of anchor points connected by segments.
- A segment can be straight or curved. Anchor points and control handles determine its shape.
- Anchor points exist as corner points, straight points, or smooth points:
- At a corner point, adjoining segments can meet at an angle.
- At a smooth point, the two control handles are aligned and equally distant from the anchor point.
- At a straight point, the two control handles are aligned but can be different distances from the anchor point.
- Moving anchor points or adjusting their handles changes the shape’s outline.
To edit PowerPoint shapes through edit points, Aspose.Slides provides the GeometryPath class and IGeometryPath interface.
- A GeometryPath instance represents a geometry path of the IGeometryShape object.
- To retrieve the GeometryPath from the IGeometryShape instance, you can use the IGeometryShape::GetGeometryPaths method.
- To set the GeometryPath for a shape, you can use these methods: IGeometryShape::SetGeometryPath for shapes with a single path and IGeometryShape::SetGeometryPaths for shapes with multiple paths.
- To add segments, you can use the methods under IGeometryPath.
- Using the IGeometryPath::set_Stroke and IGeometryPath::set_FillMode methods, you can set the appearance for a geometry path.
- Using the IGeometryPath::get_PathData method, you can retrieve the geometry path of a GeometryShape as an array of path segments.
Simple Editing Operations
Use the following methods to edit a geometry path. Overloads that accept an index insert a segment at that zero-based position in the path’s segment array.
| Operation | Method |
|---|---|
| Append or insert a straight line. | GeometryPath::LineTo |
| Append or insert a cubic Bézier curve with two control points and an endpoint. | GeometryPath::CubicBezierTo |
| Append or insert a quadratic Bézier curve with one control point and an endpoint. | GeometryPath::QuadraticBezierTo |
| Append an elliptical arc with the specified dimensions and angles in degrees. | GeometryPath::ArcTo |
| Close the current figure by connecting its endpoint to its starting point. | GeometryPath::CloseFigure |
| Set the starting point of a figure. | GeometryPath::MoveTo |
| Remove a segment at a zero-based index. | GeometryPath::RemoveAt |
Add Custom Points to a Shape
This example inserts two points into a rectangular shape using GeometryPath::LineTo and applies the edited path with IGeometryShape::SetGeometryPath. The coordinates are local to the shape: both new points are at its center, 100 points from the left and 50 points from the top. Segment indices 1 and 4 place them on the top and bottom edges, pulling those edges inward.
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <system/smart_ptr.h>
using namespace Aspose::Slides;
using namespace System;
auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);
auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100.0f, 100.0f, 200.0f, 100.0f);
auto geometryPath = shape->GetGeometryPaths()->idx_get(0);
geometryPath->LineTo(100.0f, 50.0f, 1);
geometryPath->LineTo(100.0f, 50.0f, 4);
shape->SetGeometryPath(geometryPath);

Remove Points from a Shape
This example changes a heart-shaped outline by removing the segment at zero-based index 2 with GeometryPath::RemoveAt. Applying the modified path with IGeometryShape::SetGeometryPath updates the shape’s contour.
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <system/smart_ptr.h>
using namespace Aspose::Slides;
using namespace System;
auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);
auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Heart, 100.0f, 100.0f, 300.0f, 300.0f);
auto path = shape->GetGeometryPaths()->idx_get(0);
path->RemoveAt(2);
shape->SetGeometryPath(path);

Create a Custom Shape
This example builds a five-pointed star with GeometryPath. Alternating outer and inner radii of 100 and 50 points define the tips and indentations; a 72-degree step spaces the five tips evenly. The path is closed with GeometryPath::CloseFigure and applied to a rectangular shape using IGeometryShape::SetGeometryPath, replacing its outline.
#include <DOM/GeometryPath.h>
#include <DOM/IAutoShape.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <drawing/point_f.h>
#include <system/collections/list.h>
#include <system/math.h>
#include <system/smart_ptr.h>
using namespace Aspose::Slides;
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Drawing;
auto points = System::MakeObject<List<PointF>>();
auto outerRadius = 100.0f;
auto innerRadius = 50.0f;
auto angleStep = 72;
for (auto angle = -90; angle < 270; angle += angleStep)
{
auto radians = angle * (Math::PI / 180.f);
auto x = outerRadius * Math::Cos(radians);
auto y = outerRadius * Math::Sin(radians);
points->Add(PointF((float)x + outerRadius, (float)y + outerRadius));
radians = Math::PI * (angle + angleStep / 2) / 180.0;
x = innerRadius * Math::Cos(radians);
y = innerRadius * Math::Sin(radians);
points->Add(PointF((float)x + outerRadius, (float)y + outerRadius));
}
auto starPath = System::MakeObject<GeometryPath>();
starPath->MoveTo(points->idx_get(0));
for (auto i = 1; i < points->get_Count(); i++)
{
starPath->LineTo(points->idx_get(i));
}
starPath->CloseFigure();
auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);
auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100.0f, 100.0f, outerRadius * 2, outerRadius * 2);
shape->SetGeometryPath(starPath);

Create a Composite Custom Shape
This example defines two closed GeometryPath objects for the top and bottom thirds of a shape. Applying both paths with IGeometryShape::SetGeometryPaths creates a single composite shape with a gap across its middle third.
#include <DOM/GeometryPath.h>
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <system/smart_ptr.h>
using namespace Aspose::Slides;
using namespace System;
auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);
auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100.0f, 100.0f, 200.0f, 100.0f);
auto topPath = System::MakeObject<GeometryPath>();
topPath->MoveTo(0.0f, 0.0f);
topPath->LineTo(shape->get_Width(), 0.0f);
topPath->LineTo(shape->get_Width(), shape->get_Height() / 3);
topPath->LineTo(0.0f, shape->get_Height() / 3);
topPath->CloseFigure();
auto bottomPath = System::MakeObject<GeometryPath>();
bottomPath->MoveTo(0.0f, shape->get_Height() / 3 * 2);
bottomPath->LineTo(shape->get_Width(), shape->get_Height() / 3 * 2);
bottomPath->LineTo(shape->get_Width(), shape->get_Height());
bottomPath->LineTo(0.0f, shape->get_Height());
bottomPath->CloseFigure();
auto paths = System::MakeArray<SharedPtr<IGeometryPath>>({ topPath, bottomPath });
shape->SetGeometryPaths(paths);

Create a Custom Shape with Curved Corners
This example combines straight segments with GeometryPath::ArcTo to create a shape with inward-curved corners. Each corner size sets the arc’s width and height in points, and the -90-degree sweep draws a counterclockwise quarter ellipse. The completed path is applied with IGeometryShape::SetGeometryPath, and the presentation is saved in PPTX format.
#include <DOM/GeometryPath.h>
#include <DOM/IAutoShape.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <Export/SaveFormat.h>
#include <drawing/point_f.h>
using namespace Aspose::Slides;
using namespace Aspose::Slides::Export;
using namespace System::Drawing;
auto shapeX = 20.f;
auto shapeY = 20.f;
auto shapeWidth = 300.f;
auto shapeHeight = 200.f;
auto leftTopSize = 50.f;
auto rightTopSize = 20.f;
auto rightBottomSize = 40.f;
auto leftBottomSize = 10.f;
auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);
auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Custom, shapeX, shapeY, shapeWidth, shapeHeight);
auto topLeft = PointF(leftTopSize, 0.0f);
auto topRight = PointF(shapeWidth - rightTopSize, 0.0f);
auto rightBottom = PointF(shapeWidth, shapeHeight - rightBottomSize);
auto bottomLeft = PointF(leftBottomSize, shapeHeight);
auto leftTop = PointF(0.0f, leftTopSize);
auto geometryPath = System::MakeObject<GeometryPath>();
geometryPath->MoveTo(topLeft);
geometryPath->LineTo(topRight);
geometryPath->ArcTo(rightTopSize, rightTopSize, 180.0f, -90.0f);
geometryPath->LineTo(rightBottom);
geometryPath->ArcTo(rightBottomSize, rightBottomSize, -90.0f, -90.0f);
geometryPath->LineTo(bottomLeft);
geometryPath->ArcTo(leftBottomSize, leftBottomSize, 0.0f, -90.0f);
geometryPath->LineTo(leftTop);
geometryPath->ArcTo(leftTopSize, leftTopSize, 90.0f, -90.0f);
geometryPath->CloseFigure();
shape->SetGeometryPath(geometryPath);
presentation->Save(u"output.pptx", SaveFormat::Pptx);
Find Out If a Shape Geometry Is Closed
The following helper checks whether every nonempty geometry path ends with a closing segment. It reads IGeometryPath::get_PathData and compares the final segment’s IPathSegment::get_PathCommand with PathCommandType::Close. It returns false when there are no nonempty paths. This checks the final command of each path; it does not inspect every subfigure within a path. The example prints True for a rectangle.
#include <DOM/Presentation.h>
#include <DOM/ISlide.h>
#include <DOM/IShapeCollection.h>
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/ShapeType.h>
#include <system/array.h>
#include <system/smart_ptr.h>
#include <system/console.h>
#include <DOM/IPathSegment.h>
#include <DOM/PathCommandType.h>
using namespace Aspose::Slides;
using namespace System;
auto isGeometryClosed = [](SharedPtr<IGeometryShape> geometryShape)
{
auto hasNonemptyPath = false;
for (auto&& geometryPath : geometryShape->GetGeometryPaths())
{
auto segments = geometryPath->get_PathData();
if (segments->get_Length() == 0)
{
continue;
}
hasNonemptyPath = true;
auto lastSegment = segments->idx_get(segments->get_Length() - 1);
if (lastSegment->get_PathCommand() != PathCommandType::Close)
{
return false;
}
}
return hasNonemptyPath;
};
auto presentation = MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);
auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100, 100, 200, 100);
Console::WriteLine(isGeometryClosed(shape));
Extract and Inspect Shape Elements
Use IGeometryShape::CreateShapeElements to inspect an existing shape as elements containing path points and point types. Cast each returned IShapeElement to ShapeElement to read the arrays returned by get_PathPoints and get_PathTypes. This representation is useful for contour analysis or passing geometry to custom drawing tools.
The example opens a presentation, finds the first geometry shape directly on the first slide, and prints its path segments. Use a presentation containing a shape with straight and curved segments to see both kinds of output. It does not search inside groups.
#include <DOM/Presentation.h>
#include <DOM/ISlide.h>
#include <DOM/IShapeCollection.h>
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/ShapeType.h>
#include <system/array.h>
#include <system/smart_ptr.h>
#include <system/console.h>
#include <DOM/IShapeElement.h>
#include <DOM/ShapeElement.h>
using namespace Aspose::Slides;
using namespace System;
auto presentation = MakeObject<Presentation>(u"input.pptx");
auto slide = presentation->get_Slide(0);
SharedPtr<IGeometryShape> shape;
for (auto&& candidate : slide->get_Shapes())
{
shape = AsCast<IGeometryShape>(candidate);
if (shape != nullptr)
{
break;
}
}
if (shape == nullptr)
{
Console::WriteLine(u"No geometry shape found on the first slide.");
return;
}
for (auto&& element : shape->CreateShapeElements())
{
auto shapeElement = ExplicitCast<ShapeElement>(element);
auto points = shapeElement->get_PathPoints();
auto types = shapeElement->get_PathTypes();
Console::WriteLine(u"Shape element:");
for (auto i = 0; i < points->get_Length(); i++)
{
auto pointType = types[i] & 0x07; // Keep the segment type, excluding flags.
switch (pointType)
{
case 0:
Console::WriteLine(u"Start figure at {0}", points[i]);
break;
case 1:
Console::WriteLine(u"Line to {0}", points[i]);
break;
case 3:
Console::WriteLine(u"Cubic Bezier: controls {0}, {1}; end {2}", points[i], points[i + 1], points[i + 2]);
i += 2; // Consume both control points and the endpoint.
break;
}
if ((types[i] & 0x80) != 0)
{
Console::WriteLine(u"Close subpath back to its starting point.");
}
}
}
The arrays correspond by index: points[i] is a point and types[i] describes its role. Type 0 starts a figure, and type 1 draws a line from the previous point to the current point. A cubic Bézier segment starts at the previous endpoint and uses three consecutive type-3 entries: two control points followed by the new endpoint. The loop consumes these three entries together and checks the endpoint for closure.
Point types also contain flags. Mask with 0x07 to identify the segment type, and test 0x80 separately to detect a closed subpath. For example, 129 (0x81) means a line endpoint that closes a subpath, and 131 (0x83) means a Bézier endpoint that closes one. Comparing the raw value only with 1 or 3 would miss these cases. Other flags, such as dash (0x10) and marker (0x20), do not change the base segment type.
Coordinates and transformations. Points are measured in points (1/72 inch), with X increasing to the right and Y increasing downward. For an ungrouped shape, the extracted coordinates already include the position returned by get_X and get_Y and reflect its current width and height. For example, an unrotated rectangle at (100, 200) with size 120 × 80 has contour bounds (100, 200)–(220, 280). Do not add the shape’s position again. Subtract its X and Y to obtain coordinates relative to its unrotated frame.
The extracted points do not apply the rotation set through set_Rotation or horizontal and vertical flips. To reproduce the displayed contour, apply those transformations around the shape’s center. For grouped shapes, also account for the group coordinate mapping and transformations; do not assume that the extracted points are the final rendered slide coordinates or apply a translation or scale twice if it is already reflected in the child’s frame.
Inspection versus editing. Extracted arrays describe the contour; changing them does not update the original shape. Choose this approach when your analysis or drawing code needs point/type arrays. Use GetGeometryPaths when you need geometry commands in the shape’s local coordinate system or want to edit its geometry. Apply edited paths through SetGeometryPath or SetGeometryPaths.
Convert GeometryPath to GraphicsPath
The current Aspose.Slides for C++ public API does not expose conversions between geometry paths and graphics paths. To inspect a contour, use shape elements as described above. To edit a shape, use geometry paths and apply the updated paths to the shape.
FAQ
What will happen to the fill and outline after replacing the geometry?
The style remains with the shape; only the contour changes. The fill and outline are automatically applied to the new geometry.
How do I correctly rotate a custom shape along with its geometry?
Use the shape’s set_Rotation method; the geometry rotates with the shape because it’s bound to the shape’s own coordinate system.
Can I convert a custom shape to an image to “lock in” the result?
Yes. Export the required slide area or the shape itself to a raster format; this simplifies further work with complex geometries.