Настройка форм презентаций в Java
Обзор
Эта статья объясняет, как настраивать формы презентаций в Aspose.Slides, редактируя геометрию формы с помощью точек редактирования и путей геометрии. Показано, как работать с GeometryPath и IGeometryPath для изменения существующих форм, выполнения базовых операций редактирования путей, добавления или удаления точек и применения обновлённой геометрии обратно к форме.
Также демонстрируется, как создавать пользовательские и составные формы, построить формы со сглаженными углами, определить, замкнута ли геометрия формы, просмотреть элементы формы и преобразовать контуры текста Java AWT в пути геометрии.
Изменение формы с помощью точек редактирования
В PowerPoint можно использовать Edit Points для изменения квадрата:
- Переместить угол внутрь или наружу.
- Отрегулировать кривизну в точке.
- Добавить новые точки.
Редактирование точек позволяет изменять контур формы или использовать существующую форму как основу для новой.
Советы по редактированию форм

При редактировании форм PowerPoint учитывайте следующие понятия:
- Форма (или её путь) может быть замкнутой или открытой.
- Путь формы состоит из контрольных точек, соединённых сегментами.
- Сегмент может быть прямым или кривым. Точки и управляющие рычаги определяют его форму.
- Точки бывают угловыми, прямыми или сглаженными:
- В угловой точке соединяющие сегменты могут встречаться под углом.
- В сглаженной точке два управляющих рычага выровнены и находятся на одинаковом расстоянии от точки.
- В прямой точке два управляющих рычага выровнены, но могут находиться на разном расстоянии от точки.
- Перемещение точек или изменение их рычагов меняет контур формы.
Для редактирования форм PowerPoint через точки редактирования Aspose.Slides предоставляет класс GeometryPath и интерфейс IGeometryPath.
- Экземпляр GeometryPath представляет путь геометрии объекта IGeometryShape.
- Чтобы получить GeometryPath из экземпляра IGeometryShape, используйте метод IGeometryShape.getGeometryPaths.
- Чтобы задать GeometryPath для формы, используйте методы: IGeometryShape.setGeometryPath для форм с одним путем и IGeometryShape.setGeometryPaths для форм с несколькими путями.
- Чтобы добавить сегменты, используйте методы интерфейса IGeometryPath.
- С помощью методов IGeometryPath.setStroke и IGeometryPath.setFillMode можно задать внешний вид пути геометрии.
- Методом IGeometryPath.getPathData можно получить путь геометрии GeometryShape в виде массива сегментов пути.
- Чтобы получить дополнительные возможности настройки геометрии формы, можно прочитать объект java.awt.Shape через его PathIterator и воспроизвести сегменты на GeometryPath.
Простые операции редактирования
Используйте перечисленные ниже методы для редактирования пути геометрии. Перегрузки, принимающие индекс, вставляют сегмент в указанную позицию (нумерация с нуля) в массиве сегментов пути.
| Операция | Метод |
|---|---|
| Добавить или вставить прямую линию. | GeometryPath.lineTo |
| Добавить или вставить кубическую кривую Безье с двумя контрольными точками и конечной точкой. | GeometryPath.cubicBezierTo |
| Добавить или вставить квадратичную кривую Безье с одной контрольной точкой и конечной точкой. | GeometryPath.quadraticBezierTo |
| Добавить эллиптическую дугу с указанными размерами и углами в градусах. | GeometryPath.arcTo |
| Замкнуть текущую фигуру, соединив её конечную точку с начальной. | GeometryPath.closeFigure |
| Задать начальную точку фигуры. | GeometryPath.moveTo |
| Удалить сегмент по индексу (ноль‑based). | GeometryPath.removeAt |
Добавление пользовательских точек в форму
В этом примере в прямоугольную форму вставляются две точки с помощью GeometryPath.lineTo и применяется отредактированный путь через IGeometryShape.setGeometryPath. Координаты задаются локально относительно формы: обе новые точки находятся в её центре, на 100 пунктов от левого края и 50 пунктов от верхнего. Индексы сегментов 1 и 4 помещают их на верхнюю и нижнюю границы, притягивая эти грани внутрь.
import com.aspose.slides.*;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
IGeometryPath geometryPath = shape.getGeometryPaths()[0];
geometryPath.lineTo(100, 50, 1);
geometryPath.lineTo(100, 50, 4);
shape.setGeometryPath(geometryPath);
} finally {
presentation.dispose();
}

Удаление точек из формы
В этом примере контур сердца изменяется удалением сегмента с ноль‑based индексом 2 с помощью GeometryPath.removeAt. Применив изменённый путь через IGeometryShape.setGeometryPath, контур формы обновляется.
import com.aspose.slides.*;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Heart, 100, 100, 300, 300);
IGeometryPath path = shape.getGeometryPaths()[0];
path.removeAt(2);
shape.setGeometryPath(path);
} finally {
presentation.dispose();
}

Создание пользовательской формы
В данном примере создаётся пятиконечная звезда с помощью GeometryPath. Чередующиеся внешние и внутренние радиусы 100 и 50 пунктов задают кончики и впадины; шаг в 72 градуса равномерно распределяет пять кончиков. Путь замыкается методом GeometryPath.closeFigure и применяется к прямоугольной форме через IGeometryShape.setGeometryPath, заменяя её контур.
import com.aspose.slides.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
List<Point2D.Float> points = new ArrayList<>();
float outerRadius = 100f;
float innerRadius = 50f;
int angleStep = 72;
for (int angle = -90; angle < 270; angle += angleStep)
{
double radians = angle * (Math.PI / 180f);
double x = outerRadius * Math.cos(radians);
double y = outerRadius * Math.sin(radians);
Point2D.Float point = new Point2D.Float((float)x + outerRadius, (float)y + outerRadius);
points.add(point);
radians = Math.PI * (angle + angleStep / 2) / 180.0;
x = innerRadius * Math.cos(radians);
y = innerRadius * Math.sin(radians);
point = new Point2D.Float((float)x + outerRadius, (float)y + outerRadius);
points.add(point);
}
GeometryPath starPath = new GeometryPath();
starPath.moveTo(points.get(0));
for (int i = 1; i < points.size(); i++)
{
starPath.lineTo(points.get(i));
}
starPath.closeFigure();
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, outerRadius * 2, outerRadius * 2);
shape.setGeometryPath(starPath);
} finally {
presentation.dispose();
}

Создание составной пользовательской формы
В этом примере определяются два замкнутых объекта GeometryPath для верхней и нижней трети формы. Применив оба пути через IGeometryShape.setGeometryPaths создаётся единая составная форма с разрывом в её средней трети.
import com.aspose.slides.*;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
GeometryPath topPath = new GeometryPath();
topPath.moveTo(0, 0);
topPath.lineTo(shape.getWidth(), 0);
topPath.lineTo(shape.getWidth(), shape.getHeight() / 3);
topPath.lineTo(0, shape.getHeight() / 3);
topPath.closeFigure();
GeometryPath bottomPath = new GeometryPath();
bottomPath.moveTo(0, shape.getHeight() / 3 * 2);
bottomPath.lineTo(shape.getWidth(), shape.getHeight() / 3 * 2);
bottomPath.lineTo(shape.getWidth(), shape.getHeight());
bottomPath.lineTo(0, shape.getHeight());
bottomPath.closeFigure();
shape.setGeometryPaths(new GeometryPath[] { topPath, bottomPath });
} finally {
presentation.dispose();
}

Создание пользовательской формы со сглажёнными углами
В этом примере прямые сегменты комбинируются с GeometryPath.arcTo для создания формы с внутреннями изогнутыми углами. Размер каждого угла задаёт ширину и высоту дуги в пунктах, а sweep ‑90° рисует контр‑часовую четверть эллипса. Готовый путь применяется через IGeometryShape.setGeometryPath, а презентация сохраняется в формате PPTX.
import com.aspose.slides.*;
import java.awt.geom.Point2D;
float shapeX = 20f;
float shapeY = 20f;
float shapeWidth = 300f;
float shapeHeight = 200f;
float leftTopSize = 50f;
float rightTopSize = 20f;
float rightBottomSize = 40f;
float leftBottomSize = 10f;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Custom, shapeX, shapeY, shapeWidth, shapeHeight);
Point2D.Float topLeft = new Point2D.Float(leftTopSize, 0);
Point2D.Float topRight = new Point2D.Float(shapeWidth - rightTopSize, 0);
Point2D.Float rightBottom = new Point2D.Float(shapeWidth, shapeHeight - rightBottomSize);
Point2D.Float bottomLeft = new Point2D.Float(leftBottomSize, shapeHeight);
Point2D.Float leftTop = new Point2D.Float(0, leftTopSize);
GeometryPath geometryPath = new GeometryPath();
geometryPath.moveTo(topLeft);
geometryPath.lineTo(topRight);
geometryPath.arcTo(rightTopSize, rightTopSize, 180, -90);
geometryPath.lineTo(rightBottom);
geometryPath.arcTo(rightBottomSize, rightBottomSize, -90, -90);
geometryPath.lineTo(bottomLeft);
geometryPath.arcTo(leftBottomSize, leftBottomSize, 0, -90);
geometryPath.lineTo(leftTop);
geometryPath.arcTo(leftTopSize, leftTopSize, 90, -90);
geometryPath.closeFigure();
shape.setGeometryPath(geometryPath);
presentation.save("output.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Определение, замкнута ли геометрия формы
Следующая вспомогательная функция проверяет, заканчивается ли каждый непустой путь геометрии закрывающим сегментом. Она читает IGeometryPath.getPathData и сравнивает команду последнего сегмента — IPathSegment.getPathCommand — с PathCommandType.Close. Возвращает false, когда нет непустых путей. Функция проверяет окончательную команду каждого пути; она не просматривает каждую подфигуру внутри пути. Пример выводит true для прямоугольника.
import com.aspose.slides.*;
class GeometryInspector {
boolean isGeometryClosed(IGeometryShape geometryShape) {
boolean hasNonemptyPath = false;
for (IGeometryPath geometryPath : geometryShape.getGeometryPaths()) {
IPathSegment[] segments = geometryPath.getPathData();
if (segments.length == 0) {
continue;
}
hasNonemptyPath = true;
IPathSegment lastSegment = segments[segments.length - 1];
if (lastSegment.getPathCommand() != PathCommandType.Close) {
return false;
}
}
return hasNonemptyPath;
}
}
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);
GeometryInspector inspector = new GeometryInspector();
boolean closed = inspector.isGeometryClosed(shape);
System.out.println(closed);
} finally {
presentation.dispose();
}
Извлечение и анализ элементов формы
Используйте IGeometryShape.createShapeElements для получения существующей формы в виде элементов, содержащих точки пути и типы точек. Приведите каждый полученный IShapeElement к типу ShapeElement чтобы получить массивы точек и типов через методы getPathPoints и getPathTypes. Такое представление удобно для анализа контура или передачи геометрии в пользовательские инструменты рисования.
Пример открывает input.pptx, находит первую геометрическую форму на первой странице и выводит её сегменты пути. Используйте презентацию, содержащую форму с прямыми и кривыми сегментами, чтобы увидеть оба типа вывода. Поиск внутри групп не производится.
import com.aspose.slides.*;
import java.awt.geom.Point2D;
Presentation presentation = new Presentation("input.pptx");
try {
ISlide slide = presentation.getSlides().get_Item(0);
IGeometryShape shape = null;
for (IShape candidate : slide.getShapes()) {
if (candidate instanceof IGeometryShape) {
shape = (IGeometryShape) candidate;
break;
}
}
if (shape == null) {
System.out.println("No geometry shape found on the first slide.");
} else {
for (IShapeElement element : shape.createShapeElements()) {
ShapeElement shapeElement = (ShapeElement) element;
Point2D.Float[] points = shapeElement.getPathPoints();
byte[] types = shapeElement.getPathTypes();
System.out.println("Shape element:");
for (int i = 0; i < points.length; i++) {
int pointType = types[i] & 0x07; // Сохраняет тип сегмента, исключая флаги.
switch (pointType) {
case 0:
System.out.println("Start figure at " + points[i]);
break;
case 1:
System.out.println("Line to " + points[i]);
break;
case 3:
System.out.println("Cubic Bezier: controls " + points[i] + ", " + points[i + 1] + "; end " + points[i + 2]);
i += 2; // Потребляет обе контрольные точки и конечную точку.
break;
}
if ((types[i] & 0x80) != 0) {
System.out.println("Close subpath back to its starting point.");
}
}
}
}
} finally {
presentation.dispose();
}
Массивы сопоставляются по индексу: points[i] — это точка, types[i] — её роль. Тип 0 начинается фигура, тип 1 рисует линию от предыдущей точки к текущей. Кубический сегмент Безье начинается в предыдущей конечной точке и использует три последовательных типа 3: две контрольные точки и новую конечную точку. Цикл потребляет эти три записи совместно и проверяет конечную точку на замыкание.
Типы точек также содержат флаги. Маска 0x07 позволяет определить тип сегмента, а отдельная проверка 0x80 выявляет закрытый подпуть. Например, 129 (0x81) — конечная точка линии, закрывающая подпуть, а 131 (0x83) — конечная точка Безье, закрывающая подпуть. Java хранит эти флаги в знаковых байтах; маскирование сохраняет нужные битовые значения. Сравнение «сырого» значения только с 1 или 3 упустит такие случаи. Другие флаги, такие как тире (0x10) и маркер (0x20), не меняют базовый тип сегмента.
Координаты и преобразования. Точки измеряются в пунктах (1/72 дюйма), X — вправо, Y — вниз. Для отдельной формы извлечённые координаты уже включают позицию, возвращаемую методами getX и getY, и отражают её текущую ширину и высоту. Например, неповёрнутый прямоугольник с координатами (100, 200) и размером 120 × 80 имеет границы контура (100, 200)–(220, 280). Не добавляйте позицию формы повторно. Вычтите её X и Y, чтобы получить координаты относительно её неповороченного кадра.
Извлечённые точки не учитывают вращение формы, возвращаемое методом getRotation, а также горизонтальное и вертикальное отражения. Чтобы воспроизвести отображаемый контур, примените эти преобразования относительно центра формы. Для сгруппированных форм также учитывайте преобразования координат группы; не считӓйте, что извлечённые точки уже являются конечными координатами слайда, и не применяйте трансляцию или масштаб дважды, если они уже отражены в кадре дочернего элемента.
Анализ vs. редактирование. Извлечённые массивы описывают контур; изменение их не обновит исходную форму. Используйте такой подход, когда нужен массив точек/типов для анализа или собственного кода отрисовки. Применяйте getGeometryPaths когда нужны команды геометрии в локальной системе координат формы или требуется редактировать её геометрию. Применяйте отредактированные пути через setGeometryPath или setGeometryPaths.
Преобразование Java AWT Shape в GeometryPath
В этом примере комбинируется прямоугольный контур с текстом, преобразованным в геометрию. Метод setFillMode отключает заливку контура. Объект GlyphVector создаёт контур текста в виде java.awt.Shape. Его PathIterator поставляет сегменты перемещения, линии, квадратичной, кубической и закрывающие сегменты, которые пример воспроизводит на GeometryPath.
Текст использует шрифт Arial размером 40 пунктов. Контур сдвинут на 20 пунктов по горизонтали и расположен так, чтобы его визуальная верхняя граница была на 10 пунктов ниже верхнего края формы. Путь текста заполнен, тогда как исходный прямоугольник остаётся контуру. Оба пути применяются через IGeometryShape.setGeometryPaths, образуя одну форму, содержащую прямоугольник и текст.
import com.aspose.slides.*;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
Presentation presentation = new Presentation();
try {
// Создать новую форму
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 300, 100);
// Получить путь геометрии формы
IGeometryPath originalPath = shape.getGeometryPaths()[0];
originalPath.setFillMode(PathFillModeType.None);
// Создать новый графический путь с текстом
Shape graphicsPath;
Font font = new Font("Arial", Font.PLAIN, 40);
String text = "Text in shape";
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
try
{
FontRenderContext fontContext = graphics.getFontRenderContext();
GlyphVector glyphVector = font.createGlyphVector(fontContext, text);
Rectangle2D textBounds = glyphVector.getVisualBounds();
float baseline = (float) -textBounds.getY() + 10;
graphicsPath = glyphVector.getOutline(20f, baseline);
}
finally {
graphics.dispose();
}
// Преобразовать графический путь в путь геометрии
IGeometryPath textPath = new GeometryPath();
PathIterator pathIterator = graphicsPath.getPathIterator(null);
float[] points = new float[6];
while (!pathIterator.isDone())
{
int segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO:
textPath.moveTo(points[0], points[1]);
break;
case PathIterator.SEG_LINETO:
textPath.lineTo(points[0], points[1]);
break;
case PathIterator.SEG_QUADTO:
textPath.quadraticBezierTo(points[0], points[1], points[2], points[3]);
break;
case PathIterator.SEG_CUBICTO:
textPath.cubicBezierTo(points[0], points[1], points[2], points[3], points[4], points[5]);
break;
case PathIterator.SEG_CLOSE:
textPath.closeFigure();
break;
}
pathIterator.next();
}
textPath.setFillMode(PathFillModeType.Normal);
// Установить комбинацию нового пути геометрии и исходного пути геометрии для формы
shape.setGeometryPaths(new IGeometryPath[] { originalPath, textPath });
} finally {
presentation.dispose();
}

FAQ
Что произойдёт с заливкой и контурами после замены геометрии?
Стиль остаётся привязанным к форме; меняется только контур. Заливка и контур автоматически применяются к новой геометрии.
Как правильно повернуть пользовательскую форму вместе с её геометрией?
Вызовите метод формы setRotation; геометрия вращается вместе с формой, поскольку привязана к её собственной системе координат.
Можно ли преобразовать пользовательскую форму в изображение, чтобы «запечатлеть» результат?
Да. Экспортируйте нужный slide или саму shape в растровый формат; это упрощает дальнейшую работу со сложными геометриями.